Click here to Skip to main content
Click here to Skip to main content

A XAML-Only Font ComboBox

, 9 Mar 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Barry Lapthorn

United Kingdom United Kingdom
Jack of all trades.

Comments and Discussions

 
QuestionCould be really good PinmemberReiner Block30-May-14 22:33 
GeneralMy vote of 5 PinmemberPeter Huber SG4-Sep-13 20:52 
GeneralMy vote of 5 PinmemberN_cooL10-Aug-13 3:05 
QuestionWon't work... PinmemberSteveXMon5-Sep-12 7:13 
AnswerRe: Won't work... PinprotectorBarry Lapthorn15-Sep-12 0:21 
GeneralMy vote of 5 PinmemberShi Yingjie13-Mar-12 14:26 
QuestionMessage Automatically Removed Pingroupkjlfdgehe9-Mar-12 15:29 
GeneralMy vote of 5 PinmemberCarstenV213-Feb-12 8:29 
GeneralRe: My vote of 5 PinprotectorBarry Lapthorn23-Feb-12 10:44 
GeneralMy vote of 5 PinprotectorPete O'Hanlon7-Feb-12 3:10 
GeneralRe: My vote of 5 PinprotectorBarry Lapthorn7-Feb-12 3:56 
GeneralRe: My vote of 5 PinprotectorPete O'Hanlon7-Feb-12 5:48 
GeneralGood stuff! PinmvpNish Sivakumar6-Feb-12 5:23 
GeneralRe: Good stuff! PinprotectorBarry Lapthorn6-Feb-12 5:25 
GeneralRe: Good stuff! PinmvpNish Sivakumar6-Feb-12 5:28 
GeneralRe: Good stuff! PinprotectorBarry Lapthorn6-Feb-12 5:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 9 Mar 2012
Article Copyright 2012 by Barry Lapthorn
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid