akuruhinode's blog

pythonやC#を中心に興味を持った内容について調べています。

WPF シンプルなCheckBoxのスタイルを作る

はじめに

ここでは、シンプルかつFlatなUIデザインで使えそうなチェックボックスのスタイルを作成します。スタイルの変更方法については以下を参考にしています。
CheckBox のスタイルとテンプレート - WPF .NET Framework | Microsoft Docs

丸いスタイル1

イメージ

作成するスタイルは以下の通りです。
WPF スタイル チェックボックス1

ソースコード

ソースコードは以下の通りです。

<Window
    x:Class="Checkbox1.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:local="clr-namespace:Checkbox1"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.Resources>
        <!--  チェックボックスのサイズや見た目  -->
        <Thickness x:Key="CheckBorderThickness">0.0</Thickness>
        <Thickness x:Key="UnCheckBorderThickness">2.0</Thickness>
        <CornerRadius x:Key="CheckBoxCornerRadius">20.0</CornerRadius>

        <!--  チェックボックスの色  -->
        <SolidColorBrush x:Key="CheckedBorderColor">#0984e3</SolidColorBrush>
        <SolidColorBrush x:Key="CheckedBackColor">#0984e3</SolidColorBrush>
        <SolidColorBrush x:Key="UnCheckedBorderColor">#0984e3</SolidColorBrush>
        <SolidColorBrush x:Key="UnCheckedBackColor">#ffffff</SolidColorBrush>
        <SolidColorBrush x:Key="CheckMarkColor">#ffffff</SolidColorBrush>
        <SolidColorBrush x:Key="IndeterminateMarkColor">#d63031</SolidColorBrush>

        <!--  チェックボックスのスタイル  -->
        <Style x:Key="MyCheckBox" TargetType="{x:Type CheckBox}">
            <Setter Property="SnapsToDevicePixels" Value="true" />
            <Setter Property="OverridesDefaultStyle" Value="true" />
            <Setter Property="FocusVisualStyle" Value="{DynamicResource CheckBoxFocusVisual}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CheckBox}">
                        <BulletDecorator>
                            <BulletDecorator.Bullet>
                                <Grid Width="20.0" Height="20.0">
                                    <!--  チェックボックス  -->
                                    <Border
                                        x:Name="CheckBoxBorder"
                                        Background="{StaticResource UnCheckedBackColor}"
                                        BorderBrush="{StaticResource UnCheckedBorderColor}"
                                        BorderThickness="{StaticResource UnCheckBorderThickness}"
                                        CornerRadius="{StaticResource CheckBoxCornerRadius}" />
                                    <!--  チェックマーク  -->
                                    <Path
                                        x:Name="CheckMark"
                                        Margin="4"
                                        Data="M0 4 L3.5 7 8 2"
                                        SnapsToDevicePixels="False"
                                        Stretch="Uniform"
                                        Stroke="{StaticResource CheckMarkColor}"
                                        StrokeThickness="2.5"
                                        Visibility="Collapsed" />
                                    <!--  不定時のチェックマーク  -->
                                    <Path
                                        x:Name="IndeterminateMark"
                                        Margin="4"
                                        Data="M 0 7 L 7 0"
                                        SnapsToDevicePixels="False"
                                        Stretch="Uniform"
                                        Stroke="{StaticResource IndeterminateMarkColor}"
                                        StrokeThickness="2.5"
                                        Visibility="Collapsed" />
                                </Grid>
                            </BulletDecorator.Bullet>
                            <ContentPresenter
                                Margin="4,0,0,0"
                                HorizontalAlignment="Left"
                                VerticalAlignment="Center" />
                        </BulletDecorator>
                        <ControlTemplate.Triggers>
                            <!--  チェック時のスタイル  -->
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
                                <Setter TargetName="CheckBoxBorder" Property="Background" Value="{StaticResource CheckedBackColor}" />
                                <Setter TargetName="CheckBoxBorder" Property="BorderThickness" Value="{StaticResource CheckBorderThickness}" />
                            </Trigger>
                            <!--  不定時のスタイル  -->
                            <Trigger Property="IsChecked" Value="{x:Null}">
                                <Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <!--  未チェック状態でマウスオーバー時のスタイル  -->
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True" />
                                    <Condition Property="IsChecked" Value="False" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
                                <Setter TargetName="CheckMark" Property="Stroke" Value="{StaticResource CheckedBackColor}" />
                                <Setter TargetName="CheckMark" Property="Opacity" Value="0.2" />
                            </MultiTrigger>
                            <!--  チェック状態でマウスオーバー時のスタイル  -->
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True" />
                                    <Condition Property="IsChecked" Value="True" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="CheckBoxBorder" Property="Opacity" Value="0.8" />
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel>
            <!--  スタイルの適用例  -->
            <CheckBox
                Content="未チェック状態"
                IsChecked="False"
                Style="{StaticResource MyCheckBox}" />
            <Label Height="10" />
            <CheckBox
                Content="チェック状態"
                IsChecked="True"
                Style="{StaticResource MyCheckBox}" />
            <Label Height="10" />
            <CheckBox
                Content="不定状態"
                IsChecked="{x:Null}"
                Style="{StaticResource MyCheckBox}" />
        </StackPanel>
    </Grid>
</Window>

解説

サイズや色の定義

まず主なパラメータをリソースとして定義します。後から異なるスタイルに変更した例も紹介しますが、こうすることでスムーズにスタイルを変更することができます(目的のスタイルが決まった場合は直接入力しても良いと思います)。


色選びについては以下のサイトが参考になるのでおすすめです。
Flat UI Colors 2 - 14 Color Palettes, 280 colors 🎨

<Window.Resources>
    <!--  チェックボックスのサイズや見た目  -->
    <Thickness x:Key="CheckBorderThickness">0.0</Thickness>
    <Thickness x:Key="UnCheckBorderThickness">2.0</Thickness>
    <CornerRadius x:Key="CheckBoxCornerRadius">20.0</CornerRadius>

    <!--  チェックボックスの色  -->
    <SolidColorBrush x:Key="CheckedBorderColor">#0984e3</SolidColorBrush>
    <SolidColorBrush x:Key="CheckedBackColor">#0984e3</SolidColorBrush>
    <SolidColorBrush x:Key="UnCheckedBorderColor">#0984e3</SolidColorBrush>
    <SolidColorBrush x:Key="UnCheckedBackColor">#ffffff</SolidColorBrush>
    <SolidColorBrush x:Key="CheckMarkColor">#ffffff</SolidColorBrush>
    <SolidColorBrush x:Key="IndeterminateMarkColor">#d63031</SolidColorBrush>
チェックボックスの背景、チェックマークの定義

Grid内に、チェックボックスの背景用のBorder、チェックマーク用のPathを作成します。
ここではチェック状態と不定状態の2つのPathを定義します。このPathの使い方は結構複雑なので、ここでは詳しく説明しません。機会があれば、別記事で取り上げようと思います。

ポイントとしては、Stretch="Uniform"とすることで親であるGridのWight、Heightが変化した場合にチェックマークのサイズも変更されるようにしています。そのためサイズ調整したい場合はGridのWight、Heightだけ調整すればよいです。

<Grid Width="20.0" Height="20.0">
    <!--  チェックボックス  -->
    <Border
        x:Name="CheckBoxBorder"
        Background="{StaticResource UnCheckedBackColor}"
        BorderBrush="{StaticResource UnCheckedBorderColor}"
        BorderThickness="{StaticResource UnCheckBorderThickness}"
        CornerRadius="{StaticResource CheckBoxCornerRadius}" />
    <!--  チェックマーク  -->
    <Path
        x:Name="CheckMark"
        Margin="4"
        Data="M0 4 L3.5 7 8 2"
        SnapsToDevicePixels="False"
        Stretch="Uniform"
        Stroke="{StaticResource CheckMarkColor}"
        StrokeThickness="2.5"
        Visibility="Collapsed" />
    <!--  不定時のチェックマーク  -->
    <Path
        x:Name="IndeterminateMark"
        Margin="4"
        Data="M 0 7 L 7 0"
        SnapsToDevicePixels="False"
        Stretch="Uniform"
        Stroke="{StaticResource IndeterminateMarkColor}"
        StrokeThickness="2.5"
        Visibility="Collapsed" />
</Grid>
チェックマークを表示させる

Triggerを利用して、IsCheckedの値に応じて表示するチェックマークの変更やBorderのBackground、BorderThicknessを変更します。
BorderThicknessについてはチェック時に0以上にして外枠と内側両方に色を設定した場合、色が綺麗に表示されないことがあったためこのようにしています。


今回はチェック状態のTriggerだけではなく、「チェックかつ、マウスオーバー」「未チェックかつ、マウスオーバー」の複合型のTriggerも設定します。
この場合は、MultiTriggerを利用することで実現可能です。

<ControlTemplate.Triggers>
    <!--  チェック時のスタイル  -->
    <Trigger Property="IsChecked" Value="True">
        <Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
        <Setter TargetName="CheckBoxBorder" Property="Background" Value="{StaticResource CheckedBackColor}" />
        <Setter TargetName="CheckBoxBorder" Property="BorderThickness" Value="{StaticResource CheckBorderThickness}" />
    </Trigger>
    <!--  不定時のスタイル  -->
    <Trigger Property="IsChecked" Value="{x:Null}">
        <Setter TargetName="IndeterminateMark" Property="Visibility" Value="Visible" />
    </Trigger>
    <!--  未チェック状態でマウスオーバー時のスタイル  -->
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="True" />
            <Condition Property="IsChecked" Value="False" />
        </MultiTrigger.Conditions>
        <Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
        <Setter TargetName="CheckMark" Property="Stroke" Value="{StaticResource CheckedBackColor}" />
        <Setter TargetName="CheckMark" Property="Opacity" Value="0.2" />
    </MultiTrigger>
    <!--  チェック状態でマウスオーバー時のスタイル  -->
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="True" />
            <Condition Property="IsChecked" Value="True" />
        </MultiTrigger.Conditions>
        <Setter TargetName="CheckBoxBorder" Property="Opacity" Value="0.8" />
    </MultiTrigger>
</ControlTemplate.Triggers>
スタイルを適用する

それぞれの状態を確認できるように3つのチェックボックスを追加して、それぞれにスタイルを適用させています。間のLabelはスペース調整のためのもので深い意味はありません。

<StackPanel>
    <!--  スタイルの適用例  -->
    <CheckBox
        Content="未チェック状態"
        IsChecked="False"
        Style="{StaticResource MyCheckBox}" />
    <Label Height="10" />
    <CheckBox
        Content="チェック状態"
        IsChecked="True"
        Style="{StaticResource MyCheckBox}" />
    <Label Height="10" />
    <CheckBox
        Content="不定状態"
        IsChecked="{x:Null}"
        Style="{StaticResource MyCheckBox}" />
</StackPanel>

丸いスタイル2

イメージ

作成するスタイルは以下の通りです。上の例とは色のパターンを変更しています。
WPF スタイル チェックボックス2

ソースコード

上の例から、以下の部分のみ変更することで対応可能です。ここでは上の例と異なりチェック状態のCheckBorderThicknessを2.0に設定してます。

    <Window.Resources>
        <!--  チェックボックスのサイズや見た目関連  -->
        <Thickness x:Key="CheckBorderThickness">2.0</Thickness>
        <Thickness x:Key="UnCheckBorderThickness">2.0</Thickness>
        <CornerRadius x:Key="CheckBoxCornerRadius">20.0</CornerRadius>

        <!--  チェックボックスの色  -->
        <SolidColorBrush x:Key="CheckedBorderColor">#00b894</SolidColorBrush>
        <SolidColorBrush x:Key="CheckedBackColor">#ffffff</SolidColorBrush>
        <SolidColorBrush x:Key="UnCheckedBorderColor">#00b894</SolidColorBrush>
        <SolidColorBrush x:Key="UnCheckedBackColor">#ffffff</SolidColorBrush>
        <SolidColorBrush x:Key="CheckMarkColor">#00b894</SolidColorBrush>
        <SolidColorBrush x:Key="IndeterminateMarkColor">#d63031</SolidColorBrush>

なお、チェックマークの色、チェックボックスの色の変更に伴い、マウスオーバー時のスタイルも少し変更しています。

<ControlTemplate.Triggers>
	・・・省略・・・
    <!--  未チェック状態でマウスオーバー時のスタイル  -->
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="True" />
            <Condition Property="IsChecked" Value="False" />
        </MultiTrigger.Conditions>
        <Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
        <Setter TargetName="CheckMark" Property="Stroke" Value="{StaticResource CheckMarkColor}" /> ←ここ
        <Setter TargetName="CheckMark" Property="Opacity" Value="0.2" />
    </MultiTrigger>
    <!--  チェック状態でマウスオーバー時のスタイル  -->
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="True" />
            <Condition Property="IsChecked" Value="True" />
        </MultiTrigger.Conditions>
        <Setter TargetName="CheckMark" Property="Opacity" Value="0.6" /> ←ここ
    </MultiTrigger>
</ControlTemplate.Triggers>

角丸スタイル1

イメージ

作成するスタイルは以下の通りです。角丸な外観にしていますが他は最初の例と変わりません。CheckBoxCornerRadiusを調整するだけで対応可能です。
WPF スタイル チェックボックス3 角丸

ソースコード

上の例と同様に以下の部分のみ変更することで対応可能です。

<Window.Resources>
    <!--  チェックボックスのサイズや見た目関連  -->
    <Thickness x:Key="CheckBorderThickness">0.0</Thickness>
    <Thickness x:Key="UnCheckBorderThickness">2.0</Thickness>
    <CornerRadius x:Key="CheckBoxCornerRadius">4.0</CornerRadius>

    <!--  チェックボックスの色  -->
    <SolidColorBrush x:Key="CheckedBorderColor">#0984e3</SolidColorBrush>
    <SolidColorBrush x:Key="CheckedBackColor">#0984e3</SolidColorBrush>
    <SolidColorBrush x:Key="UnCheckedBorderColor">#0984e3</SolidColorBrush>
    <SolidColorBrush x:Key="UnCheckedBackColor">#ffffff</SolidColorBrush>
    <SolidColorBrush x:Key="CheckMarkColor">White</SolidColorBrush>
    <SolidColorBrush x:Key="IndeterminateMarkColor">#d63031</SolidColorBrush>

角丸スタイル2

イメージ

作成するスタイルは以下の通りです。角丸な外観にしつつ2つ目の例と同じような色のパターンにしています。
WPF スタイル チェックボックス4 角丸

ソースコード

上の例と同様に以下の部分のみ変更することで対応可能です。

<Window.Resources>
    <!--  チェックボックスのサイズや見た目関連  -->
    <Thickness x:Key="CheckBorderThickness">2.0</Thickness>
    <Thickness x:Key="UnCheckBorderThickness">2.0</Thickness>
    <CornerRadius x:Key="CheckBoxCornerRadius">4.0</CornerRadius>

    <!--  チェックボックスの色  -->
    <SolidColorBrush x:Key="CheckedBorderColor">#00b894</SolidColorBrush>
    <SolidColorBrush x:Key="CheckedBackColor">#ffffff</SolidColorBrush>
    <SolidColorBrush x:Key="UnCheckedBorderColor">#00b894</SolidColorBrush>
    <SolidColorBrush x:Key="UnCheckedBackColor">#ffffff</SolidColorBrush>
    <SolidColorBrush x:Key="CheckMarkColor">#00b894</SolidColorBrush>
    <SolidColorBrush x:Key="IndeterminateMarkColor">#d63031</SolidColorBrush>