Scott Hanselman

Learning WPF with BabySmash - Keeping it DRY with XAML Styles

June 12, '08 Comments [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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, June 12, 2008 8:36:41 PM UTC
I really gonna have to start using WPF.

It looks like doing ASP.NET but that looks like Forms and have way more power.

Hummm... this weekend!
Thursday, June 12, 2008 8:40:48 PM UTC
You could put the style you mentioned above in <App.Resources>, and then override it where you didn't want the style to apply. For example, you could simply change the properties in the control that you wanted to be different. You could put another style in the (insert control name here).resource etc. This should help you along in keeping things "dry".

<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes\InfoTextBox.generic.xaml"/>
<ResourceDictionary Source="Themes\TextBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

<Style x:Key="PathMargin" TargetType="{x:Type Path}">
<Setter Property="Margin" Value="0,8,0,32"/>
</Style>

</ResourceDictionary>

</Application.Resources>

I did themes for like items that made "sense" to group together. But for my paths I just wanted to make sure the margins were the same, so they just got a Style entry.

Hope this helps.
David
David in Charlotte
Thursday, June 12, 2008 9:24:02 PM UTC
Great to see that my article (which was my very first for MSDN if I remember correctly) is still being used :)
Thursday, June 12, 2008 10:34:41 PM UTC
If you pony up the bucks, Total Training (totaltraining.com) has EXCELLENT WPF training videos. I am not a shill for TotalTraining, just a satisfied customer. The two presenters who do a very good job are Dante Gagne and Robby Ingebretsen (sp?). Robby is amazing at typing in XAML in XAMLpad which I think everyone doing WPF needs to know at some level. Using Blend is great but knowing what <Border/> means and how to work with XAML directly is indispensible.
Louis
Friday, June 13, 2008 3:57:37 AM UTC
styles on styles.. its amazing in IT what that one level further of abstraction can do.. pointers, or pointers to pointers.. DNS aliases, Virtual Machines....
Friday, June 13, 2008 6:24:12 AM UTC
Hi,

At http://www.xamltemplates.net you cand download a free style for all the controls in wpf and you can have a play with it.
Friday, June 13, 2008 1:14:06 PM UTC
Considering the number of resource-able brushes, styles, datatemplates, controltemplates, etc you can have in WPF, keeping it DRY can be difficult. A nice addin to have would allow you to do stuff inline and at one go refactor all shareable resources into either the window or application level resource dictionary.

The coolest thing about resources is that you can index them by name (x:Key) or by type (TargetType), or by both. If you index them by x:Key, you must always refer to them as a static resource {StaticResource MyKey}. If you index them by type, they will be applied to every instance of that type

That means if you have a button, you can put a control template indexed on the type "Button" in your application resources and have every button automatically pick up that style in any window in your app without any further work on your part.

TargetType doesn't have to be a CLR type; it can also be the name of an element in an embedded XML "island". Index your datatemplate on the element name and that template will be used wherever you bind against that x:Data XML island. I found this particularly useful when adding a hybrid help/input system into a small application I'm working on. I could add an xml document as a data island in my application resources that had not only the input values, but also a name, description, and tooltip attributes for each option. I then created data templates for each node type and bound the island to a treeview. Without any codebehind or custom controls (!!!!), I got a very rich UI experience that was also very DRY. Imagine doing something like that in windows forms in anything less than a week. I did it in a single evening, and I'm pretty new to WPF.

I so friggen love WPF.
Tuesday, June 17, 2008 3:08:18 PM UTC
omg, WPF is soo cool
kiki
Comments are closed.

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