迁移桌面程序到MS Store(11)——应用SVG图标

在传统桌面程序中,对图标的使用大可能是直接嵌入JPG或者PNG的图片。在祖传的1366x768分辨率下,并无什么问题。相对于手机硬件的日新月异,也侧面反映了PC行业的落寞和桌面程序开发的不思进取。用360卫士的群众并不能倒推PC行业的升级。反却是水果公司双高的利润和口碑让人非常眼馋。加之某软跳出来教猪队友作硬件。如今却是有些转机,1080p的屏幕已经是标配,4k也算常见。那么传统桌面程序在升级过程当中,就会遇到今天要讨论的,如何解决高分辨率下图标模糊的问题。

一种解决方案是按最高的分辨率提供图片。这种适合较大的图片,好比背景啥的。另外一种就是今天要讨论的,针对当前流行的、扁平化图标的解决方案。html

 从本篇的标题能够看出,咱们但愿应用SVG矢量图来适应各类分辨率的状况。以WPF程序为例,首先要面对的问题是,WPF并不支持像嵌入JPG/PNG图标这样,直接使用SVG图标。大动干戈的引用第三方library经过自定义类型来支持SVG并非本文的目的。这里咱们要介绍如何经过字体文件,进而在WPF或UWP中使用SVG图标的方式。

虽然WPF不支持直接使用SVG文件,可是Windows是支持矢量字体的。而咱们的目的就是要将图标以字体的形式在WPF程序中显示。具体使用的字体TrueType,则是由微软和苹果共同开发的字体类型标准,该字体文件的扩展名是.ttf。

https://en.wikipedia.org/wiki/TrueType

接下来咱们依然是经过Sample工程来讲明。首先给出GitHub的地址:

https://github.com/manupstairs/WpfAppForFontIcon

首先咱们打开WpfAppWithPNGs工程,图标的使用代码以下:git

        <Image Grid.Row="0" Grid.Column="0"  Width="32" Height="32" Source="Resources/Airplane_Off.png" ></Image>
        <Image Grid.Row="0" Grid.Column="1"  Width="64" Height="64" Source="Resources/Airplane_On.png" ></Image>
        <Image Grid.Row="0" Grid.Column="2"  Width="96" Height="96" Source="Resources/Bluetooth_Off.png"  ></Image>
        <Image Grid.Row="0" Grid.Column="3"  Width="128" Height="128" Source="Resources/Bluetooth_On.png"  ></Image>

 

这里主要有两个问题,由于咱们默认提供的是32x32的图标,所以除了第一列Width和Height设置为32的图标,其余的图标都存在模糊的问题。第二个问题是针对图标的每一种颜色,都须要对应提供不一样的图标文件(图中的例子须要有灰色和蓝色两份文件)。相对的SVG图标仅仅须要一份文件。便可在程序中动态设定不一样的颜色了。

这里先给出最终WPF项目中,对SVG图标的引用的代码,而后咱们再进行详细解释。对应的工程名为WpfAppWithFontIcons。github

        <TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Static local:FontIcons.airplane_mode_circ}"   Foreground="Gray"  FontSize="32" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Static local:FontIcons.airplane_mode_circ}"  Foreground="{StaticResource dellBlue}"  FontSize="64" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Static local:FontIcons.bluetooth_inactive}"   Foreground="Orange"  FontSize="96" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Static local:FontIcons.bluetooth_inactive}"    Foreground="Brown"  FontSize="128" ></TextBlock>

代码最大的不一样应该是由<Image/>标签更改成<TextBlock/>标签,这是由于咱们是经过ttf字体文件,曲线救国的方式来使用SVG图标。

具体的步骤以下:

准备SVG图标文件,将这些文件打包成一整个ttf字体文件。打包的方式有不少种,一般我使用的是IcoMoon的免费解决方案。地址以下:

https://icomoon.io/app/#/select

经过这个网站选择SVG图标文件上传,打包生成一个zip文件。解压后文件夹结构以下图:app

ttf文件在fonts文件夹中,实际使用时,须要做为资源文件,添加到WPF工程中。点击图中的demo.html会打开一个本地网页,可用于查找ttf文件中包含的SVG图标,以及对应的unicode。实际咱们是经过对unicode的引用来显示SVG图标的。svg

完整的project结构以下图,Fonts文件夹是手动添加用来放置ttf文件。ttf文件名字都是根据项目须要来取,并不固定。工具

ttf字体文件须要以<FontFamily/>的形式添加到项目的<Resources/>节点中。而后再经过<Style/>指定给<TextBlock/>。固然不在<Resources/>节点定义Style,而是在每一个<TextBlock/>中指定FontFamily属性也是能够的。有关XAML的语法细节,回字的四种写法什么的,这里略过不提。字体

    <Window.Resources>
        <FontFamily x:Key="Fonticon">/Fonts/rcc-fonticon-ribbon-v2.ttf#rcc-fonticon-ribbon-v2</FontFamily>
        <Style TargetType="TextBlock">
            <Setter Property="FontFamily" Value="{StaticResource Fonticon}" ></Setter>
        </Style>
        <SolidColorBrush x:Key="dellBlue">#007DB8</SolidColorBrush>
    </Window.Resources>

这里说明一下“/Fonts/rcc-fonticon-ribbon-v2.ttf#rcc-fonticon-ribbon-v2”值的定义,#前面的是文件路径,#后面的是font name,查看的方法是双击ttf文件,参考下图。网站

在定义好FontFamily以后,咱们并不推荐直接将unicode写到XAML或.cs文件中。由于在XAML中,你须要以下编写:spa

<TextBlock Grid.Row="0" Grid.Column="0" Text="&#xe900;" Foreground="Gray"  FontSize="32" ></TextBlock>

而在C#代码中,又须要如下面这种格式:3d

textBlockAirplane.Text = "\ue900"

两种不统一的格式会在未来修改时带来极大的困难,特别是图标被多处引用时,全局的查找替换根本就是噩梦。此外,毫无心义的unicode值的可读性根本等于0。正常人类没法将"&#xe900;","\ue900"和Airplane的图标联系起来。

我推荐的作法是生成一个FontIcons Class,以string类型属性的形式暴露出来。这样能够得到IDE智能语法提示的支持,更新时也仅需修改这个Class,Find All Reference更是方便无比。同时不管在XAML文件,仍是C#代码中,咱们看到的都是统一的“FontIcons.airplane_mode_circ”。

    public static class FontIcons
    {
        public static string airplane_mode_circ { get; } = "\ue900";
        public static string bluetooth_inactive { get; } = "\ue901";
        public static string brightness { get; } = "\ue902";
        public static string brightness_inactive { get; } = "\ue903";
        public static string browse_inactive { get; } = "\ue904";
        public static string camera { get; } = "\ue905";
    }

那么咱们是否是须要手工来编写FontIcons Class呢?大哥咱们是能把午餐(我不爱喝咖啡)转换成Code的生物啊!固然是写个小工具来自动生成了。在Sample库中,参考IcoMoonReader工程,只需将IcoMoon生成的.svg文件(icomoon.zip解压后的fonts文件夹里)丢在IconMoonReader.exe同级目录,便可生成相应代码。

 其实只有一个方法啦,使用时须要注意具体的文件名是否正确。

            using (var stream = new FileStream("rcc-fonticon-ribbon-v2.svg", FileMode.Open))
            {
                using (var reader = new StreamReader(stream))
                {
                    var pattern = "unicode(\\S)*\\sglyph-name(\\S)*\"";
                    var input = reader.ReadToEnd();
                    foreach (Match match in Regex.Matches(input, pattern))
                    {
                        pattern = "\"\\S*\"";
                        var list = new List<string>();
                        foreach (var result in Regex.Matches(match.Value, pattern))
                        {
                            list.Insert(0, result.ToString());
                        }
                        var name = list[0].Replace("\"", "").Replace("-","_");
                        var code = list[1].Replace("&#x", "\\u").Replace(";", "");
                        Console.WriteLine($"public static string {name} {{ get; }} = {code};");
                    }
                }
            }

把生成的C#字符串定义贴到具体工程的FontIcons Class(名字随意)。

这样一个优秀的解决方案若是仅支持WPF,那又谈何迁移到MS Store呢?实际上这套机制放到UWP工程中也是能够的。虽然UWP能够经过SvgImageSource属性原生支持SVG了,但咱们的这套方案在图标的应用方面绝不逊色,甚至能够说更为方便。具体的例子能够参考AppWithFontIcon工程。在这个UWP的工程中,除了放ttf文件的位置我换到了现成的Assets文件夹,几乎没有改变。

        <TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind local:FontIcons.airplane_mode_circ}"   Foreground="Gray"  FontSize="32" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind local:FontIcons.airplane_mode_circ}"  Foreground="{StaticResource dellBlue}"  FontSize="64" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Bind local:FontIcons.bluetooth_inactive}"   Foreground="Orange"  FontSize="96" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Bind local:FontIcons.bluetooth_inactive}"    Foreground="Brown"  FontSize="{x:Bind DynamicFontSize(),Mode=OneWay,FallbackValue=128}" ></TextBlock>

由于UWP没有了x:static关键字,因此我换成了x:Bind。换成x:Bind以后甚至能够动态的响应值的变化。好比我在这里把FontSize作了一个x:bind到DynamicFontSize()方法,让字体随着界面改变,动态的变大变小。虽然并无什么卵用……可是Demo的时候能够增长点噱头……

本篇到此结束,照例贴上Github地址:

https://github.com/manupstairs/WpfAppForFontIcon感谢耐着性子看到这里的同窗!

相关文章
相关标签/搜索