纯 XAML 字体 ComboBox





5.00/5 (17投票s)
一个纯 XAML 的字体组合框。
引言
我需要在 WPF 应用程序中选择 FontFamily
的一个简单 ComboBox
(我不关心字体粗细)。 经过一番搜索,我找到了 Pete O'Hanlon 的文章,其中描述了我想要的内容。
那么为什么还要写另一篇(短!)文章呢?文章中的第一个评论者提出了这个
<ListBox ItemsSource="{Binding Source={x:Static Member=Fonts.SystemFontFamilies}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label FontFamily="{Binding .}" Content="{Binding Source}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
作为一种替代方案,这让我考虑将两者结合起来,作为一个纯 XAML 解决方案,您可以剪切和粘贴(因为 Pete 的代码有一点代码隐藏)。为了创建 XAML 解决方案,我发现了一些有趣的事情,我想在一个真实的例子中分享一下(因为我仍然在努力掌握 WPF 的许多方面)。
展示 XAML 代码!
这是它的全部内容
<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>
您应该能够直接将它剪切并粘贴到您的代码中。然后,您会将 ComboBox
的 SelectedValue
绑定到您选择的属性。 SelectedValue
的类型为 System.Windows.Media.FontFamily
。
发生了什么?
我们需要描述几件事。当心!更冗长的 XAML!
排序的字体列表
直接跳到 ComboBox.Resources
部分:我们获得了完整的系统字体集合。 但是,默认情况下,它们仅部分排序(按 FamilyName
),因此我们将它们排序到我们自己的名为 myFonts
的集合中。 我们通过此 XAML 标记导入 ComponentModel
命名空间来做到这一点
xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
然后创建我们自己的按 Source
属性(字体系列名称)排序的集合
<CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<CollectionViewSource.SortDescriptions>
<ComponentModel:SortDescription PropertyName="Source" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
数据模板
我们声明一个简单的模板,在 ComboBox
中以其自己的字体类型呈现字体,并提供工具提示。
静态资源
最后,我们使用长格式 XAML 绑定将 ComboBox.ItemsSource
绑定到我们排序的字体集合 myFonts
。 为什么我们要最后执行此操作,而不是直接作为 ComboBox
属性?
ItemsSource
属性要求它绑定到静态资源。 假设我们这样做
<ComboBox
xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
ItemTemplate="{DynamicResource FontTemplate}"
ItemsSource="{Binding Source={StaticResource myFonts}}">
我们得到一个异常抛出
"找不到名为“myFonts”的资源。 资源名称区分大小写。"
因为 myFonts
尚未声明。
当然,我们可以将字体集合移动到 UserControl
/Window
/Application
Resources
部分,但是在此示例中,我们只有一个字体组合框,因此最好将其放在 ComboBox.Resources
部分中。
您还可以尝试通过以下方式将 ItemsSource
设置为动态引用 myFonts
:
<ComboBox
xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
ItemTemplate="{DynamicResource FontTemplate}"
ItemsSource="{Binding Source={DynamicResource myFonts}}">
这也会失败,并出现异常
"不能在类型为“Binding”的“Source”属性上设置“DynamicResourceExtension”。 “DynamicResourceExtension”只能在 DependencyObject 的 DependencyProperty 上设置。"
因此,为了回答我们的问题:由于 XAML 有一个“单遍编译器”,因此必须在词法上先声明 StaticResource
,然后才能引用它:如果我们最后声明绑定,那么我们可以在 ComboBox.Resources
中创建我们排序的字体列表 StaticResource
,然后在 ComboBox
的 XAML 中绑定到它,因此有了这段 XAML
<ComboBox.ItemsSource>
<Binding Source="{StaticResource myFonts}" />
</ComboBox.ItemsSource>
使用此 XAML 代码段
如上所述,如果您打算使用此 XAML(并多次使用字体组合框),请移动排序的字体集合
<CollectionViewSource x:Key="myFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
...
</CollectionViewSource>
到您的 Application/Window/UserControl Resources
部分,并将此属性
xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
放入相应的 XAML 文档根目录。
感谢 Pete 的原始 XAML。
关于安全字体使用的最后一句话
永远,永远不要选择 Comic Sans。 永远。