[WPF] Styleでできることと書き方

もくじ

https://tera1707.com/entry/2022/02/06/144447

# ■やりたいこと

画面の部品の見た目や挙動をStyleを使って変えたいなと思ったときに、いまだにどこにどう書くんだったか、と迷う。
しかもなぜか、以前に調べたことを毎回結構忘れていて時間を取られるので、一度テンプレート的なものを持っておきたい。

# ■Styleを書く場所

## 直接Styleを書く

通常状態／マウスオーバー状態
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/5c972229-3a39-37f4-d595-56c7356100fc.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/40224c64-111a-8087-aa69-8745520553fc.png)

```MainWindow.xaml
<Window x:Class="WpfApp3.MainWindow"
        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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Grid>
        <Rectangle x:Name="chk" Margin="50" Stroke="Black" >
            <Rectangle.Style>
                <Style TargetType="Rectangle">
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Fill" Value="GreenYellow"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="False">
                            <Setter Property="Fill" Value="RosyBrown"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Rectangle.Style>
        </Rectangle>
    </Grid>
</Window>
```

## `<Window.Resources>`にStyleを書く(Keyなし)

Resourcesの中にキー名なしでTargetTypeを指定してstyleを書くと、画面に配置されている指定したTypeのものが全部そのstyleの見た目になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/4d2f6129-597a-9302-0f97-ef6e00e74c62.png)

```MainWindow.xaml
<Window x:Class="WpfApp3.MainWindow"
        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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Window.Resources>
        <Style TargetType="Rectangle">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Fill" Value="GreenYellow"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Fill" Value="RosyBrown"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Rectangle Margin="5" Height="50" Width="50" Stroke="Black" />
            <Rectangle Margin="5" Height="50" Width="50" Stroke="Black" />
        </StackPanel>
    </Grid>
</Window>
```

## `<Window.Resources>`にStyleを書く(Keyあり)

Resourcesの中にキー名あり(`<Style x:Key="xxxx"`のところ)でStyleを書くと、画面上にあるkey名を指定したものだけ、その見た目になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/6a105e08-24f0-cca8-753f-aea32d90df45.png)

```MainWindow.xaml
<Window x:Class="WpfApp3.MainWindow"
        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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Window.Resources>
        <Style x:Key="RectStyle" TargetType="Rectangle">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Fill" Value="GreenYellow"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Fill" Value="RosyBrown"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Rectangle Style="{DynamicResource RectStyle}" Margin="5" Height="50" Width="50" Stroke="Black" />
            <Rectangle Margin="5" Height="50" Width="50" Stroke="Black" />
        </StackPanel>
    </Grid>
</Window>
```

**※注意**
同じTargetTypeのStyleでkey名のあるもの、ないもの両方あるときも、Styleに"{DynamicResource key名}"を指定したものだけその見た目になる

## ResourceDictionary.xamlにStyleを書く

以前の記事を参照。

https://qiita.com/tera1707/items/a462678cdfb61a87334b

# ■できること

## 自分自身のプロパティの値に応じて見た目を変える

これを**「プロパティトリガー」**という。
**自分のプロパティの値**に応じて、**自分の見た目にかかわるプロパティの値を変える**、ということ。

```例.xaml
<Trigger Property="IsMouseOver" Value="True">
    <Setter Property="Fill" Value="GreenYellow"/>
</Trigger>
```

※上の「書く場所」に挙げたサンプルにある内容。

## ViewModel等のプロパティの値に応じて見た目を変える

これを**「データトリガー」**という。
詳細は以前の記事を参照。

https://qiita.com/tera1707/items/7591889617efc69fcb95

下記のような感じ。

```例.xaml
<!-- ViewModelが持ってるプロパティColorChangeFlagにバインドしてる -->
<DataTrigger Binding="{Binding ColorChangeFlag}" Value="true">
     <Setter Property="Fill" Value="Blue"/>
</DataTrigger>
```

## Styleの中でTemplateを変更する

できること、というよりは、こういう書き方もできる、という感じ。

各コントロールの標準のテンプレートを出すと、よく見る書かれ方。

下は、CheckBoxのデフォルトのStyleをまともに動かないくらい簡略化したものを、
リソースにControlTemplateを書いてkey指定してつかうものと、styleに直接書くものをやってみたコード。

やる機会はあまりないと思うが、これをやれば、プロパティの値によってTemplateを変えることができる、イコール見た目をガラッと変えることもできる。(むしろそんなことはしてはいけないかもしれない)

通常時／マウスオーバー時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/7532228e-f222-0291-515e-5d7a44abcb56.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/8cac1e38-499f-43b3-e463-476fbf83d08a.png)

```MainWindows.xaml
<Window x:Class="WpfApp3.MainWindow"
        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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Window.Resources>
        <!-- テンプレートをリソースとして書く -->
        <ControlTemplate x:Key="type1" TargetType="{x:Type CheckBox}">
            <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Border x:Name="checkBoxBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                    <Grid x:Name="markGrid">
                        <TextBlock Text="1"/>
                    </Grid>
                </Border>
                <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Grid>
        </ControlTemplate>
                
        <!-- チェックボックスのスタイル本体 -->
        <Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <!-- ★styleの中でTemplateを指定(Resourceに書いたやつをDynamicResourceで指定) -->
                    <Setter Property="Template" Value="{DynamicResource type1}"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="False">
                    <!-- ★styleの中でTemplateを指定(ここに直接書く)(デフォのStyleでよくこの書き方で書かれてるので、こっちが基本っぽい) -->
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type CheckBox}">
                                <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
                                    <Border x:Name="checkBoxBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                        <Grid x:Name="markGrid">
                                            <TextBlock Text="2"/>
                                        </Grid>
                                    </Border>
                                    <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    
    <!-- 画面本体 -->
    <Grid>
        <CheckBox Content="チェックボックス" Style="{DynamicResource CheckBoxStyle1}"/>
    </Grid>
</Window>
```

## アニメーションを記述する

以前の記事を参照。

https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b

以前の記事では、CheckBoxの見た目を変化させてスイッチがぐいーんと動くようなことをやっているので、ControlTemplateとの合わせ技で実現させているが、見た目がダイナミックに変わらなくてよいようなアニメーションをするだけなら(例えば、背景色がじわじわ変わるだけとか)、Templateを使わなくても、プロパティの値を変化させるだけ(例えばBackgroundの値をじわじわ変化させるとか)でも実現できるはず。