Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / XAML

A XAML-Only Font ComboBox

Rate me:
Please Sign up or sign in to vote.
5.00/5 (18 votes)
9 Mar 2012CPOL3 min read 64.9K   1.4K   20   19
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:

XML
<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:

XML
<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:

XML
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):

XML
<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:

XML
<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:

XML
<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:

XML
<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:

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

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

XML
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)


Written By
United Kingdom United Kingdom
Jack of all trades.

Comments and Discussions

 
QuestionWhere is the SelectedValue stored? Pin
Member 118692094-Oct-23 3:59
Member 118692094-Oct-23 3:59 
QuestionThis is just excellent Pin
jonavark15-Nov-22 5:47
jonavark15-Nov-22 5:47 
QuestionAny reason for not to use 'Comic Sans' Font? Pin
aruyc27-Mar-20 3:42
aruyc27-Mar-20 3:42 
QuestionCould be really good Pin
Reiner Block30-May-14 22:33
Reiner Block30-May-14 22:33 
GeneralMy vote of 5 Pin
Peter Huber SG4-Sep-13 20:52
mvaPeter Huber SG4-Sep-13 20:52 
GeneralMy vote of 5 Pin
N_cooL10-Aug-13 3:05
N_cooL10-Aug-13 3:05 
QuestionWon't work... Pin
SteveXMon5-Sep-12 7:13
SteveXMon5-Sep-12 7:13 
I keep getting this error...

{"Input file or data stream does not conform to the expected file format specification."}

Has to be something easy. Any ideas?
AnswerRe: Won't work... Pin
Barry Lapthorn15-Sep-12 0:21
protectorBarry Lapthorn15-Sep-12 0:21 
GeneralMy vote of 5 Pin
Shi Yingjie13-Mar-12 14:26
Shi Yingjie13-Mar-12 14:26 
GeneralMy vote of 5 Pin
Carsten V2.013-Feb-12 8:29
Carsten V2.013-Feb-12 8:29 
GeneralRe: My vote of 5 Pin
Barry Lapthorn23-Feb-12 10:44
protectorBarry Lapthorn23-Feb-12 10:44 
GeneralMy vote of 5 Pin
Pete O'Hanlon7-Feb-12 3:10
subeditorPete O'Hanlon7-Feb-12 3:10 
GeneralRe: My vote of 5 Pin
Barry Lapthorn7-Feb-12 3:56
protectorBarry Lapthorn7-Feb-12 3:56 
GeneralRe: My vote of 5 Pin
Pete O'Hanlon7-Feb-12 5:48
subeditorPete O'Hanlon7-Feb-12 5:48 
GeneralGood stuff! Pin
Nish Nishant6-Feb-12 5:23
sitebuilderNish Nishant6-Feb-12 5:23 
GeneralRe: Good stuff! Pin
Barry Lapthorn6-Feb-12 5:25
protectorBarry Lapthorn6-Feb-12 5:25 
GeneralRe: Good stuff! Pin
Nish Nishant6-Feb-12 5:28
sitebuilderNish Nishant6-Feb-12 5:28 
GeneralRe: Good stuff! Pin
Barry Lapthorn6-Feb-12 5:30
protectorBarry Lapthorn6-Feb-12 5:30 

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

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