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:
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:
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
:
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, ConvertToChar
, 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.
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))
{
Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
{
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");
}
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
{
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:
<Button Margin="50"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<fontAwesomeImageSample:FontSymbolImage Foreground="HotPink"
FontFamily="{StaticResource FontAwesomeTtf}"
FontAwesomeSymbol="fa_building_o"
Rotation="10" />
</Button>
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.
If it is installed in a subdirectory, then you would have something more like this:
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:
<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:
public enum <font face="Consolas" size="2"><font face="Consolas" size="2">FontAwesomeSymbols</font>
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"/>
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:
<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">
Then any use of this FontAwesome control would look like this:
<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.
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
BaseWindow
control used of the Cursor only