概要
xamlのコントロールのControlTemplate
やStyle
を別のxamlファイルに定義し、それを色々なxamlファイルから利用する方法をまとめました。
別ファイルにまとめておくことで、テンプレートやスタイルが再利用できやすくなったり、各コントロールの装飾の定義自体で、画面を定義しているxamlが煩雑になりすぎることも防ぐことができます。
方法
ControlTemplate
やStyle
の定義をまとめたxamlファイル (ResourceDictionary
) を作成しておきます。そして、画面を作成している別のxamlファイル (View
) から、ResourceDictionary
内のMergedDictionaries
を使用して別のxamlファイルにまとめたリソースを利用します。
MergedDictionaries
はリソースの共有や再利用を容易にするための機能です。
ResourceDictionaryについて
ResourceDictionary
は、XAMLで色々な箇所で使用することができる再利用可能なリソースを定義するするための機能です。
各リソースには、名前 (Key) を設定して、他から参照できるようにしておくことができます。
以下のように、画面を作成しているViewの<ResourceDictionary>
部分にリソースを定義して同じxamlファイルから使用することが可能です。
<Window x:Class="Hoge.SampleView1
Title="サンプル画面1" Width="300">
<Window.Resources>
<ResourceDictionary>
<!-- リソースの定義 -->
<ControlTemplate x:Key="SampleView1Button" TargetType="{x:Type ToggleButton}">
<Border
x:Name="sampleBorder"
CornerRadius="20">
<TextBlock
x:Name="sampleTextBlock"
Text="{TemplateBinding Content}" />
</Border>
<ControlTemplate.Triggers>
<!-- リソースの処理 ※省略 -->
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ToggleButton
Width="150"
Height="30"
Content="{Binding ButtonName}"
IsChecked="{Binding IsChecked}"
Template="{StaticResource SampleView1Button}" /> <!-- リソースを使用 -->
</Grid>
</Window>
ただ他のxamlファイルからも上記と同じリソース (ここではSampleView1Button
) を使用したいという場合があります。そのような場合に別のxamlファイルのResourceDictionary
にリソースを定義します。
ここではSampleDictionary.xaml
というファイル名のResourceDictionary
を定義しています。
<ResourceDictionary xmlns="http://hogehoge"
xmlns:x="http://fugafuga">
<ControlTemplate x:Key="SampleView1Button" TargetType="{x:Type ToggleButton}">
<Border
x:Name="sampleBorder"
CornerRadius="20">
<TextBlock
x:Name="sampleTextBlock"
Text="{TemplateBinding Content}" />
</Border>
<ControlTemplate.Triggers>
<!-- リソースの処理 ※省略 -->
</ControlTemplate.Triggers>
</ControlTemplate>
画面を作成するview側では、ResourceDictionary.MergedDictionaries
を使用して、SampleDictionary.xaml
のリソースを使用できるようにし、ToggleButton
のテンプレートにSampleDictionary.xaml
で定義したテンプレートを使用しています。
<Window x:Class="Hoge.SampleView1
Title="サンプル画面1" Width="300">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../SampleDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ToggleButton
Width="150"
Height="30"
Content="{Binding ButtonName}"
IsChecked="{Binding IsChecked}"
Template="{StaticResource SampleView1Button}" /> <!-- リソースを使用 -->
</Grid>
</Window>
サンプルコード
サンプル画面1 (SampleView1
) とサンプル画面2 (SampleView2
) を作成しました。
リソースを定義するファイルは、StyleDictionary.xaml
というファイルを作成しました。そのファイルにControlTemplate
やStyle
を定義して、各画面のxamlからそれを利用しています。
サンプル画面1のView
ToggleButton
のテンプレートにStyleDictionary.xaml
で定義したテンプレートを使用しています。
StyleDictionary
には、ToggleButton
の押下時、未押下時のボタンの見た目を定義しています。
<Window x:Class="Hoge.SampleView1
Title="サンプル画面1" Width="300">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../StyleDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ToggleButton
Width="150"
Height="30"
Content="{Binding ButtonName}"
IsChecked="{Binding IsChecked}"
Template="{StaticResource SampleView1Button}" />
</Grid>
</Window>
サンプル画面2のView
ListView
のListViewItem
のStyle
を定義する箇所 (ItemContainerStyle
) に、StyleDictionary.xaml
で定義したテンプレートを使用しています。
StyleDictionary
には、ListView
の奇数行や偶数行、選択行の背景色を設定するStyle
を定義しています。
<Window x:Class="Hoge.SampleView2"
Title="サンプル画面2" Width="500">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../StyleDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ListView
SelectionMode="Single"
AlternationCount="2"
KeyboardNavigation.TabNavigation="Continue"
Padding="7"
ItemContainerStyle="{StaticResource SampleListViewItemStyle}"
ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridView.Columns>
<!-- 以下省略 -->
ControlTemplate
やStyle
を定義したxamlファイル (StyleDictionary.xaml)
<ResourceDictionary xmlns="http://hogehoge"
xmlns:x="http://fugafuga">
<!-- サンプル画面1のボタンのテンプレート -->
<ControlTemplate x:Key="SampleView1Button" TargetType="{x:Type ToggleButton}">
<Border
x:Name="sampleBorder"
CornerRadius="20">
<TextBlock
x:Name="sampleTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="10"
FontWeight="Bold"
Text="{TemplateBinding Content}" />
</Border>
<ControlTemplate.Triggers>
<!-- ボタン未押下時 -->
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="sampleBorder" Property="Background" Value="LightGray" />
<Setter TargetName="sampleBorder" Property="BorderBrush" Value="Transparent" />
<Setter TargetName="sampleTextBlock" Property="Foreground" Value="White" />
</Trigger>
<!-- ボタン押下時 -->
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="sampleBorder" Property="Background" Value="DodgerBlue" />
<Setter TargetName="sampleBorder" Property="BorderBrush" Value="Transparent" />
<Setter TargetName="sampleTextBlock" Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- サンプル画面2のListViewItemのStyle -->
<Style x:Key="SampleListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="IsTabStop" Value="False"/>
<Style.Triggers>
<!-- 奇数行の背景色 -->
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="AntiqueWhite"/>
</Trigger>
<!-- 偶数行の背景色 -->
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="NavajoWhite"/>
</Trigger>
<!-- 選択行の背景色 -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
TemplateBinding
について
ControlTemplate
内で使用可能で、テンプレート内にあるプロパティを、テンプレートが適用されているコントロールのプロパティとリンクするものです。
上記の場合はSample1View
のToggleButton
のContent
プロパティとリンクしています。
おわりに
この方法は実業務でもよく利用していたので、まとめておきました。
画面のコントロールに少々複雑なControlTemplate
やStyle
を定義していても、別ファイルに切り出しておくことで実際に使用するView側のファイルでは、画面をスクロールする頻度が減りました。なのでxamlの画面の改修の際も、改修が行い易くなった気がします。