WPF のリソース
◆ デフォルトのスタイルは Application.Resources にまとめる
◆ ResourceDictionary に書いておいてまとめることができる
◆ ResourceDictionary に書いておいてまとめることができる
WPF のリソースは FrameworkElement のプロパティと Application のプロパティに設定できます
こういう感じ
リソースには好きなオブジェクトを置いておけます
よく使う色やアイコン画像や Converter などいろいろです
基本的に XAML のプロパティ設定のときに StaticResource で指定するのが使いみちです
x:Key は省略できて 省略すればそのリソースを持つコントロールの子孫要素全てに対して TargetType で設定したスタイルが適用されます
上のコードは Window がもっているリソースに Button の背景色が #efe0fe とあるので この Window の中にあるすべての Button は #efe0fe 色が背景になります
優先順はその要素から徐々に上の階層をみていきます
こういうのだと TextBlock のリソースが最優先で 次に CheckBox → StackPanel → Grid → Window の順になります
Window.Resources に Style を設定しておけば その Window の中の全体が対象ですが ひとつの Window だけじゃなくてアプリケーション全体を対象にしたいときは App.xaml にある Applcation.Resources に設定します
Window.Resources に TargetType が Button の Style をつくれば その Window の中の Button に対するスタイルです
その Style にも Style.Resources を指定することができます
Style.Resources に TargetType が Label の Style を作れば Window のなかの Button のなかの Label が対象です
CSS でいう

1 つめは Label の中なので赤色
2 つめは TextBlock の中なので青色
3 つめは Button のスタイルで紫色です
Button の VisualTree を見ると 文字の表示には TextBlock が使われているので 2 つめと 3 つめは同じツリー構造です
なのに 3 つめには TextBlock のスタイルが適用されていません
実際に XAML に書いた LogicalTree でスタイルが当たってるようです
Button を継承して拡張した Buttton クラスを作ったとします
その XAML でデフォルトのスタイルをこのように指定します
このコントロールを使う Window でリソースを設定すると実行時にエラーが起きます
local:Buttton.Resources の Style は Buttton.xaml のほうにあるのでここで設定すると 2 つあることになってしまいます
直接 local:Buttton の Resources でなく Window に設定すれば 2 つあることは回避できます
ですが こうしても local:Buttton につけられている Resources のほうが優先されるので 背景が Orange になるだけで Window.Resources で設定した Style は無視されてしまいます
local:Buttton.Resources でなく local:Buttton.Style を設定することもできます
Resources より Style が優先なので このコードでは 文字色が青色になりますが背景は Orange にならずデフォルトのボタン色です
背景を引き継ぎたいなら BasedOn を Style に設定します
これだと Style だけの設定なので Resources にあれこれ含めたい場合は .Resources もかかないといけません
長くなりますし Style だけ別なのもなんかイヤです
できればデフォルトのスタイルを そのクラス定義ファイルに書いておきたいのですが 要素の Resources プロパティでなく Application.Resources に定義しておくのが一般的みたいです
Buttton.xaml ファイルはなくしてしまって App.xaml にこう書きます
Buttton の Resources プロパティには何も入っていないので Window の方ではこうかけます
この場合も BasedOn がないと適用されるスタイルはここに書いた青色文字だけになって 背景色がなくなりますので注意です
Style や Resources を使わずに直接プロパティを指定した場合は 適用されたスタイルにプロパティ設定したものを上書きするように両方が適用されます
ですが ひとつひとつに設定していかないといけないのは不便ですよね
分けて書きたい ってときには ResourceDictionary が使えます
よくあるファイル分割です
パーツとなる XAML のルートは ResourceDictionary にします
ロードする側は .Resources に ResourceDictionary を Source プロパティ付きでいれます
複数をまとめたいなら ResourceDictionary の MergedDictionaries プロパティに ResourceDictionary をいれます
Source プロパティ付きの ResourceDictionary は子要素にリソースをもつことができないです
Source とその場で書いたリソースをまとめたいなら MergedDictionaries で Source 付きと直接リソース定義したものをマージします
MergedDictionaries プロパティがあるときも Source プロパティがあるのと一緒で こういう直接リソースかくことはできません
それと .Resources の中に ResourceDictionary があるときに 直接他のリソース定義もできません
「liblib」 がアセンブリ名です
「src/wpf/resource.xaml」 がそのアセンブリのプロジェクトルートからのファイルのパスです
プロジェクトのルートに src フォルダがあって wpf フォルダがあって resource.xaml というファイルがある場合です
「component」 もパスの一部に見えますが ここはパスじゃないです
別アセンブリを表す URI に必要なものです
こういう感じ
<Window 略>
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="#efe0fe" />
</Style>
<s:Boolean x:Key="true">True</s:Boolean>
<local:TestConverter x:Key="test_converter"/>
</Window.Resources>
</Window>
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="#efe0fe" />
</Style>
<s:Boolean x:Key="true">True</s:Boolean>
<local:TestConverter x:Key="test_converter"/>
</Window.Resources>
</Window>
リソースには好きなオブジェクトを置いておけます
よく使う色やアイコン画像や Converter などいろいろです
基本的に XAML のプロパティ設定のときに StaticResource で指定するのが使いみちです
<Label Content="{Binding path,Converter={StaticResource test_converter},ConverterParameter={StaticResource true}}"/>
Style
Resources 内の Style で TargetType で指定した種類のコントロールのスタイルを設定できますx:Key は省略できて 省略すればそのリソースを持つコントロールの子孫要素全てに対して TargetType で設定したスタイルが適用されます
上のコードは Window がもっているリソースに Button の背景色が #efe0fe とあるので この Window の中にあるすべての Button は #efe0fe 色が背景になります
優先順はその要素から徐々に上の階層をみていきます
<Window>
<Grid>
<StackPanel>
<CheckBox>
<TextBlock>
あ
</TextBlock>
</CheckBox>
</StackPanel>
</Grid>
</Window>
<Grid>
<StackPanel>
<CheckBox>
<TextBlock>
あ
</TextBlock>
</CheckBox>
</StackPanel>
</Grid>
</Window>
こういうのだと TextBlock のリソースが最優先で 次に CheckBox → StackPanel → Grid → Window の順になります
Window.Resources に Style を設定しておけば その Window の中の全体が対象ですが ひとつの Window だけじゃなくてアプリケーション全体を対象にしたいときは App.xaml にある Applcation.Resources に設定します
ネスト
あちこちに Resources 書くと見づらくなるので Window.Resources に Button など他の要素のスタイルも書くことができますWindow.Resources に TargetType が Button の Style をつくれば その Window の中の Button に対するスタイルです
その Style にも Style.Resources を指定することができます
Style.Resources に TargetType が Label の Style を作れば Window のなかの Button のなかの Label が対象です
CSS でいう
window button label {}
みたいな感じですサンプル
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Foreground" Value="Purple"/>
<Style.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<StackPanel>
<Button>
<Label>abc</Label>
</Button>
<Button>
<TextBlock>abc</TextBlock>
</Button>
<Button>abc</Button>
</StackPanel>
<Style TargetType="Button">
<Setter Property="Foreground" Value="Purple"/>
<Style.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<StackPanel>
<Button>
<Label>abc</Label>
</Button>
<Button>
<TextBlock>abc</TextBlock>
</Button>
<Button>abc</Button>
</StackPanel>

1 つめは Label の中なので赤色
2 つめは TextBlock の中なので青色
3 つめは Button のスタイルで紫色です
Button の VisualTree を見ると 文字の表示には TextBlock が使われているので 2 つめと 3 つめは同じツリー構造です
なのに 3 つめには TextBlock のスタイルが適用されていません
実際に XAML に書いた LogicalTree でスタイルが当たってるようです
デフォルトスタイル
Resources は便利ですけど 1 つの Resources に同じ TargetType の Style を複数書けませんButton を継承して拡張した Buttton クラスを作ったとします
その XAML でデフォルトのスタイルをこのように指定します
<Button x:Class="liblib.wpf.Buttton"
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:liblib.wpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Background" Value="Orange"/>
</Style>
</Button.Resources>
</Button>
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:liblib.wpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Background" Value="Orange"/>
</Style>
</Button.Resources>
</Button>
このコントロールを使う Window でリソースを設定すると実行時にエラーが起きます
<local:Buttton>
<local:Buttton.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Foreground" Value="Blue" />
</Style>
</local:Buttton.Resources>
abc
</local:Buttton>
<local:Buttton.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Foreground" Value="Blue" />
</Style>
</local:Buttton.Resources>
abc
</local:Buttton>
local:Buttton.Resources の Style は Buttton.xaml のほうにあるのでここで設定すると 2 つあることになってしまいます
直接 local:Buttton の Resources でなく Window に設定すれば 2 つあることは回避できます
<Window.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Background" Value="Aqua"/>
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Window.Resources>
<local:Buttton>
abc
</local:Buttton>
<Style TargetType="local:Buttton">
<Setter Property="Background" Value="Aqua"/>
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Window.Resources>
<local:Buttton>
abc
</local:Buttton>
ですが こうしても local:Buttton につけられている Resources のほうが優先されるので 背景が Orange になるだけで Window.Resources で設定した Style は無視されてしまいます
local:Buttton.Resources でなく local:Buttton.Style を設定することもできます
<local:Buttton>
<local:Buttton.Style>
<Style TargetType="local:Buttton">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Style>
abc
</local:Buttton>
<local:Buttton.Style>
<Style TargetType="local:Buttton">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Style>
abc
</local:Buttton>
Resources より Style が優先なので このコードでは 文字色が青色になりますが背景は Orange にならずデフォルトのボタン色です
背景を引き継ぎたいなら BasedOn を Style に設定します
<local:Buttton>
<local:Buttton.Style>
<Style TargetType="local:Buttton" BasedOn="{StaticResource {x:Type local:Buttton}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Style>
abc
</local:Buttton>
<local:Buttton.Style>
<Style TargetType="local:Buttton" BasedOn="{StaticResource {x:Type local:Buttton}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Style>
abc
</local:Buttton>
これだと Style だけの設定なので Resources にあれこれ含めたい場合は .Resources もかかないといけません
<local:Buttton>
<local:Buttton.Style>
<Style TargetType="local:Buttton" BasedOn="{StaticResource {x:Type local:Buttton}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Style>
<local:Buttton.Resources>
<local:SampleConverter x:Key="converter" />
</local:Buttton.Resources>
abc
</local:Buttton>
<local:Buttton.Style>
<Style TargetType="local:Buttton" BasedOn="{StaticResource {x:Type local:Buttton}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Style>
<local:Buttton.Resources>
<local:SampleConverter x:Key="converter" />
</local:Buttton.Resources>
abc
</local:Buttton>
長くなりますし Style だけ別なのもなんかイヤです
できればデフォルトのスタイルを そのクラス定義ファイルに書いておきたいのですが 要素の Resources プロパティでなく Application.Resources に定義しておくのが一般的みたいです
Buttton.xaml ファイルはなくしてしまって App.xaml にこう書きます
<Application.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Background" Value="Orange"/>
</Style>
</Application.Resources>
<Style TargetType="local:Buttton">
<Setter Property="Background" Value="Orange"/>
</Style>
</Application.Resources>
Buttton の Resources プロパティには何も入っていないので Window の方ではこうかけます
<local:Buttton>
<local:Buttton.Resources>
<local:SampleConverter x:Key="converter" />
<Style TargetType="local:Buttton" BasedOn="{StaticResource {x:Type local:Buttton}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Resources>
abc
</local:Buttton>
<local:Buttton.Resources>
<local:SampleConverter x:Key="converter" />
<Style TargetType="local:Buttton" BasedOn="{StaticResource {x:Type local:Buttton}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:Buttton.Resources>
abc
</local:Buttton>
この場合も BasedOn がないと適用されるスタイルはここに書いた青色文字だけになって 背景色がなくなりますので注意です
Style や Resources を使わずに直接プロパティを指定した場合は 適用されたスタイルにプロパティ設定したものを上書きするように両方が適用されます
<local:Buttton Foreground="Blue">
abc
</local:Buttton>
だと 背景も文字色もありますabc
</local:Buttton>
ですが ひとつひとつに設定していかないといけないのは不便ですよね
ResourceDictionary
App.xaml の Application.Resources にデフォルトスタイルを書くとしても 全部ここに書いたらすごく長くなるし あれこれ混ざって見づらいです分けて書きたい ってときには ResourceDictionary が使えます
よくあるファイル分割です
パーツとなる XAML のルートは ResourceDictionary にします
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:liblib.wpf">
<Style TargetType="Button">
<Setter Property="Foreground" Value="Orange" />
</Style>
<Color x:Key="Red">#fe4060</Color>
</ResourceDictionary>
ResourceDictionary の中には .Resources と同じく Style などを置いておけますxmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:liblib.wpf">
<Style TargetType="Button">
<Setter Property="Foreground" Value="Orange" />
</Style>
<Color x:Key="Red">#fe4060</Color>
</ResourceDictionary>
ロードする側は .Resources に ResourceDictionary を Source プロパティ付きでいれます
<Application 略>
<Application.Resources>
<ResourceDictionary Source="Parts1.xaml" />
</Application.Resources>
</Application>
<Application.Resources>
<ResourceDictionary Source="Parts1.xaml" />
</Application.Resources>
</Application>
複数をまとめたいなら ResourceDictionary の MergedDictionaries プロパティに ResourceDictionary をいれます
<Window 略>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Parts1.xaml"/>
<ResourceDictionary Source="Parts2.xaml"/>
<ResourceDictionary Source="Parts3.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Window.Resources>
</Window>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Parts1.xaml"/>
<ResourceDictionary Source="Parts2.xaml"/>
<ResourceDictionary Source="Parts3.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Window.Resources>
</Window>
Source プロパティ付きの ResourceDictionary は子要素にリソースをもつことができないです
Source とその場で書いたリソースをまとめたいなら MergedDictionaries で Source 付きと直接リソース定義したものをマージします
<Window 略>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Parts1.xaml"/>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="Foreground" Value="HotPink" />
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Window.Resources>
</Window>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Parts1.xaml"/>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="Foreground" Value="HotPink" />
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Window.Resources>
</Window>
MergedDictionaries プロパティがあるときも Source プロパティがあるのと一緒で こういう直接リソースかくことはできません
<Window 略>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Parts1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Blue" />
</Style>
</ResourceDictionary>
<Window.Resources>
</Window>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Parts1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Blue" />
</Style>
</ResourceDictionary>
<Window.Resources>
</Window>
それと .Resources の中に ResourceDictionary があるときに 直接他のリソース定義もできません
<Window 略>
<Window.Resources>
<ResourceDictionary>
</ResourceDictionary>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Blue" />
</Style>
<Window.Resources>
</Window>
<Window.Resources>
<ResourceDictionary>
</ResourceDictionary>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Blue" />
</Style>
<Window.Resources>
</Window>
別アセンブリ
Source 指定で ResourceDictionary を取得するときに別アセンブリも指定できます<Application 略>
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/liblib;component/src/wpf/resource.xaml"/>
</Application.Resources>
</Application>
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/liblib;component/src/wpf/resource.xaml"/>
</Application.Resources>
</Application>
「liblib」 がアセンブリ名です
「src/wpf/resource.xaml」 がそのアセンブリのプロジェクトルートからのファイルのパスです
プロジェクトのルートに src フォルダがあって wpf フォルダがあって resource.xaml というファイルがある場合です
「component」 もパスの一部に見えますが ここはパスじゃないです
別アセンブリを表す URI に必要なものです