プログラミング

【WPF】Material DesignでButtonやData Gridのフォントサイズを個別に変更する方法

こんにちは、くろがきです。

最近、WPFでWindowsアプリ開発がマイブームです。

WPFではXAMLでアプリUIを記述していくのですが、Material Designを使うことで簡単におしゃれなUIを作成できます。

Material Designは便利なのですが、いざ自分の環境に適用してみると、ボタン等のフォントサイズがイマイチだったりします。

そこでMaterial Designのスタイルを上書きしてフォントサイズを変えようとしたのですが、ちょっとしたことでかなりハマってしまいました

ということで、今回ハマってしまった内容をまとめておきたいと思います。

同じように悩んでいる方の参考になれば幸いです。

1.Material Designのスタイルを上書きする

Material Designのスタイルを上書きするにはいくつか方法がありますが、私はApp.xamlにコードを記載しています。

例えば、MaterialDesignFlatLightBgButtonというButton用スタイルのフォントサイズを変更したい場合は、下記のように記載します。

<Application x:Class="******"... >
    <Application.Resources>
        <ResourceDictionary>

            <Style BasedOn="{StaticResource MaterialDesignFlatLightBgButton}" TargetType="{x:Type Button}">
                <Setter Property="TextBlock.FontSize" Value="14" />
            </Style>      

        </ResourceDictionary>
    </Application.Resources>
</Application>

SetterのPropertyは、TextBlock.FontSizeもしくはFontSizeのどちらでもOKです。

またDataGridの場合は、下記のように記載します。

<Application x:Class="******"... >
    <Application.Resources>
        <ResourceDictionary>

            <Style BasedOn="{StaticResource MaterialDesignFlatSecondaryButton}" TargetType="{x:Type DataGridColumnHeader}">
                <Setter Property="TextBlock.FontSize" Value="14" />
            </Style>

            <Style BasedOn="{StaticResource MaterialDesignDataGrid}" TargetType="{x:Type DataGrid}">
                <Setter Property="TextBlock.FontSize" Value="14"/>
            </Style>

        </ResourceDictionary>
    </Application.Resources>
</Application>

DataGridはカラムヘッダーとデータ部分で、別々にスタイルを設定する必要があります。

ちなみに、BasedOnのStaticResourceとして選択できるスタイルは、TargetTypeで設定したx:Typeの種類によって変わります。

BasedOnは、TargetTypeの値に応じた選択肢から補完できます。

フォントサイズ以外にも様々な項目を上書きすることができますが、その際のコードの書き方はMaterial Designのソースコードを参考にしました。

2.TextBlockには注意が必要

Material Designのスタイルを上書きしてフォントサイズを変更するには、上記のようにすればいいわけですが、私の場合、上記の方法で試してもうまくフォントサイズが適用されませんでした。

おかしいなぁ、と思って色々コードを触っているうちに、ようやく答えがわかりました。

それがTextBlockのスタイル設定です。

私はTextBlockの文字サイズを上書きしたくて、下記のようなコードをApp.xamlに記載していたのですが、これが悪さをしていたようです。

<Style BasedOn="{StaticResource MaterialDesignTextBlock}" TargetType="{x:Type TextBlock}">
    <Setter Property="FontSize" Value="20" />
</Style>

MaterialDesignTextBlockのフォントサイズを変更すると、TextBlockだけでなく、ButtonやDataGridのフォントサイズまで変わります。

しかも、その後でButtonやDataGridのフォントサイズを設定し直しても上書きされず、TextBlockのフォントサイズ設定が優先されていました。

この部分を消去してやると、問題なくButtonやDataGridのフォントサイズが変更できました。

3.TextBlockのスタイル変更はどうすればいい?

2で説明したように、MaterialDesignTextBlockでフォントサイズを変更すると、ButtonやDataGridのフォントサイズも連動してしまいます。

そのため、ButtonやDataGridのフォントサイズを個別に設定するためにはMaterialDesignTextBlockのスタイルに関する記述を消すしかありませんでした。

とはいえ、MaterialDesignTextBlockでフォントサイズを上書きする方法がないわけではありません。

スタイル設定をMainView.xamlに記述してやれば、問題なくTextBlockのフォントサイズを変更できました。

<Window.Resources>
    <Style TargetType="TextBlock" BasedOn="{StaticResource MaterialDesignTextBlock}">
        <Setter Property="FontSize" Value="20"/>
    </Style>
</Window.Resources>

おそらくスタイルが適用される順番の問題だと思うのですが、MainView.xamlにMaterialDesignTextBlockのフォントサイズ変更を記載した場合には、特に何の問題も生じませんでした。

やり方が分かってしまえばなんてことないのですが、しばらくハマってしまいました。

4.なぜTextBlockの設定が優先されるのか

なぜTextBlockの設定が優先されるのかについて調べていると、Stack Overflowにそれらしい内容がありました。

ここを読む限り、Buttonのテキスト表示にはTextBlockを使っているようです。

You’re right about

My guess is that the buttons create a TextBlock when their content is set to a string and then use the textblock style

「WPF Change fontSize of button with style fails?」 stackoverflow.com

そのため、MaterialDesignTextBlockの上書きした場合、Buttonに内包されるTextBlockのフォントサイズにまで作用してしまうのではないかと思います。

さらに読み進めていくと、

TextBlock設定をButton要素等に反映させたくなければ、System.StringのDataTemplateを定義しなさい

とあります。

. See this post.

A workaround is to define a DataTemplate for System.String, where we can explicitly use a default TextBlock to display the content. You can place that DataTemplate in the same dictionary you define the TextBlock style so that this DataTemplate will be applied to whatever ContentPresenter effected by your style.

「WPF Change fontSize of button with style fails?」 stackoverflow.com

実際にButton要素に、System.StringのDataTemplateを定義してやります。

要素に直接記述する場合は、下記のように書けばいいみたいです。

<Window ...
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
...>

<Button
    Content="Push"
    Style="{DynamicResource md-style}">
    <Button.ContentTemplate>
         <DataTemplate DataType="{x:Type sys:String}">
             <TextBlock Text="{Binding}">
                 <TextBlock.Resources>
                     <Style TargetType="{x:Type TextBlock}" />
                 </TextBlock.Resources>
             </TextBlock>
        </DataTemplate>
    </Button.ContentTemplate>
</Button>

ここではButton要素のスタイルとして、App.xamlに記載した「md-sytle」を設定し、そのあとDataTemplateを定義しています。

こうすることで、「md-sytle」のフォントサイズ設定を反映させることができます。

DataTemplateを定義する方法は、下記のサイトを参考にさせていただきました。

Button要素全体のスタイルとして設定したい場合は、下記のようにApp.xamlに記載すれば大丈夫でした。

<Application ...
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
...>
    <Application.Resources>
        <ResourceDictionary>

            <Style x:Key="md-style" BasedOn="{StaticResource MaterialDesignFlatLightBgButton}" TargetType="{x:Type Button}">
                <Setter Property="TextBlock.FontSize" Value="14"/>
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate DataType="{x:Type sys:String}">
                            <TextBlock Text="{Binding}">
                                <TextBlock.Resources>
                                    <Style TargetType="{x:Type TextBlock}" />
                                </TextBlock.Resources>
                            </TextBlock>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

        </ResourceDictionary>
    </Application.Resources>
</Application>

Application.Resources内でDataTemplateを設定する方法は、こちらのサイトを参考にさせていただきました。

色々と勉強になりました。ありがとうございました。

ちなみに、ここではButton要素に限定していますが、DataGridでも同様でした。

まとめ

今回の記事では、Material DesignのButtonやDataGridのフォントサイズを上書きする方法を説明しました。

フォントサイズを変更するための記述自体はそれほど難しくないのですが、TextBlockのフォントサイズ設定と競合してしまい、思ったとおりにフォントサイズの変更ができませんでした。

いったんは、App.xamlで悪さをしていたMaterialDesignTextBlockのスタイル設定をMainView.xamlに記述することで、この競合問題が解決しました。

さらに、System.StringのDataTemplateを定義することで要素別にフォントサイズを設定できることもわかりました。

C#もxamlもまだまだわからないことがいっぱいですが、少しずつ勉強していきたいと思います。

この記事が皆さんの参考になれば幸いです。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です