Microsoft has provided some very good documentation on how to package fonts with WPF applications, and I’ve been following it in several different ways in different apps I’ve written. Recently I provided a feature in Posh Console which allows you to load the startup banner from an external file: StartupBanner.xaml.

Loading the banner from an external XAML file means that users can configure their own startup banner and make it look however they like, but it broke our default banner. The startup banner we’ve been using has a logo and some text which are all defined in pure XAML and uses several fonts which we had embedded in a FontLibrary.dll resource assembly. It worked great as long as the XAML was defined in the application, but as soon as we removed it to an external file and loaded it in using the XamlReader.Load method, the fonts all went to the fallback default fonts instead of our embedded ones.

Normally this might not be a big deal, but since our logo is based on the “Q” in a Quake-like font … it looks really lame without fonts. :D Apparently, when you load external XAML, it can’t use the Font Resource Library resources. At this point, I can’t seem to find anything to indicate why this is the case, but I suspect it has to do with the fact that the external, loaded XAML only has partial trust, and since the font is in an external assembly instead of embedded in the partial trust content, it can’t get to it. However, it might be simpler than that: it might just be that the base URI for the externally loaded XAML is an actual file location rather than one of the pack://,,,application/ type URIs, so the slightly odd font path doesn’t resolve.

In any case, I found a way around it, which is of course, why I’m writing all of this down. :) If you declare WPF resources in your project, you can inject them into the WPF objects loaded from the XAML to make them available to them. So for instance, I have a font called “Quake” in the FontLibrary assembly. I can declare it as a resource in my application’s XAML (at the app level, window level, or wherever).

<Style x:Key="POSHFont">
  <Setter Property="TextElement.FontFamily" Value="/FontLibrary;Component/#QUAKE" />
</Style>

The font in that style gets resolved in full trust with the pack URI, and it works fine when used from my application. Having created that, I can do the same thing in the external XAML files, and apply the style to the text as a DynamicResource … and even feel free to override parts of the style if I want to. For instance, even though the style might specify a FontSize, if I refer to the style on this text block and specify my own FontSize, the one that’s on the TextBlock (or Paragraph, or Run, or whatever) will be used. Now, we’re not quite done yet, because although they look the same in source, the fonts won’t resolve properly in the externally loaded XAML.

<TextBlock TextAlignment="Center" Width="180" Style="{DynamicResource POSHFont}" FontSize="145" Foreground="#FF3275B9">
  <TextBlock.BitmapEffect>
    <BevelBitmapEffect BevelWidth="1" EdgeProfile="CurvedOut" LightAngle="258" Relief="0.03" Smoothness="0.565"/>
  </TextBlock.BitmapEffect>Q
 </TextBlock>

However, because the style is specified as a dynamic resource rather than a static one, I don’t have to define that resource at all in that XAML file, but even if I do, when I load the XAML into my application, I can simply set it’s resources from the resources that I’ve loaded in full trust and with the pack:// base URI... In my case, I have actually defined several fonts as resources on a Document element, so I just copy them all into the element I read in from the XAML file:

Paragraph banner = (Paragraph)XamlReader.Load(System.Xml.XmlReader.Create("StartupBanner.xaml"));
if (banner != null)
{
  // Copy over resources from my DOCUMENT to the BANNER
  foreach (string key in Document.Resources.Keys) {
      banner.Resources[key] = Document.Resources[key];
  }
  Document.Blocks.Add(_currentParagraph);
}

And that’s it: PoshConsole Banner

4 Responses to “XamlReader and Resource Library fonts in WPF”

  • Rob Relyea says:

    Have you posted a question about the font problem to the wpf forum (http://robrelyea.com/wpf/forum)?
    XamlReader runs under partial trust when running in an XBAP, but not when running in a normal application.

    Thanks, Rob

    Rob Relyea | Program Manager, WPF & Xaml Language Team
    robrelyea.com | /blog | /wpf | /xaml

  • Well, good. That means it’s a resource path problem then, which is what I was starting to lean toward anyway, after playing with it more this morning … I guess I’d have to call that a bug then.

  • pbabineau says:

    I have externalized xaml similar to your StartupBanner.xaml but need it to contain binding to data somehow – I have not been able to get this to work – wondered if you might have in your travels

  • I have externalized xaml similar to your StartupBanner.xaml but need it to contain binding to data somehow – I have not been able to get this to work – wondered if you might have in your travels.

    I think that the same method I described should basically work – I mean, I’m basically binding the style to a resource, it’s actually in the xaml as a DynamicResource. As long as you can make your data binding work with a DynamicResource, you should be able to use a simple modification of this? Do you want to give me a more concrete example?