Click here to Skip to main content
15,867,704 members
Articles / Desktop Programming / WPF

Creating an Image from a Font Symbol (Font Awesome) for WPF

Rate me:
Please Sign up or sign in to vote.
4.94/5 (16 votes)
29 Feb 2016CPOL7 min read 35.1K   584   18   10
This article presents a control that makes it very easy to create an Image control from a font symbol, with special support for Font Awesome.

Introduction

I was working on a project where I had to use a lot of icons. I was picking them off the web, but there are always issues with consistency and having the rights to the image—in other words I needed free icons. One of my fellow developers pointed out Font Awsome (http://fortawesome.github.io/Font-Awesome/icons/) to me, and said I should use this font to create my icons. I liked the idea. I eventually got around to looking at incorporating Font Awesome into my project, and it was actually pretty easy. I easily found a class called ImageFromFont (http://www.codeproject.com/Tips/634540/Using-Font-Icons), and it worked well. However, it derived from the MarkupExtension class which does not an DependencyObject so cannot have DependencyProperty’s. I looked at the code and did some thinking, and figured that I could do the same if I created a control that inherited from an Image, and then could have whatever DependencyProperty’s I wanted. In this case I wanted to be able to specify the font color using the Foreground property, and be able to bind it to a StaticResource.

Initially I built around the Font Awesome font, but have I am not happy about their poor receptiveness of suggestions for new symbols--I think some really good suggestions have been rejected that should not have been like adding the planet and astrological symbols, and road symbols. I decided that maybe I should make this Control a little more flexible, and did some renaming to make the association with Font Awesome more specific for the enumeration and DependencyProperty. I then added a new property called Character which is type char, which is what is actually used for generating the glyph. Now the changing of the now named FontAwesomeSymbol only changes the Character DependencyProperty, which in turn forces an update of the image. Now can use any FontFamily and the Character DependencyProperty as the source for the image. If the FontAwesomeSymbol DependencyProperty is used, it is still transparent, and the Font Awesome enumeration values are still available. There are currently over 300 of the enumeations provided in the renamed FontAwesomeSymbols enumeration.

The Code

I figured there were three important properties that this code needed: the FontFamily, the Foreground Brush, Character, and the FontAwesomeSymbol to display. I later added a Rotatation DependencyProperty that allows the image to be rotated by the specified number of degrees. I implemented all these as DependencyProperty's so that the value could be dynamic:

C#
  public FontFamily FontFamily
  {
   get { return (FontFamily)GetValue(FontFamilyProperty); }
   set { SetValue(FontFamilyProperty, value); }
  }

  public static readonly DependencyProperty FontFamilyProperty =
   DependencyProperty.Register("FontFamily", typeof(FontFamily), typeof(FontSymbolImage),
    new PropertyMetadata(null, UpdateImage));

  public FontAwesomeSymbols FontAwesomeSymbol
  {
   get { return (FontAwesomeSymbols)GetValue(FontAwesomeSymbolProperty); }
   set { SetValue(FontAwesomeSymbolProperty, value); }
  }

  public static readonly DependencyProperty FontAwesomeSymbolProperty =
   DependencyProperty.Register("FontAwesomeSymbol", typeof(FontAwesomeSymbols), typeof(FontSymbolImage),
    new PropertyMetadata(FontAwesomeSymbols.fa_smile_o, UpdateFontAwesome));

  public char Character
  {
   get { return (char)GetValue(CharacterProperty); }
   set { SetValue(CharacterProperty, value); }
  }

  public static readonly DependencyProperty CharacterProperty =
   DependencyProperty.Register("Character", typeof(char), typeof(FontSymbolImage),
    new PropertyMetadata(' ', UpdateImage));

  public double Rotation
  {
   get { return (double)GetValue(RotationProperty); }
   set { SetValue(RotationProperty, value); }
  }

  public static readonly DependencyProperty RotationProperty =
   DependencyProperty.Register("Rotation", typeof(double), typeof(FontSymbolImage),
    new PropertyMetadata(0.0, UpdateImage));

  public Brush Foreground
  {
   get { return (Brush)GetValue(ForegroundProperty); }
   set { SetValue(ForegroundProperty, value); }
  }

  public static readonly DependencyProperty ForegroundProperty =
   DependencyProperty.Register("Foreground", typeof(Brush), typeof(FontSymbolImage),
    new PropertyMetadata(new SolidColorBrush(Colors.Black), UpdateImage));

  public FlipValues Flip
  {
   get { return (FlipValues)GetValue(FlipProperty); }
   set { SetValue(FlipProperty, value); }
  }

  public static readonly DependencyProperty FlipProperty =
   DependencyProperty.Register("Flip", typeof(FlipValues), typeof(FontSymbolImage),
    new PropertyMetadata(FlipValues.None, UpdateFontAwesome));

The FontAwesomeSymbol property is an enum so that it is easier to set its value rather so that rather than having to always look up the number associated with a Font Awesome symbol, could use the name of the enumeration to help determine the symbol. This all makes the XAML a lot easier since the name of the symbol can easily be associated with the image.

Each of these DependencyProperty's used that same method when their value is changed:

C#
  private static void UpdateImage(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
   var imageControl = (FontSymbolImage)d;
   var glyphRunDrawing = CreateGlyph(imageControl.Character.ToString(), imageControl.FontFamily,
    FontStyles.Normal, FontWeights.Normal, FontStretches.Normal, imageControl.Foreground);
   if (glyphRunDrawing == null) return;
   if (Math.Abs(imageControl.Rotation) < .1 && imageControl.Flip == FlipValues.None)
   {
    imageControl.Source = new DrawingImage(glyphRunDrawing);
   }
   else
   {
    var drawingGroup = new DrawingGroup();
    drawingGroup.Children.Add(glyphRunDrawing);
    TransformImage(drawingGroup, imageControl.Rotation, imageControl.Flip);
    imageControl.Source = new DrawingImage(drawingGroup);
   }
  }

The code in bold was added with the Rotation DependencyProperty and the Flip DependencyProperty capability was added to the project.

The FontAwesomeSymbol DependencyProperty actually calls another method, UpdateFontAwesome, which converts the enumeration into the appropriate character, and then updates the Character DependencyProperty:

C#
  private static void UpdateFontAwesome(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    var imageControl = (FontSymbolImage)d;
    var index = ConvertToChar((FontAwesomeSymbols)e.NewValue);
    imageControl.Character = index;
  }

The changing Character DependencyProperty then causes the UpdateImage event handler to be called.

Basically this method calls two static methods and then sets the Image's Source. One method, <font face="Consolas" size="2"><font face="Consolas" size="2">ConvertToChar</font></font>, takes the Symbol enumeration and converts it into the id for the character in the Font Awesome font. The other converts this symbol into an instance of the Drawing class which then can be converter into a DrawingImage instance required by the Source property.

C#
  private static Drawing CreateGlyph(string text, FontFamily fontFamily, FontStyle fontStyle,
     FontWeight fontWeight, FontStretch fontStretch, System.Windows.Media.Brush foreBrush)
  {
   if (fontFamily != null && !string.IsNullOrEmpty(text))
   {
    //first test in charge the police directly
    Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);

    GlyphTypeface glyphTypeface;
    if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
    {
     //if it does not work (for design and fashion in some cases) is added the uri pack://application
     typeface = new Typeface(new FontFamily(new Uri("pack://application:,,,"),
       fontFamily.Source), fontStyle, fontWeight, fontStretch);
     if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
      throw new InvalidOperationException("No glyphtypeface found");
    }

    //determination of the indices / sizes of the characters in the font
    var glyphs = new ushort[text.Length];
    var advanceWidths = new double[text.Length];

    for (var n = 0; n < text.Length; n++)
    {
     var glyph = glyphs[n] = GetGlyph(text[n], glyphTypeface);
     advanceWidths[n] = glyphTypeface.AdvanceWidths[glyph];
    }

    try
    {
     //creation Drawing Image object (compatible with ImageSource) from a GlyphRun
     var glyphRun = new GlyphRun(glyphTypeface, 0, false, 1.0, glyphs,
         new System.Windows.Point(0, 0), advanceWidths, null, null, null, null, null, null);
     var glyphRunDrawing = new GlyphRunDrawing(foreBrush, glyphRun);
     return glyphRunDrawing;
    }
    catch (Exception ex)
    {
     Debug.WriteLine("Error in generating Glyph Run: " + ex.Message);
    }
   }
   return null;
  }

  private static char ConvertToChar(FontAwesomeSymbols symbolEnum) => (char)(int)symbolEnum;

This method is pretty much verbatim from the ImageFromFont class above, but I did make a number of changes including returning a Drawing instead of a DrawingSource. I did this because I want to eventually enhance this project, and figured Drawing was a more flexible class.

Using the code

It is pretty straight forward using the code outside of getting the reference to the Font Awesome true type font:

XML
  <Button Margin="50"
          HorizontalAlignment="Center"
          VerticalAlignment="Center">
   <fontAwesomeImageSample:FontSymbolImage Foreground="HotPink"
                                       FontFamily="{StaticResource FontAwesomeTtf}"
                                       FontAwesomeSymbol="fa_building_o"
                                       Rotation="10" />
  </Button>

 

Image 1

The Foreground property should be very obvious. The FontFamily less so unless you often deal with custom fonts that have to be included in the project. In this case I have added the font to the root of the project since did not want to have to depend on the font being installed on the system I was running. This font will probably not be installed in your root, the path to the location will be required. The image below shows what my solution looks like. You can see the fontawesome.ttf file is in the root of the solution.

Image 2

If it is installed in a subdirectory, then you would have something more like this:

XML
FontFamily="/HolosDisplay;component/Assets/Fonts/#fontawesome"

The last entry in the XAML, FontAwesomeSymbol, is the name you will find on the site, but with a suttle change. On the site, if you go to the detail for a symbol, you will see somthing like this associated with each symbol: fa-plus-circle. In HTML it would be used like this:

HTML
<i class="fa fa-plus-circle"></i>

I had to use something in place of the dashes because C# does not allow dashes in the name so I used the underscore. What I did was create an enum, and used a name for each element of the enum that was close to that used on the site:

C#
public enum <font face="Consolas" size="2"><font face="Consolas" size="2">FontAwesomeSymbols</font></font>
{
 fa_user = 0xf007,
 fa_eject = 0xf052,
 fa_arrow_circle_left = 0xf0A8,
 fa_arrow_circle_right = 0xf0A9,
 .
 .
 .
 fa_floppy_o = 0xf0C7,
 fa_rotate_left = 0xf0E2,
 fa_file_text_o = 0xf0F6,
 fa_smile_o = 0xf118,
 fa_user_plus = 0xf234,
}

I only have a small subset of the available symbols defined in this enum, the ones I needed for the project. The numeric value of the enum is a Unicode associated with the symbol. This made it very easy to convert the enumeration to the Unicode value to be used when generating the symbol. This simple method does the trick:

  private static char Convert(FontAwesomeSymbols symbolEnum)
  {
   var symbolInteger = (int)symbolEnum;
   return (char)symbolInteger;
  }

Rotation

As an enhancement I included the capability to rotate the image. The following XAML will add a 10 degree rotation to the image:

<fontAwesomeImageSample:<span> </span><span> </span>FontSymbolImage <span> </span><span> </span>Foreground="HotPink"
                                        FontFamily="{StaticResource FontAwesome}"
                                        FontAwesomeSymbol="fa_building_o"
                                        Rotation="10"/>

Image 3

Suggestion

To make it a little easier to refer to the Font Awesome font, I would recommend that the FontFamily should be set in a central location, and then referred to as a StaticResource. If it is the App.xaml file, the file would contain the following:

XML
<Application x:Class="FontAwesomeImageSample.App"
             xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation</a>"
             xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</a>"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
  <FontFamily x:Key="FontAwesome">#FontAwesome</FontFamily>
    </Application.Resources>
</Application>

Then any use of this FontAwesome control would look like this:

XML
   <fontAwesomeImageSample:FontAwesome Foreground="HotPink"
                                       FontFamily="{StaticResource FontAwesome}"
                                       Symbol="fa_arrow_circle_right" />

This is particularly nice when the font is not in the root directory, but buried in some asset directory, or in another project.

Need Help

There are a lot of things that can be done using the web support of Font Awesome, and I could probably add the support without too much difficulty if it was of interest. One of the things I would really like to do if be able to add a symbol to slightly modify images. In particular I would like to be able to add a plus sign. Font Awesome has a few symbols that have alterations for things like new and delete, but only a few, and this is probably the area I would like to have specialized images since my images are quite large because it is designed for touch. The problem is that I need to be able to do something like the OpacityMask and then add the plus symbol (may want a delete in the future) so that the symbols do not run together. Have investigated it, but nothing is immediately apparent. Pointing me in the right direction would help.

Image 4

Extra

There is code included in the project for using a font symbol as a cursor. This code is in the FontSymbolCusror.cs file. This code is initialized in the constructor for the MainWindow. This is documented in http://www.codeproject.com/Articles/1087610/Creating-a-Cursor-from-a-Font-Symbol.

History

  • 02/29/2016: initial version
  • 03/04/2016: Updated solution with more characters
  • 03/11/2016: Updated solution with rotate and more flexibility in selecting character to render.
  • 03/26/2016: Editing, class renaming and more symbols
  • 04/27/2016: Update to Font Awesome 4.6
  • 05/17/2016: Updated code with improved wait cursor implementation
  • 05/20/2016: Updated code with new <font face="Courier New">BaseWindow</font> control used of the Cursor only

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
BugProblems with running the demo Pin
ifko5-Feb-19 20:52
ifko5-Feb-19 20:52 
PraiseThanks for another good control Pin
CapGroupAccess29-Nov-18 6:00
CapGroupAccess29-Nov-18 6:00 
GeneralMy vote of 5 Pin
DrABELL16-Jul-17 17:42
DrABELL16-Jul-17 17:42 
QuestionNo Source Pin
Jeff Bowman7-Mar-16 16:04
professionalJeff Bowman7-Mar-16 16:04 
AnswerRe: No Source Pin
Clifford Nelson8-Mar-16 4:27
Clifford Nelson8-Mar-16 4:27 
GeneralRe: No Source Pin
Jeff Bowman8-Mar-16 11:37
professionalJeff Bowman8-Mar-16 11:37 
AnswerRe: No Source Pin
Clifford Nelson8-Mar-16 13:09
Clifford Nelson8-Mar-16 13:09 
GeneralRe: No Source Pin
Jeff Bowman8-Mar-16 13:17
professionalJeff Bowman8-Mar-16 13:17 
GeneralMy vote of 5 Pin
clawton29-Feb-16 13:39
clawton29-Feb-16 13:39 
Useful! Smile | :)
AnswerRe: My vote of 5 Pin
Clifford Nelson29-Feb-16 15:22
Clifford Nelson29-Feb-16 15:22 

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.