Introduction
This tip gives a solution to use font icons in most of WPF controls (like Ribbon
).
Background
I recently worked on using a ribbon in a WPF application. The problem is that the ribbon only permits to use
ImageSource
to display icons.
My aim is to use a font icon library (like in HTML) because it contains vector icons and the final application is homogeneous.
In the sample, you will find the Font:
Using the Code
- Create a new WPF Project (can be MVVM).
- Create a ribbon (Add reference to
System.Windows.Controls.Ribbon
).
- Embed a font that will contain scalable icons:
- From your Resource.resx, add the ttf file
- In solution explorer, find the TTF file and change the "Build action" to "Resource"
- Add reference to
WpfTools
(in download package) - Use font icons as ImageSource
To display an icon from a font:
<RibbonButton Label="Import data"
LargeImageSource="{WpfTools:ImageFromFont Text=,
FontFamily=/RibbonFontIconSample;component/Resources/#FontAwesome, Brush=Brown}" />
Use the following parameters:
Text
: The character representing the icon (depending on the font. For FontAwesome
, I used
the CheatSheet http://fortawesome.github.io/Font-Awesome/cheatsheet/ to get code.
FontFamily
to choose the embedded font (use the syntax below) Brush
used to colorize icon (can be a Color
or a static
resource)
Style
for italic Weight
for bold
Points of Interest
This class can be used with any controls which have an ImageSource
property (Ribbon
, Image
, ...).
It is a way to have high definition icons in application because they are scalable vector icons.
How It Works
As you cannot override ImageSource
one of the solutions is to create a MarkupExtension
.
A markup extension is a class that can be used in
XAML code inside { }
and as only one pure abstract
method ProvideValue
which is called to get the
resulting object.
public override object ProvideValue(IServiceProvider serviceProvider)
{
return CreateGlyph(Text, FontFamily, Style, Weight, Stretch, Brush);
}
In WPF, one way to render a string
as a drawable object is to use Glyph
(and GlyphRun
).
A glyph
is a way to render one or more characters in a scalar way. It uses a Font and character index to draw one letter or icon.
Here is the code that draws a text into ImageSource
:
private static ImageSource CreateGlyph(string text,
FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight,
FontStretch fontStretch, Brush foreBrush)
{
if (fontFamily != null && !String.IsNullOrEmpty(text))
{
Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
GlyphTypeface glyphTypeface;
if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
throw new InvalidOperationException("No glyphtypeface found");
ushort[] glyphIndexes = new ushort[text.Length];
double[] advanceWidths = new double[text.Length];
for (int n = 0; n < text.Length; n++)
{
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];
glyphIndexes[n] = glyphIndex;
double width = glyphTypeface.AdvanceWidths[glyphIndex] * 1.0;
advanceWidths[n] = width;
}
GlyphRun gr = new GlyphRun(glyphTypeface, 0, false, 1.0, glyphIndexes,
new Point(0, 0), advanceWidths,
null, null, null, null, null, null);
GlyphRunDrawing glyphRunDrawing = new GlyphRunDrawing(foreBrush, gr);
return new DrawingImage(glyphRunDrawing);
}
return null;
}
Limitations
All the properties (Text
, FontFamily
, Brush
)
are not bindable (due to MarkupExtension
which is not a DependencyObject
).
The size is not directly managed, because the glyph
is rendered to stretch in the container object.
You have to put the class in a separate assembly (like WpfTool
) to make it work in design mode (it is a known bug of Visual Studio).
History