読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

WPFでフォントの列挙を行う方法

WPFにはフォント選択などのダイアログがありません。
で、フォント選択ダイアログを作るときなどに必要になる、WPFでのフォント一覧の列挙方法を調べてみました。

結果はこんな感じ
f:id:minami_SC:20140425011945p:plain

フォント一覧の列挙

まずは、フォントの一覧の表示からやってみます。
システムにインストールされているフォント一覧は、以下のように1行で取得することができます。

var fontList = Fonts.SystemFontFamilies;

フォント一覧をListBoxに表示するだけのサンプル

f:id:minami_SC:20140425012113p:plain

MainWindow.xaml
<Window x:Class="WpfBaseTemplate1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <ListBox Width="200"
                 Margin="10"
                 HorizontalAlignment="Left"
                 ItemsSource="{Binding FontList}"/>

    </Grid>
</Window>
MainWindow.xaml.cs
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new MainWindowViewModel();
        }
    }
MainWindowViewModel.cs
    class MainWindowViewModel
    {
        public IEnumerable<FontFamily> FontList { get; set; }

        public MainWindowViewModel()
        {
            this.FontList = Fonts.SystemFontFamilies;
        }
    }

画面表示時にフォントを適用した文字列で表示

f:id:minami_SC:20140425012128p:plain
フォント名をstringで表示しただけではどんなフォントかわかりにくいので、文字列にフォントを適用して表示してみました。
以下のようなDataTemplateを適用すると、フォントを適用したTextBlockで表示できます。

MainWindow.xaml
<Window x:Class="WpfBaseTemplate1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <ListBox Width="200"
                 Margin="10"
                 HorizontalAlignment="Left"
                 ItemsSource="{Binding FontList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" FontFamily="{Binding}" FontSize="16"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

フォント名をローカライズして表示する

フォントを適用してみても、以下のように日本語のフォント名とかを見てみると、アルファベット表記で表示されてることに気付きます。
f:id:minami_SC:20140425012146p:plain
日本語のフォントだったら、是非とも日本語で表示したい!!
ということで、コンバータを使って、実行時の言語環境に応じたローカライズをしてみました。
実行時の環境に合ったフォント名称があれば、その名称を表示し、なければFontFamilyのSourceプロパティを表示しています。

この手のコンバータのCultureInfo型のプロパティを使ってローカライズする時は、以下の1文を実行してプログラムの言語設定をしておく必要があります。

this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);

以下のサンプルでは、これをMainWindowのコンストラクタで実行してます。

MainWindow.xaml
<Window x:Class="WpfBaseTemplate1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfBaseTemplate1"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Window.Resources>
        <local:FontFamilyToNameConverter x:Key="FontFamilyToNameConverter" />
    </Window.Resources>
    <Grid>
        <ListBox Width="200"
                 Margin="10"
                 HorizontalAlignment="Left"
                 ItemsSource="{Binding FontList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock FontFamily="{Binding}"
                               FontSize="16"
                               Text="{Binding Converter={StaticResource FontFamilyToNameConverter},
                                              Mode=OneWay}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>
MainWindow.xaml.cs
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);
            this.DataContext = new MainWindowViewModel();
        }
    }

    public class FontFamilyToNameConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var v = value as FontFamily;
            var currentLang = XmlLanguage.GetLanguage(culture.IetfLanguageTag);
            return v.FamilyNames.FirstOrDefault(o => o.Key == currentLang).Value ?? v.Source;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

結果はこんな感じ。
f:id:minami_SC:20140425012200p:plain
とりあえず、表示するだけなら割と簡単にできそう。