在Winform时代图标,图标都是在资源文件下创建JPG或者PNG来作为图片实现,但是随着TTF字体图标的普及,图标类型的图片越来越多的被放入到TTF中。 比如fontawesome 、iconfont 等,本文以iconfont为例聊一聊在WPF下如何使用TTF字体作为图标。
 

下载


     iconfont是阿里巴巴下面的矢量图标管理、交流平台,用户注册登录后,可以在上面搜索和下载需要的矢量图标。
    第一步是搜索需要的图标,然后将其添加到购物车: 
    等所有想要的图标都添加到购物车之后,可以点击“下载代码”,这里以我前面的NTP时间同步程序需要的11个图标为例:
    下载完成之后,它是一个压缩包,解压出来:
    在WPF中,真正需要用到的是.ttf这个文件,.html这个文件可以帮助提供所需图标的对应代码。
 

使用


     要使用ttf文件,首先要将上述安装包里面的.ttf文件加入到项目中来,并将其编译属性设置为“资源”。
        现在可以在代码中使用了。我这里有两个场景,一个是作为按钮的图片,一个是作为弹出菜单的ico,这里分别讲解。
    作为按钮的图片很简单。
    在按钮前面添加图片,只需要将按钮的Content设置为StackPanel,然后在里面水平放置两个TextBlock,将第一个TextBlock的Text属性设置为字体图标即可,注意,这里是TextBlock不是Image:
<Button  Margin="15 5 0 5" VerticalAlignment="Center" Name="btnSettingTiming" Click="BtnSettingTiming_OnClick">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="&#xe61f;" FontFamily="/Resources/#iconfont" FontSize="14" VerticalAlignment="Center"></TextBlock>
        <TextBlock Name="btnSettingTimingText"  Text="{DynamicResource S.TimeSetting.SettingTime}" Margin="5 0 0 0"/>
    </StackPanel>
</Button>
     第一个TextBlock就是图标,这里设置了FontFamily为Resources文件夹下的iconfont.ttf文件,需要注意的是,FontFamily的规则是: # + 字体名称(去掉.ttf),字体的文件名是iconfont.ttf,所以在这里面设置的就是#iconfont。
    第二个就是Text属性,这个属性指定了tff文件里面的哪个图标,可以在提供的.html文件里查看。
   这里闹钟图标是“&#xe61f;”,所以Text属性设置为“”,注意当在C#代码中指定是,需要换为“0xe61f;”。
   这里是将TextBlock的文字作为了图片。有时候比如MenuItem也需要图片,当然,也可以在MenuItem的Header中做如上的操作:
<MenuItem Header="{DynamicResource S.Options.Language.English}" Tag="en" IsCheckable="True" Click="MenuItem_Click">
    <MenuItem.Header>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="&#xe605;" FontFamily="/Resources/#iconfont"   FontSize="14" VerticalAlignment="Center"/>
            <TextBlock Text="{DynamicResource S.Options.Language.English}" Margin="5 0 0 0"/>
        </StackPanel>
    </MenuItem.Header>
</MenuItem>

    但这实际上这是在菜单的文字前面放了一张图片,MenuItem有专门显示图标的位置,就是.ICO属性,它是一个image对象,那么如何将一个字体图标转换为image对象呢?有一些繁琐,如下:

MenuItem Header="{DynamicResource S.TimeSetting.SyncNow}"  Command="{StaticResource SyncNow}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ContextMenu}}}" >
    <MenuItem.Icon>
        <Image Stretch="None">
            <Image.Source>
                <DrawingImage>
                    <DrawingImage.Drawing>
                        <GeometryDrawing>
                            <GeometryDrawing.Brush>
                                <VisualBrush Stretch="Uniform">
                                    <VisualBrush.Visual>
                                        <TextBlock FontFamily="/Resources/#iconfont"  Text="&#xe613;"/>
                                    </VisualBrush.Visual>
                                </VisualBrush>
                            </GeometryDrawing.Brush>
                            <GeometryDrawing.Geometry>
                                <RectangleGeometry Rect="0,0,32,32"/>
                            </GeometryDrawing.Geometry>
                        </GeometryDrawing>
                    </DrawingImage.Drawing>
                </DrawingImage>
            </Image.Source>
        </Image>
    </MenuItem.Icon>
</MenuItem>
     当然,可以将这些XAML代码转换为类,先定义一个继承自StaticResourceExtension的名为IconImageExtension的类:
[MarkupExtensionReturnType(typeof(DrawingImage))]
public class IconImageExtension : StaticResourceExtension
{
    private static readonly FontFamily fontFamily
   //=   new FontFamily("file:///D:/Study/WPFStudy/WPFStudy/NTPClock/Resources/#iconfont");
   = new FontFamily(new Uri("pack://application:,,,/"), "./Resources/#iconfont");
    public int SymbolCode { get; set; }

    public double SymbolSize { get; set; } = 16;

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var textBlock = new TextBlock
        {
            FontFamily = fontFamily,
            Text = char.ConvertFromUtf32(SymbolCode)
        };

        var brush = new VisualBrush
        {
            Visual = textBlock,
            Stretch = Stretch.Uniform
        };

        var drawing = new GeometryDrawing
        {
            Brush = brush,
            Geometry = new RectangleGeometry(
                new Rect(0, 0, SymbolSize, SymbolSize))
        };

        return new DrawingImage(drawing);
    }
}
    这里要特别注意的是FontFamily的加载方式。有了这个扩展类,前面的一大段XAML语句就可以简化为:
<MenuItem Header="{DynamicResource S.TimeSetting.SyncNow}"  Command="{StaticResource SyncNow}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ContextMenu}}}" >
    <MenuItem.Icon>
        <Image Stretch="None"  Source="{local:IconImage SymbolSize=16, SymbolCode=0xe613}"/>
    </MenuItem.Icon>
</MenuItem>
 
 
参考