WPF: Use font installed with 'AddFontMemResourceEx' for process only

前端 未结 1 1157
我寻月下人不归
我寻月下人不归 2021-01-03 05:01

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

相关标签:
1条回答
  • 2021-01-03 05:26

    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.

    1. Declare all fonts you want as Resource.
    2. Make a custom MarkupExtension called FontExplorer
    3. Try my XAML Example

    When 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
    }
    

    Preview

    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.

    0 讨论(0)
提交回复
热议问题