Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A XAML-Only Font ComboBox

0.00/5 (No votes)
9 Mar 2012 1  
A XAML-only font combobox.

325753/xamlfontchooser_menu.png

Introduction

I needed a simple ComboBox to select a FontFamily in a WPF application (I don't care about the font-weight). After some searching, I found Pete O'Hanlon's article describing what I wanted.

So why another (short!) article? The first commenter in the article suggested this:

<ListBox ItemsSource="{Binding Source={x:Static Member=Fonts.SystemFontFamilies}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Label FontFamily="{Binding .}" Content="{Binding Source}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

as an alternative, which made me think about combining the two as a pure XAML solution that you can cut and paste (as Pete's code has a tiny bit of code-behind). In order to create the XAML solution, I found out a few interesting things that I thought I would share in a real example (as I'm still getting to grips with the many facets of WPF).

Show Me the XAML!

Here it is, in its entirety:

<ComboBox 
          xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
          ItemTemplate="{DynamicResource FontTemplate}">
    <ComboBox.Resources>

        <CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
            <CollectionViewSource.SortDescriptions>
                <ComponentModel:SortDescription PropertyName="Source" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>

        <Style x:Key="FontStyle">
            <Setter Property="Control.FontFamily" Value="{Binding Source}" />
            <Setter Property="Control.FontSize" Value="16" />
        </Style>

        <DataTemplate x:Key="FontTemplate">
            <StackPanel VirtualizingStackPanel.IsVirtualizing="True">
                <TextBlock Style="{StaticResource FontStyle}"
                           Text="{Binding Source}"
                           ToolTip="{Binding Source}" />
            </StackPanel>
        </DataTemplate>

    </ComboBox.Resources>

    <ComboBox.ItemsSource>
        <Binding Source="{StaticResource myFonts}" />
    </ComboBox.ItemsSource>
</ComboBox>

You should be able to cut 'n' paste this directly into your code. You would then bind the ComboBox's SelectedValue to a property of your choice. The SelectedValue is of type System.Windows.Media.FontFamily.

What is Going On?

There are several things going on that we need to describe. Beware! More verbose XAML!

A Sorted List of Fonts

Skipping directly to the ComboBox.Resources section: we get the full collection of system fonts. However, by default, they only come partially sorted (by FamilyName), so we sort them into our own collection called myFonts. We do this by importing the ComponentModel namespace via this XAML markup:

xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"

and then create our own collections sorted by the Source property (which is the font family name):

<CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
    <CollectionViewSource.SortDescriptions>
        <ComponentModel:SortDescription PropertyName="Source" />
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

Data Template

We declare a simple template that renders the fonts in their own type face, and provides a tooltip, within the ComboBox.

Static Resources

Lastly we bind the ComboBox.ItemsSource to our sorted collection of fonts, myFonts, using the long-hand XAML binding. Why do we do this last, and not directly as a ComboBox attribute?

The ItemsSource attribute requires that it is bound to a static resource. Suppose we do this:

<ComboBox 
  xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
  ItemTemplate="{DynamicResource FontTemplate}"
  ItemsSource="{Binding Source={StaticResource myFonts}}">

We get an exception thrown:

"Cannot find resource named 'myFonts'. Resource names are case sensitive."

as myFonts has not yet been declared.

We could of course move our font collection to the UserControl/Window/Application Resources section, however in this example we only have one font combo box, so it is nice to have it within the ComboBox.Resources section.

You might also try setting the ItemsSource to reference myFonts dynamically, via:

<ComboBox 
  xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
  ItemTemplate="{DynamicResource FontTemplate}"
  ItemsSource="{Binding Source={DynamicResource myFonts}}">

This also fails with the exception:

"A 'DynamicResourceExtension' cannot be set on the 'Source' property of type 'Binding'. A 'DynamicResourceExtension' can only be set on a DependencyProperty of a DependencyObject."

So in answer to our question: as XAML has a 'one-pass compiler', a StaticResource has to be declared lexically before it is referenced: if we declare the binding last, then we can create our sorted list of fonts StaticResource, within ComboBox.Resources, and then bind to it within the XAML of the ComboBox, hence this piece of XAML:

<ComboBox.ItemsSource>
    <Binding Source="{StaticResource myFonts}" />
</ComboBox.ItemsSource>

Using this XAML Snippet

As mentioned above, if you intend on using this XAML (and using the font combobox multiple times), move the sorted font collection:

<CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
    ...
</CollectionViewSource>

into your Application/Window/UserControl Resources section, and put this attribute:

xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"

into the corresponding XAML document root.

Tip o' the hat to Pete for the original XAML.

A Final Word on Safe Font Usage

Never, ever, choose Comic Sans. Ever.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here