Scott Hanselman

Learning WPF with BabySmash - Customer Feedback and a WPF Font ComboBox

July 18, '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!

When I decided I was running out of ideas for BabySmash! I decided that I needed to get some customer feedback. The babies had less to say that expected, but there are many videos of babies testing the application popping up on Flickr and YouTube, so I'll call that End User Testing.

I found a site called UserVoice.com (another similar one is GetSatisfaction.com) that is basically a hosted instant customer feedback site. Poof. UserVoice.com even supports a few widgets via Javascript like this one for embedding on your site.

It also supports DNS CName aliasing, so http://feedback.babysmash.com looks like it's hosted by me at http://www.babysmash.com, but instead it's at UserVoice. It's really professional and takes literally minutes. I had the whole thing installed an integrated in under 15 minutes, including color customization and logo.

After I put the site up and inserted links to BabySmash Feedback on the site, there were two ideas that didn't get many votes but had great potential and I figured would be fun to do. One person suggested using Wingdigs (the silly font) to use letter keystrokes to show the fun pictures embedded in the font, while the other suggestion was just to allow a custom font.

When I make the Letters, I create a single object of FormattedText and had hardcoded "Arial," then I call BuildGeometry() on that which basically creates a Path or Outline of the letter that I can mess with. I fill it, and outline it and sent it on its way. I hadn't even given alternative fonts a second thought.

private static Geometry MakeCharacterGeometry(string t)
{
var fText = new FormattedText(
t,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(
new FontFamily(Properties.Settings.Default.FontFamily),
FontStyles.Normal,
FontWeights.Heavy,
FontStretches.Normal),
300,
Brushes.Black
);
return fText.BuildGeometry(new Point(0, 0)).GetAsFrozen() as Geometry;
}

So armed with this idea, I added FontFamily as an option in Visual Studio Settings for this project. Remember that I refactored the Options Dialog a few weeks back using Jason Kemp's code. This made it easy to just add one additional ComboBox. I'm getting good enough that I added the XAML in the Text Editor pane in Visual Studio rather than the designer.

image

I figured, since this WPF, can't I make that ComboBox more interesting than just a bunch of strings? Why not have the FontNames show up in the style of the Font like in Word? Well, System.Windows.Media.Fonts.SystemFontFamilies has the list of the fonts on the machine. I searched around and found part of what I needed at Norbert Eder's WPF blog. (BTW, congrats on being a newly minted MVP!)

<Window.Resources>
<local:Settings x:Key="settings" />
<CollectionViewSource Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}" x:Key="myFonts"/>
</Window.Resources>

I can make a CollectionViewSource and make it a resource that is available to the Window, as seen above. The source is the collection of FontFamilies. Next, I make a regular ComboBox, which usually looks like this if you are just holding strings:

<Label Height="23" Grid.Row="2" Margin="10">Cursor</Label>
<ComboBox
SelectedValue="{Binding Path=Default.CursorType}"
SelectedValuePath="Content" Grid.Row="2" Height="23" Margin="70,10,7,0" Grid.ColumnSpan="2" VerticalAlignment="Top">
<ComboBoxItem>Hand</ComboBoxItem>
<ComboBoxItem>Arrow</ComboBoxItem>
</ComboBox>
But in mine, I'm going to bind the ItemsSource to the list of Fonts from the Resources above. I'll say that the ItemsPanel will be a VirtualizingStackPanel which is amazing. You know when you have a long list of potentially expensive-to-show stuff? The VirtualizingStackPanel knows it's expensive takes care of doing the minimal amount of work it takes to show just those items that are visible. This makes rendering hundreds of fonts way faster.
<ComboBox x:Name="FontChooser" Grid.Row="3" Margin="70,10,7,0" 
ItemsSource="{Binding Source={StaticResource myFonts}}"
SelectedValue="{Binding Path=Default.FontFamily}" SelectedValuePath="Source" Height="23" VerticalAlignment="Top" Grid.ColumnSpan="2">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontFamily="{Binding}" Height="20"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

The Data Template is what each item looks like, which is just the name of the font and setting the FontFamily to that item's FontFamily! Hard to write until you've done it once, then you start thinking of other cool stuff you can do with this knowledge elsewhere in your app.

The one thing that it flummox me for a half-hour was that I was binding the ComboBox to the list of Fonts from my Resources, but I wanted the SelectedValue (the item that is selected in the ComboBox) to be bound like all the other controls in my options dialog, to my application's Settings. That's where these attributes came in:

SelectedValue="{Binding Path=Default.FontFamily}" SelectedValuePath="Source" 

There the SelectedValue the FontFamily string from my Settings class (just like the Default.CursorType and others in the dialog) and the SelectedValuePath shows how to dig into that value. In this case, the Source property had the name (string) of the FontFamily.

The result looks like this:

 Baby Smash! - Options

I'm starting to "grok" WPF more each evening I code on BabySmash. I know I'm still just barely scratching the surface. I'm looking forward to implementing (with your help) more of the BabySmash Feedback Items.

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
Friday, July 18, 2008 8:00:36 AM UTC
Too bad that the databinding combined with the dynamic nature of WPF gives the option dialog a (for me highly unwanted) usebility "feature": the new font combo switches width dynamically based on the fonts that actually showing at the moment. This makes it a real pain to browse through by clicking on the scrollbar; it is at a different horizontal location after each click! (Also, the backspace doesn't seem to work in textboxes. Is that you, or WPF dynamic binding in some way to?)

Not something that the actual "usee base" of this application will mind, as they are not meant to be in the option dialog themselves, I think, but... ;-)
Friday, July 18, 2008 8:17:49 AM UTC
Good point! I'll find a way to set that fixed.
Scott Hanselman
Friday, July 18, 2008 2:39:44 PM UTC
Good post
Friday, July 18, 2008 3:25:24 PM UTC
"...there were two ideas that didn't get many fonts..."

didn't get many votes?

re: Jarno,

I have also had similar problems in my own applications, where drop-downs resize based on their contents. The only way I have been able to get around it is to fix the size of the drop-down at a width that is known to be greater than that of any of the items, but of course at that point I give up any of the dynamic transformation abilities of WPF. Hopefully Scott can come up with a better solution that I can then steal :)
Friday, July 18, 2008 5:35:40 PM UTC
The baby smash feedback link is missing a slash.
Friday, July 18, 2008 5:42:01 PM UTC
Both fixed, thanks!
Friday, July 18, 2008 6:11:43 PM UTC
This game is way too simple. I took me less than a half hour to beat it.
GuyIncognito
Tuesday, August 12, 2008 2:20:47 PM UTC
Does anyone know of a way to determine if the font uses a non-latin character set, so that it can display the font name in a "plain" font and then a preview on the right, as it does in Word 2007? This is culture-dependent of course - if the current culture is for Arabic characters, you'd want the "plain" font to be an arabic one and to display for non-arabic character set fonts. I wonder what culture would consider Wingdings to be "plain"? :P
Giraffe
Comments are closed.

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