1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【WPF】ControlTemplateやStyleを別ファイルに定義し、Viewから利用する方法

Last updated at Posted at 2024-09-13

概要

xamlのコントロールのControlTemplateStyleを別のxamlファイルに定義し、それを色々なxamlファイルから利用する方法をまとめました。
別ファイルにまとめておくことで、テンプレートやスタイルが再利用できやすくなったり、各コントロールの装飾の定義自体で、画面を定義しているxamlが煩雑になりすぎることも防ぐことができます。

方法

ControlTemplateStyleの定義をまとめた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というファイルを作成しました。そのファイルにControlTemplateStyleを定義して、各画面の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

ListViewListViewItemStyleを定義する箇所 (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>
                    <!-- 以下省略 -->

ControlTemplateStyleを定義した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内で使用可能で、テンプレート内にあるプロパティを、テンプレートが適用されているコントロールのプロパティとリンクするものです。
上記の場合はSample1ViewToggleButtonContentプロパティとリンクしています。

おわりに

この方法は実業務でもよく利用していたので、まとめておきました。
画面のコントロールに少々複雑なControlTemplateStyleを定義していても、別ファイルに切り出しておくことで実際に使用するView側のファイルでは、画面をスクロールする頻度が減りました。なのでxamlの画面の改修の際も、改修が行い易くなった気がします。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?