Scott Hanselman

Hanselminutes Podcast 115 - Rediscovering Your Passion for Software

June 18, 2008 Comment on this post [7] Posted in Podcast | Programming
Sponsored By

carlfranklin (I forgot to post this one to my blog, so you may have already noticed it on the Hanselminutes Feed. My apologies.)

My one-hundred-and-fifteenth podcast is up. Carl Franklin returns in this episode, and he and I talk about the joys of programming and getting back to basics. Is it hard to stay passionate about this job? Is there a need for the community to revisit Computer Science 101? What do you do to stay excited about software?

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Telerik's new stuff is pretty sweet, check out the ONLINE DEMO of their new ASP.NET AJAX suite. RadGrid handles sorting, filtering, and paging of hundreds of thousands of records in milliseconds, and the RadEditor loads up to 4 times faster and the navigation controls now support binding to web services on the client.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Back to Basics - Life After If, For and Switch - Like, a Data Structures Reminder

June 14, 2008 Comment on this post [42] Posted in Back to Basics | Learning .NET | Programming | Source Code
Sponsored By

I just had a great one on one coding learning session with a good friend of mine over lunch. He's trying to take his coding skills to the "next level." Just as we plateau when we work out physically, I think we can plateau when coding and solving problems. That one of the reasons I try to read a lot of code to be a better developer and why I started The Weekly Source Code. He said it was OK to write about this as someone else might benefit from our discussion. The code and problem domain have been changed to protect the not-so-innocent.

One of the things that we talked about was that some programmers/coders/developers have just a few tools in their toolbox, namely, if, for, and switch.

I'm not making any judgements about junior devs vs. senior devs. I'm talking about idioms and "vocab." I think that using only if, for and switch is the Computer Programmer equivalent of using "like" in every sentence. Like, you know, like, he was all, like, whatever, and I was like, dude, and he was, like, ewh, and I was like meh, you know?

When speaking English, by no means do I have a William F Buckley, Jr.-sized vocabulary, nor do I believe in being sesquipedal for sesquipedalianism's sake, but there is a certain sparkly joyfulness in selecting the right word for the right situation. I'm consistently impressed when someone can take a wordy paragraph and condense it into a crisp sentence without losing any information.

Refactoring code often brings me the same shiny feeling. Here's a few basic things that my friend and I changed in his application over lunch, turning wordy paragraphs into crisp sentences. Certainly this isn't an exhaustive list of anything, but perhaps it can act as a reminder to myself and others to be mindful and think about solving problems beyond, like, if and for and, like, switch, y'know?

Massives Ifs are Sometimes Maps

He had some code that parsed a ridiculous XML document that came back from a Web Server. That the format of the XML was insane wasn't his fault, to be sure. We all have to parse crap sometimes. He had to check for the existence of a certain value and turn it into an Enum.

if (xmlNode.Attributes["someAttr"].Value.ToLower().IndexOf("fog") >= 0)
{
wt = MyEnum.Fog;
}
if (xmlNode.Attributes["someAttr"].Value.ToLower().IndexOf("haze") >= 0)
{
wt = MyEnum.Haze;
}
if (xmlNode.Attributes["someAttr"].Value.ToLower().IndexOf("dust") >= 0)
{
wt = MyEnum.Haze;
}
if (xmlNode.Attributes["someAttr"].Value.ToLower().IndexOf("rain") >= 0)
{
wt = MyEnum.Rainy;
}

...and this went on for 40+ values. There's a few problems with this.

First, he's using IndexOf() and ToLower() to when he's trying to say "ignoring case, does this string contain this other string?" Using ToLower() for a string comparison is always a bad idea, and not just because of the Turkish i problem (details here, here, here and here). Be sure to check out the Recommendations for Strings in .NET.

We could make this simpler and more generic with a helper method that we'll use later. It expresses what we want to do pretty well. If we were using .NET 3.5 we could make this an extension method, but he's on 2.0.

private static bool ContainsIgnoreCase(string s, string searchingFor)
{
return s.IndexOf(searchingFor, StringComparison.OrdinalIgnoreCase) >= 0;
}

Second, he's indexing into the Attributes collection over and over again, and he's hasn't "else cased" the other ifs, so every indexing into Attributes and every other operation runs every time. He can do the indexing once, pull it out, then check his values.

string ws = xmlNode.Attributes["someAttr"].Value;
if (ContainsIgnoreCase(ws, "cloud"))
wt = MyEnum.Cloudy;
else if (ContainsIgnoreCase(ws, "fog"))
wt = MyEnum.Fog;
else if (ContainsIgnoreCase(ws, "haze"))
wt = MyEnum.Haze;
else if (ContainsIgnoreCase(ws, "dust"))
wt = MyEnum.Dust;
else if (ContainsIgnoreCase(ws, "rain"))
wt = MyEnum.Rainy;
else if (ContainsIgnoreCase(ws, "shower"))

...and again, as a reminder, this goes on for dozens and dozens of lines.

We were talking this through step by step to explain my "from point a to point d" wait of thinking. I tend to skip b and c, so it's useful to show these incremental steps, kind of like showing all your work in Math class when doing long division.

At this point, I pointed out that he was clearly mapping the strings to the enums. Now, if the mapping was direct (and it's not for a variety of horrible many-to-one reasons that are spec-specific as well as that this a "contains" operations rather than a direct equality comparison) he could have parsed the string an enum like this where the last parameter ignores case:

wt = (MyEnum)Enum.Parse(typeof(MyEnum), ws, true);

However, his mapping has numerous exceptions and the XML is messy. Getting one step simpler, I suggested making a map. There's a lot of folks who use Hashtable all the time, as they have for years in .NET 1.1, but haven't realized how lovely Dictionary<TKey, TValue> is.

var stringToMyEnum = new Dictionary<string, MyEnum>()
{
{ "fog", MyEnum.Fog},
{ "haze", MyEnum.Fog},
{ "fred", MyEnum.Funky},
//and on and on
};

foreach (string key in stringToMyEnum.Keys)
{
if (ContainsIgnoreCase(ws, key))
{
wt = stringToMyEnum[key];
break;
}
}

Because his input data is gross, he spins through the Keys collection and calls ContainsIgnoreCase on each key until settling on the right Enum. I suggested he clean up his input data to avoid the for loop, turning the whole operation into a simple mapping. At this point, of course, the Dictionary can be shoved off into some global readonly static resource.

string ws = xmlNode.Attributes["someAttr"].Value;
//preproccessing over ws to tidy up
var stringToMyEnum = new Dictionary<string, MyEnum>()
{
{ "fog", MyEnum.Fog},
{ "haze", MyEnum.Fog},
{ "fred", MyEnum.Funky},
//and on and on
};
wt = stringToMyEnum[key];

When Switches Attack

Often large switches "just happen." What I mean is that one introduces a switch to deal with some uncomfortable and (apparently) unnatural mapping between two things and then it just gets out of hand. They tell themselves they'll come back and fix it soon, but by then it's grown into a hairball.

My buddy had a method that was supposed to remove an icon from his WinForms app. The intent is simple, but the implementation became another mapping between a fairly reasonable Enum that he couldn't control, and a number of named icons that he could.control.

The key here is that he could control the icons and he couldn't easily control the enum (someone else's code, etc). That the mapping was unnatural was an artifact of his design.

The next thing he knew he was embroiled in a switch statement without giving it much thought.

private void RemoveCurrentIcon()
{
switch (CurrentMyEnumIcon)
{
case MyEnum.Cloudy:
CustomIcon1 twc = (CustomIcon1)FindControl("iconCloudy");
if (twc != null)
{
twc.Visible = false;
this.Controls.Remove(twc);
}
break;
case MyEnum.Dust:
CustomIcon2 twd = (CustomIcon2)FindControl("iconHaze");
if (twd != null)
{
twd.Visible = false;
this.Controls.Remove(twd);
}
break;
//etc...for 30+ other case:

There's a few things wrong here, besides the switch is yucky. (Yes, I know there are uses for switches, just not here.) First, it's useful to remember when dealing with a lot of custom classes, in this case CustomIcon1 and CustomIcon2 that they likely have a common ancestor. In this case a common ancestor is Control.

Next, he can control the name of his controls. For all intents in his WinForms app, the Control's name is arbitrary. Because his controls map directly to his Enum, why not literally map them directly?

private void RemoveCurrentIcon(MyEnumIcon CurrentMyEnumIcon)
{
Control c = FindControl("icon" + CurrentMyEnumIcon);
if(c != null)
{
c.Visible = false;
Controls.Remove(c);
}
}

The recognition of the shared base class along with the natural Enum to Control name mapping turns 160 lines of switch into a single helper method.

It's very easy, seductive even, to try to explain to the computer to "do it like this" using if, for and switch. However, using even the basic declarative data structures to describe the shape of the data can help you avoid unnecessary uses of the classic procedural keywords if, for and switch.

FINAL NOTE: We worked for a while and turned and 8100 line long program into 3950 lines. I think we can squeeze another 1500-2000 lines out of it. We did the refactoring with the new Resharper 4.0 and a private daily build of CodeRush/Refactor!Pro. I'll do some writeups on both wonderful tools in the coming weeks.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Learning WPF with BabySmash - Factories, Interfaces, Delegates and Lambdas, oh my!

June 13, 2008 Comment on this post [19] Posted in BabySmash | Windows Client | WPF
Sponsored By

NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Issue Tracker and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

BabySmash has a little mini-hierarchy of "Figures" that represent the Shapes on the screen. There is a "FigureGenerator" that is meant to return a random Figure whenever the baby hits a key.

I thought it would be interesting to walk through the several different ways one could solve this problem, depending on what version of the .NET Framework one has available. This isn't an exhaustive list, of course, and I'm VERY interested in your refactorings, Dear Reader, so leave them in the comments inside <pre> tags. Thanks to Scott Cate (and his son) for his help with this post today.

Original Yucky Random Factory

What's the catch? Well, here's one. The "FigureGenerator" looks like this currently. I know it sucks because of all the TODOs. If that isn't enough of a hint for you, the switch statement is your 9th hint. This is actually pretty common WTF code that you've likely seen in the wild. Perhaps you'd even admit to writing something like it yourself.

private static Figure GenerateFigure(string letter)
{
//TODO: Should this be in XAML? Would that make it better?
Brush fill = Utils.GetRandomColoredBrush();
if (letter.Length == 1 && Char.IsLetterOrDigit(letter[0]))
{
return new LetterFigure(fill, letter);
}

int shape = Utils.RandomBetweenTwoNumbers(0, 6);
//TODO: Should I change the height, width and stroke to be relative to the screen size?
//TODO: I think I need a shapefactory?
//TODO: Where can I get REALLY complex shapes like animal vectors or custom pics? Where do I store them?
switch (shape)
{
case 0:
return new SquareFigure(fill);
case 1:
return new CircleFigure(fill);
case 2:
return new TriangleFigure(fill);
case 3:
return new StarFigure(fill);
case 4:
return new HeartFigure(fill);
case 5:
return new TrapezoidFigure(fill);
case 6:
return new RectangleFigure(fill);
}
return null;
}

Why so gross? Well, the goal here is to be an object factory, but to be a random object factory that returns any of a list of Figures. Notice, though, that a Letter is a special kind of Figure depending on what key the baby pressed. It's still a figure, except it's the only one that cares what key was pressed.

What I need is a better Factory to make these Figures, but a "random factory" that is a little easier to extend than a Switch statement.

Random List of Factories with Interfaces - The 1.1 Way

I'm trying to create a RandomFactory that makes concrete instances of random Shapes that drive from an abstract class. Its the randomness that's important, so I need to randomly select from a list of potentials.

On the reflection side of things, I could have created an array of System.Types then randomly picked one, then used Reflection via Activator.CreateInstance to create something concrete. However, Reflection should be a last resort, and this isn't that complex of a pattern.

One oldish, simple solution would be to create an array of FigureFactories like this:

interface IFigureFactory
{
Figure CreateFigure(Brush fill);
}

public class StarFigureFactory : IFigureFactory
{
public Figure CreateFigure(Brush fill)
{
return new StarFigure(fill);
}
}
public class SquareFigureFactory : IFigureFactory
{
public Figure CreateFigure(Brush fill)
{
return new SquareFigure(fill);
}
}

I'd need to make an array of these, then pick one randomly:

IFigureFactory[] figfacts = new IFigureFactory[]
{
new StarFigureFactory(),
new SquareFigureFactory()
//etc...
};
IFigureFactory figfac = Utils.RandomBetweenTwoNumbers(0, figfacts.Length);
Figure f = figfac.CreateFigure(fill);

This works pretty well, but requires all those FigureFactories.

Random List of Factories with Generics - The 2.0 Second Way Attempt

But, if I was using .NET 2.0, I might say, "hey, what about generics?" I could start creating a GenericFigureFactory<T> to try and get rid of all those other custom tiny factories.

public class GenericFigureFactory<T> where T : Figure, new() 
{
public Figure CreateFigure(Brush fill)
{
Figure f = new T(fill);
//Cannot provide arguments when creating instance of T
}
}

However, it's not so clean because even though the system KNOWS that T is of type Figure and that Figure has a constructor that takes a Brush, I can't call that constructor. At this point, I'd need to set properties instead of a constructor with a parameter. Poop. FAIL.

Random Factories with Delegates - The 2.0 Way

Perhaps this is too much work, with the generics and the interfaces. All I really need is a bunch of methods where the signature is "take a Brush, return a Figure." Some kind of delegate that had that signature. ;)

public delegate Figure FigureFactory<Brush, Figure>(Brush x);

So that's 2.0 style delegate that takes a Brush and returns a Factory. Remember that a delegate in this example an exemplar. It's an example of what a method should look like. It's not the method itself, just the meta-method.

I can make a list of these factories, defining the body as I add them. Then, grab a random one and call it.

List<FigureFactory<Brush, Figure>> foo = new List<FigureFactory<Brush, Figure>>();
foo.Add(delegate(Brush x) { return new SquareFigure(x); });
foo.Add(delegate(Brush x) { return new StarFigure(x); });
FigureFactory myFunc = foo[Utils.RandomBetweenTwoNumbers(0, foo.Count-1)];
Figure f = myFunc(fill);

That delegate up there that takes something and returns something could be made generic, like this:

public delegate TResult Func<TParam, TResult>(TParam p);

That's SO useful that it was added in .NET 3.5 as System.Func and it lives in System.Core.dll. If you want this kind of functionality in .NET 2.0, then just define your own bunch of Funcs. I got this tip from Scott Cate. Remember, if you're using .NET 3.5, you don't need these, they already exist.

public delegate void Func<T>();
public delegate TRes Func<TSrc1, TRes>(TSrc1 src1);
public delegate TRes Func<TSrc1, TSrc2, TRes>(TSrc1 src1, TSrc2 src2);
public delegate TRes Func<TSrc1, TSrc2, TSrc3, TRes>(TSrc1 src1, TSrc2 src2, TSrc3 src3);
public delegate TRes Func<TSrc1, TSrc2, TSrc3, TSrc4, TRes>(TSrc1 src1, TSrc2 src2, TSrc3 src3, TSrc4 s

Still, this could be cleaner.

Random Factories with Lambdas - The 3.5 Way

As Eric Lippert says:

"At first glance, lambda methods look like nothing more than a syntactic sugar, a more compact and pleasant syntax for embedding an anonymous method in code."

There are some subtle differences, and if you want to learn more I encourage you to check out Part 1, Part 2, Part 3, Part 4 and Part 5 of his detailed series on just those differences. For what we're doing, all that matters is that lambdas are nicer to look at.

Now, replacing the delegates with lambdas changes the delegate way:

List<FigureFactory<Brush, Figure>> foo = new List<FigureFactory<Brush, Figure>>();
foo.Add(delegate(Brush x) { return new SquareFigure(x); }); //etc...

to

var foo = new List
{
x => new SquareFigure(x), //etc...
}

So the whole random generator cleans up to just this chunk of code, with the list of figures moved out to a private field:

private static readonly List<Func<Brush, Figure>> listOfPotentialFigures = 
new List<Func<Brush, Figure>>
{
x => new SquareFigure(x),
x => new CircleFigure(x),
x => new TriangleFigure(x),
x => new StarFigure(x),
x => new HeartFigure(x),
x => new TrapezoidFigure(x),
x => new RectangleFigure(x)
};


private static Figure GenerateFigure(string letter)
{
var fill = Utils.GetRandomColoredBrush();
if (letter.Length == 1 && Char.IsLetterOrDigit(letter[0]))
return new LetterFigure(fill, letter);
var myFunc = listOfPotentialFigures[
Utils.RandomBetweenTwoNumbers(0, listOfPotentialFigures.Count-1)];
return myFunc(fill);
}

This feels MUCH cleaner than switch statement and it's easy to extend with a single line and it exactly expresses what I intend to do. In the future I may look at using an IOC (Inversion of Control) container to add figures dynamically via plugin or discovery model.

I'm interested in your comments as there's very likely an even cleaner pattern to solve this little problem in your head, waiting to get out.

Technorati Tags: ,,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Learning WPF with BabySmash - Pushing things up a level with another set of eyes

June 13, 2008 Comment on this post [6] Posted in BabySmash | Learning .NET | Windows Client | WPF
Sponsored By

NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Issue Tracker and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

BabySmash has a little mini-heirarchy of "Figures" that represent the Shapes on the screen, along with a little additional information like the name of the Figure ("square", "circle") as well as an English word for the color. I intend to internationalize it at some point.

Figure has been pretty simple so far and doesn't really do anything:

public abstract class Figure
{
private UIElement shape;
private readonly string name;
private readonly string color;

protected Figure(Brush fill, string name)
{
this.color = Utils.BrushToString(fill);
this.name = name;
}

public UIElement Shape
{
get { return shape; }
protected set { shape = value; }
}

public string Name { get { return name; } }
public string Color { get { return color; } }
}

Derived Shapes are mostly declarative, and very repetitive:

public class SquareFigure : Figure
{
public SquareFigure(Brush fill)
: base(fill, "square")
{
Shape = new Rectangle()
{
Fill = fill,
Height = 380,
Width = 380,
StrokeThickness = 5,
Stroke = Brushes.Black,
};
}
}

public class StarFigure : Figure
{
public StarFigure(Brush fill)
: base(fill, "star")
{
Shape = new Star()
{
NumberOfPoints = 5,
Height = 400,
Width = 400,
Fill = fill,
StrokeThickness = 5,
Stroke = Brushes.Black,
};
}
}

Last post, we removed a bunch of the Style related stuff and put it into XAML markup as a shared Style application resource we could use all over. This makes things DRYer (Don't Repeat Yourself) but still something doesn't smell right.

I still have to grab the Style and pass the Fill along into the Shape property, and I do it for EVERY Shape. Meh. It's the same four lines over and over again.

public class SquareFigure : Figure
{
public SquareFigure(Brush fill)
: base(fill, "square")
{
Shape s = new Rectangle();
s.Style = Application.Current.Resources["square"] as Style;
s.Fill = fill;
Shape = s;
}
}

Ripe for refactoring I say. Note the call to base() in the Constructor. You'd be surprised how many people either don't know about base() or who knew and forgot. ;)

Oddly, I was already passing Fill into the constructor, and then passing it up the hierarchy to the abstract base class Figure without thinking. This is very common when you slap code together then finally take a breath and LOOK at it. Ever more common when you have someone who isn't "into" the code just look over your shoulder and say "um, what's THAT?" You really can't overestimate how useful another set of eyes are. I did a quick SharedView with Jason this afternoon and his fresh eyes sped up my refactoring considerably.

public abstract class Figure
{
private readonly string name;
private readonly string color;

protected Figure(Brush fill, string name, Shape s)
{
this.color = Utils.BrushToString(fill);
this.name = name;
this.Shape = s;
s.Fill = fill;
s.Style = Application.Current.Resources[Name] as Style;
}

public UIElement Shape { get; protected set; }

public string Name { get { return name; } }
public string Color { get { return color; } }
}

Here we moved everything that was repeated (shared) up into the base class. The Shape property used the C# 3.0 property syntax that let us remove a field (it's generated now). Notice the use of the protected keyword on the setter.

The only thing that "smells" bad about this for me is the call to Application.Current.Resources. This is the moral equivalent to HttpContext.Current as it's saying "reach out into the heavens that you ought not know about and pull a rabbit out of a hat" or, in this case, a resource out of Application.Resources. However, as far as I can tell, this is a minor sin, and a common one, unless I want to start passing all these bits of information around my hierarchy while trying to obey the Law of Demeter, which I'm arguably already breaking.

Regardless, suddenly my library of Figures becomes much simpler. Of course, adding new ones will be even simpler now. For example:

public class RectangleFigure : Figure
{
public RectangleFigure(Brush fill)
: base(fill, "rectangle", new Rectangle()){}
}

public class CircleFigure : Figure
{
public CircleFigure(Brush fill)
: base(fill, "circle", new Ellipse()){}
}

public class TriangleFigure : Figure
{
public TriangleFigure(Brush fill)
: base(fill, "triangle", new Polygon()){}
}

public class StarFigure : Figure
{
public StarFigure(Brush fill)
: base(fill, "star", new Star()){}
}

In the next post I'll refactor the factory that makes these figures into something that allows me to sleep at night. ;)

Technorati Tags: ,,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Learning WPF with BabySmash - Keeping it DRY with XAML Styles

June 12, 2008 Comment on this post [8] Posted in BabySmash | Windows Client | WPF
Sponsored By

 NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Issue Tracker and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

BabySmash is full of shapes and stuff and those shapes a similar look. They have the same stroke (outline color) and stroke thickness (outline width) for example.

There are a lot of places in my code where I not only repeat the assignment of these styles.

Shape = Shape = new Path()
{
Data = MakeCharacterGeometry(nameToDisplay),
Fill = fill,
Stroke = Brushes.Black,
StrokeThickness = 5,
Height = 400
};

This kind of object initializer code is copy/pasted all over. It's not DRY. (Don't Repeat Yourself) First I started looking for object oriented ways to solve this issue. I figured I'd put in some base class that would do the work, or make a Utility (gasp!) class to do this tedium.

Ideally I wanted this stuff in one place (hence DRY) and I wanted to be able to apply it to my shapes. Later, I realized I also wanted to occasionally apply these properties to some shapes and not others. At that point, my object-oriented idea started to fall down.

I felt (and still feel) like most of the Shape stuff for BabySmash belongs in the XAML markup, and folks on CodePlex agreed. Sherwin Rice kindly pointed me towards Styles.

He suggested storing these properties in named bundles of styles in the XAML. This gives me the arbitrary flexibility I needed.

<Style x:Key="circle" TargetType="Ellipse">
<Setter Property="Width" Value="400"/>
<Setter Property="Height" Value="400"/>
<Setter Property="StrokeThickness" Value="5"/>
<Setter Property="Stroke" Value="Black"/>
</Style>

However, as I started moving most of my shape's details over into XAML, things started repeating again. I was trading one kind of "markup" (the C# kind) for another. Poop. Well, turns out you can base styles on styles, so I was able to keep it DRY again.

<Style x:Key="BabySmashBaseStyle" TargetType="Shape">
<Setter Property="StrokeThickness" Value="5"/>
<Setter Property="Stroke" Value="Black"/>
</Style>

<Style x:Key="trapezoid" TargetType="Path"
BasedOn="{StaticResource BabySmashBaseStyle}">
<Setter Property="Data" Value="F1 M 257.147,126.953L 543.657,126.953L 640.333,448.287L 160.333,448.287L 257.147,126.953 Z"/>
</Style>

<Style x:Key="star" TargetType="BabySmash:Star"
BasedOn="{StaticResource BabySmashBaseStyle}">
<Setter Property="NumberOfPoints" Value="5"/>
<Setter Property="Width" Value="400"/>
<Setter Property="Height" Value="400"/>
</Style>

These all live in <Application.Resources> and I can apply them as I like:

Shape s = new Ellipse();
s.Style = Application.Current.Resources[Name] as Style;

I appear to have just touched the surface of Styles, but I'm continuing to dig in. WPF is starting to make sense to me. Just a smidge.

Related Links

Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.