In WPF we would like to use ttf
fonts as embedded resources without copying or installing these to the system and without actually writing these to disk. Withou
I don't know if this is exactly what you want, but I got a solution where you can use your fonts as a Resource
in your solution.
fonts
you want as Resource
.MarkupExtension
called FontExplorer
XAML
ExampleWhen the application
starts and the FontExplorer
is used for the first time, it caches all fonts
which you have as resource. After that, everytime you need one of it, the cache is used to give it back.
public class FontExplorer : MarkupExtension
{
// ##############################################################################################################################
// Properties
// ##############################################################################################################################
#region Properties
// ##########################################################################################
// Public Properties
// ##########################################################################################
public string Key { get; set; }
// ##########################################################################################
// Private Properties
// ##########################################################################################
private static readonly Dictionary<string, FontFamily> _CachedFonts = new Dictionary<string, FontFamily>();
#endregion
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
static FontExplorer()
{
foreach (FontFamily fontFamily in Fonts.GetFontFamilies(new Uri("pack://application:,,,/"), "./Fonts/"))
{
_CachedFonts.Add(fontFamily.FamilyNames.First().Value, fontFamily);
}
}
#endregion
// ##############################################################################################################################
// methods
// ##############################################################################################################################
#region methods
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ReadFont();
}
private object ReadFont()
{
if (!string.IsNullOrEmpty(Key))
{
if (_CachedFonts.ContainsKey(Key))
return _CachedFonts[Key];
}
return new FontFamily("Comic Sans MS");
}
#endregion
}
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindow}"
Title="MainWindow" Height="450" Width="800">
<Window.Style>
<Style TargetType="local:MainWindow">
<Setter Property="FontFamily" Value="{local:FontExplorer Key='Candle Mustard'}"/>
<Style.Triggers>
<Trigger Property="Switch" Value="True">
<Setter Property="FontFamily" Value="{local:FontExplorer Key=Roboto}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Style>
<Grid x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0">
<TextBlock Text="Hello World" FontFamily="{local:FontExplorer Key='Candle Mustard'}"/>
<TextBlock Text="Hello World" FontFamily="{local:FontExplorer Key=Roboto}"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
<TextBlock Text="Hello World"/>
</StackPanel>
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="1" x:Name="Panel"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
public bool Switch
{
get => (bool)GetValue(SwitchProperty);
set => SetValue(SwitchProperty, value);
}
/// <summary>
/// The <see cref="Switch"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty SwitchProperty = DependencyProperty.Register("Switch", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
private readonly DispatcherTimer _Timer;
public MainWindow()
{
InitializeComponent();
_Timer = new DispatcherTimer();
_Timer.Interval = TimeSpan.FromMilliseconds(50);
_Timer.Tick += (sender, args) =>
{
Switch = !Switch;
Panel.Children.Add(new TextBlock {Text = "I'm frome code behind"});
if(Panel.Children.Count > 15)
Panel.Children.Clear();
};
_Timer.Start();
}
// ##############################################################################################################################
// PropertyChanged
// ##############################################################################################################################
#region PropertyChanged
/// <summary>
/// The PropertyChanged Eventhandler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise/invoke the propertyChanged event!
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
As you can see in the preview, the memory usage
reduces from 83,2MB to 82,9 MB after the GC
does it's job.