Scott Hanselman introduced us to BabySmash today. It’s an app for his kids that he developed using his arcane Win32 knowledge in a WPF app. He did it on purpose, mind you, to teach himself WPF, with us watching.
I think this is a fantastic idea. When I was writing the AutoComplete TextBox series, I thought it would be a cool idea for all the major WPF book authors (Adam Nathan, Chris Sells & Ian Griffiths, Charles Petzold and Chris Anderson) to write their implementations of the same problem. They’re all very good at explaining, they’d all come up with different solutions and we could all figure out the best way to do things in WPF, as Scott alludes to in his post on BabySmash.
I’ve taken a look at the code and, boy, it needs work. I saw immediately something I’ve experienced pain with before, which I’ll talk about in this post: custom settings. In .NET 2.0, one of the cool unsung heroes is the settings code in System.Configuration. Note that it’s not tied to WPF or WinForms. With it, you’re able to have user- or app-scoped settings. They can be local or roamable. The framework takes care of storage, although it’s extensible. Visual Studio assists with code-gen so you can access your settings with code.
By default, VS will create a Settings class that inherits from ApplicationSettingsBase in the YourProjectNamespace.Properties namespace. The settings you create in the Settings designer get properties generated for them. The Settings class also has a static instance accessed through the Default property. Scott actually created this file, but then didn’t use it. I haven’t used ClickOnce, but I just can’t see Microsoft not supporting the settings file automagically in ClickOnce.
Pictured below is BabySmash’s settings with Scott’s intended properties filled in.
So, like I said, this is independent of UI framework. How do we use it in WPF? Databinding! By far the best thing of WPF, even though you have to put some time in to learn it.
Before I show the code to databind to the application settings, allow me to show you the XAML for the Options page in BabySmash.
<Window x:Class="BabySmash.Options"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Baby Smash! - Options" Unloaded="Window_Unloaded" Loaded="Window_Loaded" Height="188" Width="268" ShowInTaskbar="False" Topmost="True" WindowStartupLocation="CenterScreen" WindowStyle="ThreeDBorderWindow" ResizeMode="NoResize">
<Grid>
<Button Height="23" Name="OK" VerticalAlignment="Bottom" Click="OK_Click" IsDefault="True" Margin="64,0,102,9">OK</Button>
<Button Height="23" Margin="0,0,8,9" Name="Cancel" VerticalAlignment="Bottom" IsCancel="True" HorizontalAlignment="Right" Width="80" Click="Cancel_Click">Cancel</Button>
<Label Height="28" Margin="14,19,0,0" Name="lblClearAfter" VerticalAlignment="Top" HorizontalAlignment="Left" Width="120">Clear After x Shapes</Label>
<TextBox Height="23" Margin="0,19,8,0" Name="txtClearAfter" VerticalAlignment="Top" HorizontalAlignment="Right" Width="101"></TextBox>
<CheckBox Margin="0,81,0,0" Name="chkForceUppercase" Height="16" HorizontalAlignment="Right" VerticalAlignment="Top" Width="109" >Force Uppercase</CheckBox>
<ComboBox Margin="0,48,8,0" Name="cmbSound" Height="23" HorizontalAlignment="Right" VerticalAlignment="Top" Width="101" IsDropDownOpen="False">
<ComboBoxItem>None</ComboBoxItem>
<ComboBoxItem>Speech</ComboBoxItem>
<ComboBoxItem>Laughter</ComboBoxItem>
</ComboBox>
<Label Height="28" Margin="14,43,112,0" Name="label1" VerticalAlignment="Top">Sounds</Label>
</Grid>
</Window>
If you know XAML and how the WPF grid works, then you know that this is really bad code. This is probably the most egregious thing about WPF, IMO: the visual designer. Scott didn’t write this code. Visual Studio did.
Petzold has a great transcript/paper of a talk he gave to a New York user group in 2005 titled, Does Visual Studio Rot the Mind? In it he talks about the evolution of visual programming and Visual Studio. It’s written in 2005, before WPF even got its name, but while he was writing his book, Applications = Code + Markup. But the points he raises are still valid. WPF is, by default, laid out without reference to pixels. The intention of its designers was that with WPF, you say what you want, the WPF figures out how to make it so. Most of the new frameworks from Microsoft have that in their design: "tell me what, I’ll figure out how."
In WPF’s case, it’s completely undermined by the visual designer in Visual Studio. Note above the code snippet from Scott’s Options page: see all those Margin attributes? That’s like hard-coding the same string value in more than one place. This is a dialog box, but humour me. Suppose we allowed this dialog to be resized by the user. What would happen? Chaos.
This is what Visual Studio gives you out of the box. Shameful. And if you want to use databinding, forget it, you have to write the XAML by hand. I am dimly aware of Expression Blend; I’ve used it a few times and I think I remember it being a little smarter than Visual Studio. I think that shows how Microsoft is a little out of touch with developers outside of Microsoft. In theory, I like the idea that developers can work on the app doing something, while designers work on the app’s appearance. Really good idea, in theory. In my experience, the developer is the designer the majority of the time. In this scenario, Visual Studio needs a lot more work.
Anyway, let’s stop ranting. I also think it’s a developer’s responsibility to learn either how technology is used if it’s well-established, in the case, of, say, PHP or ASP.NET; or how its designers intended it to be used in the case of WPF. That’s what I’ve done, and what I’m still doing now. So let’s make an Options window that binds to the app settings. Scott’s was laid out, as he intended it, as the following:
There are plenty of ways to do this with the default panel types in WPF. I’ll use the Grid since it’s the most powerful, the default choice and hence the most popular. I’ll preserve as much of what Scott had, and try to make it pixel perfect.
OK, I’m back. Did you miss me? Here’s what I have:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1.Properties"
xmlns:l="clr-namespace:WpfApplication1"
Title="Baby Smash! - Options"
Height="188" Width="268"
ShowInTaskbar="False"
Topmost="True"
WindowStartupLocation="CenterScreen"
WindowStyle="ThreeDBorderWindow"
ResizeMode="NoResize">
<Window.Resources>
<local:Settings x:Key="settings" />
</Window.Resources>
<Grid DataContext="{StaticResource settings}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Height="23" Margin="10,20,0,0" Grid.ColumnSpan="2">Clear after x Shape</Label>
<TextBox Text="{Binding Path=Default.ClearAfter}"
Height="23" Grid.Column="1"
Margin="15,20,0,0"/>
<Label Height="23" Grid.Row="1" Margin="10">Sounds</Label>
<ComboBox Grid.Column="1" Grid.Row="1" Height="23" Margin="15,0,0,0">
<ComboBoxItem >None</ComboBoxItem>
<ComboBoxItem >Laughter</ComboBoxItem>
<ComboBoxItem >Speech</ComboBoxItem>
</ComboBox>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="15,0,0,0"
IsChecked="{Binding Path=Default.ForceUppercase,Mode=TwoWay}" >
Force Uppercase
</CheckBox>
<StackPanel Orientation="Horizontal" Grid.Row="3" Grid.ColumnSpan="2"
HorizontalAlignment="Right">
<Button Name="okButton" IsDefault="True"
Margin="0,7,10,7"
Padding="30,0,30,0" Click="okButton_Click">OK</Button>
<Button IsCancel="True"
Margin="5,7,7,7"
Padding="15,0,15,0">Cancel</Button>
</StackPanel>
</Grid>
</Window>
It looks more verbose for sure, but I think that’s more because of editing by hand and using the ENTER key. Let’s pull out unnecessary stuff and show the code that binds to the application settings
<Window x:Class="WpfApplication1.Window1"
xmlns:local="clr-namespace:WpfApplication1.Properties"
xmlns:l="clr-namespace:WpfApplication1"
>
<Window.Resources>
<local:Settings x:Key="settings" />
</Window.Resources>
<Grid DataContext="{StaticResource settings}">
<TextBox Text="{Binding Path=Default.ClearAfter}" />
<CheckBox IsChecked="{Binding Path=Default.ForceUppercase}" >
Force Uppercase
</CheckBox>
<StackPanel >
<Button Name="okButton" IsDefault="True"
Margin="0,7,10,7"
Padding="30,0,30,0" Click="okButton_Click">OK</Button>
...
</StackPanel>
</Grid>
</Window>
There are some concepts you’ll see over and over again WPF: the use of ResourceDictionaries (Window.Resources in the XAML), DataContext, and DataBindings. I won’t explain all of it in detail (this post is long as is), but I’ll briefly explain it and bold terms that you can search the internets for more info.
Most of the XAML should be familiar even if you’ve never seen it before. It’s fairly close to ASP.NET code, if memory serves, and the XML hierarchy is a good fit for a model that represents a window as a nested tree of controls. The Windows.Resources element adds a Settings object to the window’s ResourceDictionary. All resource dictionary items must have a key to refer to. We set the DataContext on the Grid to the settings object declared in the resources. It’s a StaticResource which means that WPF loads it once and stops listening to updates to that resource. The DataContext is essentially associating the Settings object with the Grid. The Binding on the TextBox’s Text property is BindingExpression whose Path points to the Default.ClearAfter property of the Settings object. That’s a little advanced, but hey, it’s "real world." And that’s all you have to do to get the settings to show up in the right controls. Notice there is no procedural code.
There’s one more thing to talk about before I quit. The Settings object’s properties change when you change the values in the controls (set the TextBox to 32, and ClearAfter is set to 32), but we have to tell the Settings object that we want to keep those values. We do that in okButton’s handler:
private void okButton_Click(object sender, RoutedEventArgs e)
{
Properties.Settings.Default.Save();
this.Close();
}
Too easy, eh?
There are some caveats because I didn’t test thoroughly: databinding by default updates the source after focus changes so if you set the TextBox to 32, then click OK, it may not update the property properly as you’d expect. I explicitly left the third property in settings to be set by you if you want to take up the challenge. I added the settings in BabySmash but they weren’t showing up properly. I don’t know why and I didn’t pursue.
So there you have it. I didn’t show Scott’s Option dialog code, but I’ve significantly reduced it. My Options Dialog window XAML code may be a little longer but I get some layout benefits by using the Grid to its potential. Of course, you may do something different, and it may be better, but that’s the whole point of the Scott’s app. There are some treats in the solution below. I got tired of writing this post before I covered everything that I noticed. I’m going to play video games now.
No one who knows me would describe me as humble. I guess if everyone thinks I’m not humble, maybe I’m not, I don’t know.
One thing I do approach with humility is my ability as a programmer. It took some concentration to “get” the fancy algorithms in university; I’d always try to solve the problem with brute force and “move up” as needed. I still do. I don’t think that’s wrong, either; worse is better. I practice TDD when I’m writing something that matters. I also remind myself that the first question isn’t “How do I do this?”, but “How has everyone done this before?”
Judging by the code I’ve read from my colleagues and online, a lot of people don’t share this perspective. They blindly assume that they have to solve every programming problem that crops up and that they can solve every problem better than everyone else who programmed before them ever. I doubt most programmers think that way but the results in the code could support that notion.
There are lots and lots of developers out there, all with a lot of the same types of problems to solve; some times – I’d say rarely – you need to revisit the problem yourself, but chances are, you can just use what other people have done. You can see this trend with the technologies and languages in use today verses, say 50 years ago. There’s a definite progression from machine code, to assembly, to procedural, to OO, to runtimes and frameworks. The code in J2EE or .NET or Python or Rails embodies thousands of little problems already solved for you.
To be a good programmer, therefore, one must get pretty good at searching the docs of your particular framework; learn to read source code; and always remember that someone already solved your problem. Even Isaac Newton didn’t do it alone. Being a humble programmer allows you to progress passed solved problems.
Before I end this post, a few examples from .NET:
- File paths are something everyone has to deal with if you’re writing a desktop app. You have a directory path and a file name and you need to concatenate the two to work with the file on disk. You could write
string path = dirPath + fileName;
but that assumes that dirPath ends with ‘\’ on Windows (let’s not consider Mono right now). So, now we have to check to see if dirPath ends with ‘\’. Or, you could just use Path.Combine! It’s been in the framework since 1.0! That Path class has all kinds of good stuff on it. Use it! Stop reinventing the wheel.
- Here’s one that I didn’t know about until a few weeks ago: Title case for a string involves capitalizing the first letter in every word. I’ll admit I’ve written functions in production scenarios, but I didn’t have to, and now I never will again because of the TextInfo.ToTitleCase method. TextInfo hangs off of CultureInfo, and it’s in the System.Globalization. I bet your naive implementation wouldn’t have considered internationalization. Microsoft has to deal with that a lot, they’re probably really good at it. Solve your problem, not one that Microsoft already has; it’s in the framework!
Here’s a rule I usually follow: if there is a requirement to do something, always, always, always, check the Google, or your favourite search engine to see what others have done.
Have you got any methods or classes that are in the framework but nobody uses?
Recently, at work, we started using project code names. The theme for the code names is mountains.
Gee, I wonder where they got that idea from.
What’s the whole point of a codename? I think it’s to refer to a product release without getting all confused about version numbers. There is nothing saying they have to be plain and boring. You can say, “Were the bugs y and z fixed for the latest beta release of Lava Lamp?” Now, what’s wrong with that? Lava lamps are fun and mysterious. It would make the app you’re working on more fun to work on, especially if it’s “themware”.
My favourite themes are probably ‘80s cartoons characters. What about Project She-Ra? Or Project Megatron? Now that’s a server I don’t want to mess with; its security must be hard to get through.
Actually, anything from the ‘80s works. How about ‘80s rock divas? Project Benatar has a nice ring to it. ‘80s movies? Project Porky’s for version 1, Porky’s Revenge for version 2.
Got any ideas for good project codename themes?
Now playing: Odds – Satisfied
Update: Fixed some grammatical errors that were bugging my mom. 🙂 Thanks, mom.
I recently reviewed Framework Design Guidelines, the new book by Abrams and Cwalina about designing frameworks for .NET. In the review, I mentioned that I disagreed with their advice of using System.Uri (v.1.1) to represent some URIs. Here’s why.
The name System.Uri implies, to me, a URI, any URI. If your URI scheme follows the “rules” of the URI RFC (when .NET 1.1 came out, that would be RFC 2396), the System.Uri should be able to parse it, and you should be able to use the properties exposed by the type for information inside the URI: the host, the scheme, the user info, etc.
For the “big” URI schemes – http, ftp, file, mailto, news, and, of course, gopher – this is not a problem. They are parsed according to the rules and everything works as expected. Even if you decided to use your own, custom hierarchical URI scheme (http is an example of a hierarchical URI scheme), then System.Uri will parse it, as the code below demonstrates.
However, try giving System.Uri a mailto-style URI (i.e. something:user@example.com), and it’ll shit the bed. Then it’ll deny anything is wrong. You may be struggling to come up with an example, but, trust me, there are many; and they will quickly gain importance in our daily programmer lives: SIP, XMPP (kinda), and common presence and IM all represent users with mailto-style URIs. The only property that doesn’t return null with those URIs is Uri.Scheme. When I first found this out, I was shocked; I thought I was doing something wrong. One of the things that Brad and Krzysztof advise is not to surprise the user of your API, or go against their expectations.
Do you think System.Uri succeeds here?
using System;
class Program
{
static void Main(string[] args)
{
Uri[] u = new Uri[7];
u[0] = new Uri(“http://www.jasonkemp.ca/Rss.aspx”);
u[1] = new Uri(“feed://www.jasonkemp.ca/Rss.aspx”);
u[2] = new Uri(“purplemonkeydishwasher://www.jasonkemp.ca/Rss.aspx”);
u[3] = new Uri(“mailto:jasonkemp@example.com”);
u[4] = new Uri(“msn:jasonkemp@example.com”);
u[5] = new Uri(“sip:jasonkemp@example.com”);
u[6] = new Uri(“pres:jasonkemp@example.com”);
for (int i = 0; i < u.Length; i++)
{
PrintHost(u[i]);
}
for (int i = 3; i < u.Length; i++)
{
PrintUserInfo(u[i]);
}
}
private static void PrintUserInfo(Uri uri)
{
Console.WriteLine(“uri.UserInfo for {0} = {1}”,
uri, uri.UserInfo);
}
private static void PrintHost(Uri uri)
{
Console.WriteLine(“uri.Host for {0} = {1}”, uri, uri.Host);
}
}
In part 2, I’ll discuss why extending Uri to make up for the above deficiencies will cause you to shit the bed.