Using Font Awesome in WPF
Font Awesome is a css library that includes many scalable vector icons. This library is widely used in many web apps.
In this post I will show you how to use Font Awesome in a WPF App.
Creating the Solution
First of all, we create a new WPF app by selecting Project from the File -> New Menu.
Under Installed -> Templates ->Visual C#, we select WPF Application.
For this example, we enter FontAwesomeWPFDemo for the solution Name and we click OK.
Adding Font Awesome
We’ll download Font Awesome from this Link.
Next, we’ll create a new Folder in our WPF Demo Project called fonts.
We’ll add font-awesome.css and fontawesome-webfont.ttf to the fonts folder.
We’ll make sure to change the Build Action for fontawesome-webfont.ttf to Resource.
Parsing the CSS File
In oder to use the Font Awesome Icons in our WPF Demo App. We’ll need to parse the recentely added font-awesome.css File. We’ll use this Gist on GitHub.
We’ll add a T4-Template named CssParser.tt. We’ll implement the template as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Text.RegularExpressions" #> <#+ public class CssParser { private List<string> _styleSheets; private SortedList<string, StyleClass> _scc; public SortedList<string, StyleClass> Styles { get { return this._scc; } set { this._scc = value; } } public CssParser() { _styleSheets = new List<string>(); _scc = new SortedList<string, StyleClass>(); } public void AddStyleSheet(string path) { this._styleSheets.Add(path); ProcessStyleSheet(path); } private void ProcessStyleSheet(string path) { string content = CleanUp(File.ReadAllText(path)); string[] parts = content.Split('}'); foreach (string s in parts) { if (CleanUp(s).IndexOf('{') > -1) { FillStyleClass(s); } } } private void FillStyleClass(string s) { StyleClass sc = null; string[] parts = s.Split('{'); string styleName = CleanUp(parts[0]).Trim().ToLower(); if (_scc.ContainsKey(styleName)) { sc = _scc[styleName]; _scc.Remove(styleName); } else { sc = new StyleClass(); } sc.Name = styleName; string[] atrs = CleanUp(parts[1]).Replace("}", "").Split(';'); foreach (string a in atrs) { if (a.Contains(":")) { string _key = a.Split(':')[0].Trim().ToLower(); if (sc.Attributes.ContainsKey(_key)) { sc.Attributes.Remove(_key); } sc.Attributes.Add(_key, a.Split(':')[1].Trim().ToLower()); } } _scc.Add(sc.Name, sc); } private string CleanUp(string s) { string temp = s; string reg = "(/\\*(.|[\r\n])*?\\*/)|(//.*)"; Regex r = new Regex(reg); temp = r.Replace(temp, ""); temp = temp.Replace("\r", "").Replace("\n", ""); return temp; } private string CleanFontAwesomeKey(string style) { if (string.IsNullOrEmpty(style)) throw new ArgumentNullException("style"); var cleanedFaKey = style.Replace(".fa-", "").Replace("-", "_"); cleanedFaKey = cleanedFaKey.Replace(":before", ""); return UppercaseFirst(cleanedFaKey); } private string CleanFontAwesomeValue(StyleClass style) { // Add \u(unicode) so that xaml parser interprets right the font code. var cleanedFaValue = style.Attributes.Values[0].Replace("\"\\f", "\"\\uf"); return cleanedFaValue; } public Dictionary<string, string> GetFontAwesomeIcons(string path) { AddStyleSheet(path); // select the icons selectors var iconStyles = Styles .Where(s => s.Value.Attributes.ContainsKey("content")).ToList(); var fonts = new Dictionary<string, string>(); foreach (var style in iconStyles) { string fontname = string.Empty; if (style.Key.Contains(",")) { var splits = style.Key.Split(','); foreach (var item in splits) { fontname = CleanFontAwesomeKey(item); fonts.Add(fontname, CleanFontAwesomeValue(style.Value)); } continue; } fontname = CleanFontAwesomeKey(style.Key); fonts.Add(fontname, CleanFontAwesomeValue(style.Value)); } return fonts; } public class StyleClass { private string _name = string.Empty; public string Name { get { return _name; } set { _name = value; } } private SortedList<string, string> _attributes = new SortedList<string, string>(); public SortedList<string, string> Attributes { get { return _attributes; } set { _attributes = value; } } } private string UppercaseFirst(string s) { // Check for empty string. if (string.IsNullOrEmpty(s)) { return string.Empty; } // check if first letter is a number // if true add an underscore at the beginning otherwise c# compiler will complain // that variable name is starting with numbers if (char.IsNumber(s[0])) s = s.Insert(0, "_"); // Return char and concat substring. return char.ToUpper(s[0]) + s.Substring(1); } } #> |
The methods to note are GetFontAwesomeIcons and AddStyleSheet. All they really do is to parse the given css file path and return a dictionary with valid C# variable names and fonts unicode strings;
Next, we’ll add another T4-Template File named FA.tt. We’ll implement the template as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Text.RegularExpressions" #> <#@ include file="CssParser.tt" #> <#@ output extension=".cs" #> namespace FontAwesomeWPFDemo { public static class Fa { <# BuildIcons(); #> } } <#+ private void BuildIcons() { var parser = new CssParser(); // set the fonts path string path = this.Host.ResolvePath(""); Directory.SetCurrentDirectory(path); string fontsPath = path + @"\fonts\font-awesome.css"; // parse the fonts var fonts = parser.GetFontAwesomeIcons(fontsPath); // Create the static fonts fields PushIndent(" "); foreach (var font in fonts) { WriteLine(String.Format("public static string {0} = {1};", font.Key, font.Value)); } } #> |
This Template use the CssParser.tt that we included to generate a static Fa.cs class that contains all icons unicode strings.
Using Font Awesome in WPF App
We’ll implement the MainWindow.xaml as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<Window x:Class="FontAwesomeWPFDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Ui="clr-namespace:FontAwesomeWPFDemo" Title="FontAwesome Demo" Height="339" Width="286"> <Window.Resources> <ResourceDictionary> <Style x:Key="FontAwesomeStyle" TargetType="TextBlock"> <Setter Property="FontFamily" Value="pack://application:,,,/fonts/#FontAwesome" /> </Style> </ResourceDictionary> </Window.Resources> <StackPanel VerticalAlignment="Top"> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10,40,0,0"> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="20" Margin="10" Text="{x:Static Ui:Fa.Male}" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="30" Margin="10" Text="{x:Static Ui:Fa.Female}" Foreground="Red" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="40" Margin="10" Text="{x:Static Ui:Fa.Plug}" Foreground="Green" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="50" Margin="10" Text="{x:Static Ui:Fa.Life_bouy}" Foreground="blue" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="60" Margin="10" Text="{x:Static Ui:Fa.Lightbulb_o}" Foreground="SaddleBrown" VerticalAlignment="Center" /> </StackPanel> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10,40,0,0"> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="20" Margin="10" Text="{x:Static Ui:Fa.File_sound_o}" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="30" Margin="10" Text="{x:Static Ui:Fa.Arrow_circle_down}" Foreground="Red" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="40" Margin="10" Text="{x:Static Ui:Fa.Arrow_circle_o_up}" Foreground="Green" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="50" Margin="10" Text="{x:Static Ui:Fa.File_pdf_o}" Foreground="blue" VerticalAlignment="Center" /> <TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="60" Margin="10" Text="{x:Static Ui:Fa.Google}" Foreground="SaddleBrown" VerticalAlignment="Center" /> </StackPanel> </StackPanel> </Window> |
In Line 10: FontAwesomeStyle style will be applied to all instances of the TextBlock element having the the attribute Style="{StaticResource FontAwesomeStyle}":
1 2 3 |
<TextBlock Style="{StaticResource FontAwesomeStyle}" FontSize="50" Margin="10" Text="{x:Static Ui:Fa.Life_bouy}" Foreground="blue" VerticalAlignment="Center" /> |
I hope that was informative for you!
Feel free to drop some comments if you have questions.
The full example code can be found at: Github