[WPF] DataGridのデザイン変更

#はじめに
せっかくWPFでアプリを作るなら、旧来のWin32を引きずったデザインではなく、モダンな見た目で作りたいなと思うわけです。
たとえそれが、しがない業務アプリだとしても・・・

[neue ccさんのブログ](http://neue.cc/2015/09/10_515.html)を参考に、[Material Design In XAML Toolkit](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit)を入れてみると、確かにテンション上がります。
見た目が変わるだけで、素晴らしいアプリに見えてくる！
（全体のイメージは[本家スクリーンショットを参照](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit#-screenshots)）

ただ、DataGridだけは、カッコ良すぎるというか、業務アプリとしては使いづらいかなと思うわけで。
そんなDataGridのデザイン(Style)を、アレコレ悩んで 弄ったときの記録です。

###環境
.NET Framework 4.6.1
MaterialDesignThemes 2.3.0.823
MahApps.Metro 1.5.0

#素のフォーム

サンプルとして、TextBox, ComboBox, Button と DataGrid を並べたフォームを用意しました。
neue ccさん的に言う、実にギョーミーな画面。
グリッドのデータは、[なんちゃって個人情報](http://kazina.com/dummy/)から拝借しました。

![image](https://qiita-image-store.s3.amazonaws.com/0/177379/dd9c6b12-2fcd-5b28-bdba-8624e66c0d8d.png)


#Material Design In XAML Toolkit導入

こいつに、Material Design In XAML Toolkit と、MahApps.Metro を適用します。
どちらもNuGetで入れました。

App.xaml にStyle定義を行い、Window は MahApps.Metro.Controls.MetroWindow を継承します。

```xml:App.xaml
<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:materialDesign="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
             xmlns:local="clr-namespace:WpfApplication1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>

                <!-- MahApps -->
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />

                <!-- Material Design -->
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Indigo.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Red.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Button.xaml" />
                
            </ResourceDictionary.MergedDictionaries>

            <!-- MahApps Brushes -->
            <SolidColorBrush x:Key="HighlightBrush" Color="{DynamicResource Primary700}"/>
            <SolidColorBrush x:Key="AccentColorBrush" Color="{DynamicResource Primary500}"/>
            <SolidColorBrush x:Key="AccentColorBrush2" Color="{DynamicResource Primary400}"/>
            <SolidColorBrush x:Key="AccentColorBrush3" Color="{DynamicResource Primary300}"/>
            <SolidColorBrush x:Key="AccentColorBrush4" Color="{DynamicResource Primary200}"/>
            <SolidColorBrush x:Key="WindowTitleColorBrush" Color="{DynamicResource Primary700}"/>
            <SolidColorBrush x:Key="AccentSelectedColorBrush" Color="{DynamicResource Primary500Foreground}"/>
            <LinearGradientBrush x:Key="ProgressBrush" EndPoint="0.001,0.5" StartPoint="1.002,0.5">
                <GradientStop Color="{DynamicResource Primary700}" Offset="0"/>
                <GradientStop Color="{DynamicResource Primary300}" Offset="1"/>
            </LinearGradientBrush>
            <SolidColorBrush x:Key="CheckmarkFill" Color="{DynamicResource Primary500}"/>
            <SolidColorBrush x:Key="RightArrowFill" Color="{DynamicResource Primary500}"/>
            <SolidColorBrush x:Key="IdealForegroundColorBrush" Color="{DynamicResource Primary500Foreground}"/>
            <SolidColorBrush x:Key="IdealForegroundDisabledBrush" Color="{DynamicResource Primary500}" Opacity="0.4"/>

        </ResourceDictionary>
    </Application.Resources>
</Application>
```

```xml:MainWindow.xaml
<metro:MetroWindow x:Class="WpfApplication1.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"
                   mc:Ignorable="d"
                   xmlns:materialDesign="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
                   xmlns:metro="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
                   TextElement.Foreground="{DynamicResource MaterialDesignBody}"
                   TextElement.FontWeight="Regular"
                   TextElement.FontSize="13"
                   TextOptions.TextFormattingMode="Ideal" 
                   TextOptions.TextRenderingMode="Auto"        
                   Background="{DynamicResource MaterialDesignPaper}"
                   FontFamily="{StaticResource MaterialDesignFont}"                      
                   GlowBrush="{DynamicResource AccentColorBrush}"
                   ResizeMode="CanResizeWithGrip"
                   TitleCaps="False"
                   Title="MainWindow"
                   Height="500" Width="600"
                   Loaded="Window_Loaded">
```

```csharp:MainWindow.xaml.cs
using System;
using System.Windows;
using MahApps.Metro.Controls;

namespace WpfApplication1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

    }
}
```

こうして出来た画面がこちら

![image](https://qiita-image-store.s3.amazonaws.com/0/177379/7d69f60d-7866-e37a-a97e-c1ec69240c7b.png)

ガラリと雰囲気が変わりますな。
TextBoxやButtonなどはコレで良い感じだけど、DataGridは業務アプリとしては見づらい気がします。
ここだけをギョーミーにしたい。

なので、DataGridのStyleをデフォルトのもので継承し直す定義を、App.xamlに書いてみます。

```xml:App.xaml
    <Application.Resources>
        <ResourceDictionary>

           （略）

            <!--
                =======================================================
                [Style]DataGrid用スタイル再定義　ここから
                =======================================================
            -->
            <!-- DataGridColumnHeader(列ヘッダ) Style設定 -->
            <Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"/>

            <!-- DataGridCell(セル) Style設定 -->
            <Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}"/>

            <!-- DataGrid Style設定 -->
            <Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
                <Setter Property="AutoGenerateColumns" Value="False"/>
                <Setter Property="CanUserAddRows" Value="False"/>
                <Setter Property="CanUserDeleteRows" Value="False"/>
            </Style>

            <!-- その他はデフォルト継承 -->
            <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}"/>
            <Style TargetType="DataGridRowHeader" BasedOn="{StaticResource {x:Type DataGridRowHeader}}" />
            <!--
                =======================================================
                [Style]DataGrid用スタイル再定義　ここまで
                =======================================================
            -->
            
        </ResourceDictionary>
    </Application.Resources>
```
こうすると、DataGrid だけが元の Win32 なスタイルに戻ります。

![image](https://qiita-image-store.s3.amazonaws.com/0/177379/07c72e78-43ca-0f0e-829d-42a12c74be9b.png)

ただねぇ、このヘッダのデザインはいただけません。
せっかく他の部品がフラット・マテリアルな感じなのに、DataGrid ヘッダだけ、一昔前の3Dなグラデーションです。
すごく野暮ったく見えます。

なわけで、DataGrid個々のパーツのStyleにも手を入れていきます。
ソートマークの付与や、マウスオーバーの色変化、列幅変更のUIなど、機能はそのまま使えるようにして。

#カスタムStyle適用
XAMLのStyle定義だけで試行錯誤した結果が、こちらになりました。

![image](https://qiita-image-store.s3.amazonaws.com/0/177379/5928c8e6-20fa-5bfe-4267-68b39ce6a621.png)


```xml:App.xaml
    <!--
        =======================================================
        [Style]DataGrid用スタイル再定義　ここから
        =======================================================
    -->
    <!-- 罫線の色 -->
    <Brush x:Key="DG_Brush_Border">#ffaaaaaa</Brush>
    <!-- 選択行の背景色 -->
    <Color x:Key="DG_Color_SelectedBackground">#337ab7</Color>
    <!-- マウスオーバー背景色 -->
    <Color x:Key="DG_Color_MouseOver">#dadada</Color>

    <!-- Thumb(列ヘッダ幅変更マウスカーソル) Style設定 -->
    <Style x:Key="columnHeaderGripperStyle" TargetType="{x:Type Thumb}">
        <Setter Property="Width" Value="8"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Cursor" Value="SizeWE"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- DataGridColumnHeader(列ヘッダ) Style設定 -->
    <Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
        <Setter Property="SeparatorBrush" Value="LightGray" />
        <Setter Property="FontWeight" Value="Normal" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                    <Grid>
                        <!-- マウスオーバで色が変わる設定 -->
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="columnHeaderBorder"
                                                                      Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
                                            <EasingColorKeyFrame KeyTime="0" Value="#fafafa" />
                                        </ColorAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>

                        <!-- ヘッダ の外観 -->
                        <Border x:Name="columnHeaderBorder"
                                BorderThickness="0 0 1 1"
                                Padding="3 0 3 0"
                                BorderBrush="{StaticResource DG_Brush_Border}"
                                Background="#ffdddddd">

                            <Grid>
                                <!-- ソートマーク -->
                                <Path x:Name="sortArrow"
                                      HorizontalAlignment="Center"
                                      VerticalAlignment="Top"
                                      Width="8" Height="6" Margin="0 2 0 0"
                                      Stretch="Fill" Fill="DarkGray"
                                      RenderTransformOrigin="0.5 0.4"
                                      Visibility="Collapsed"
                                      Data="M0,0 L1,0 0.5,1 z" />
                                <!-- ヘッダ文字列 -->
                                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            </Grid>
                        </Border>

                        <!-- サイズ変更グリップ -->
                        <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource columnHeaderGripperStyle}"/>
                        <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource columnHeaderGripperStyle}"/>
                    </Grid>

                    <!-- ソート trigger -->
                    <ControlTemplate.Triggers>
                        <Trigger Property="SortDirection" Value="Ascending">
                            <Setter TargetName="sortArrow" Property="Visibility" Value="Visible" />
                            <Setter TargetName="sortArrow" Property="RenderTransform">
                                <Setter.Value>
                                    <RotateTransform Angle="180" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                        <Trigger Property="SortDirection" Value="Descending">
                            <Setter TargetName="sortArrow" Property="Visibility" Value="Visible" />
                        </Trigger>
                    </ControlTemplate.Triggers>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- DataGridCell(セル) Style設定 -->
    <Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
        <Setter Property="Height" Value="24"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridCell}">
                    <Grid Background="{TemplateBinding Background}">
                        <ContentPresenter VerticalAlignment="Center" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- DataGrid Style設定 -->
    <Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
        <Setter Property="AutoGenerateColumns" Value="False"/>
        <Setter Property="CanUserAddRows" Value="False"/>
        <Setter Property="CanUserDeleteRows" Value="False"/>
        <Setter Property="Background" Value="{DynamicResource MaterialDesignPaper}"/>
        <Setter Property="BorderBrush" Value="{StaticResource DG_Brush_Border}"/>
        <Setter Property="BorderThickness" Value="1 1 1 1"/>
        <Setter Property="ColumnHeaderHeight" Value="35"/>
        <Setter Property="HeadersVisibility" Value="Column"/>
        <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource DG_Brush_Border}"/>
        <Setter Property="VerticalGridLinesBrush" Value="{StaticResource DG_Brush_Border}"/>
        <Setter Property="VirtualizingPanel.IsVirtualizing" Value="True"/>
        <Setter Property="VirtualizingPanel.VirtualizationMode" Value="Standard"/>
        <Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
    </Style>
    
    <!-- その他はデフォルト継承 -->
    <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}"/>
    <Style TargetType="DataGridRowHeader" BasedOn="{StaticResource {x:Type DataGridRowHeader}}" />
    <!--
        =======================================================
        [Style]DataGrid用スタイル再定義　ここまで
        =======================================================
    -->
```

なんかもう長すぎて、解らないところもあります。
多分余計な記述もあることでしょう。
ヘッダをフラットにして、罫線を細くするだけで、こんなに苦労するとは、思わなかったです。

まだ少し気に入らない部分もありますが、いちおうこれで完成と相成りました。

#参考ページ
[Material Design In XAML Toolkitでお手軽にWPFアプリを美しく](http://neue.cc/2015/09/10_515.html)
[MaterialDesignInXamlToolkit - Getting Started](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit/wiki/Getting-Started)