#はじめに
せっかくWPFでアプリを作るなら、旧来のWin32を引きずったデザインではなく、モダンな見た目で作りたいなと思うわけです。
たとえそれが、しがない業務アプリだとしても・・・
neue ccさんのブログを参考に、Material Design In XAML Toolkitを入れてみると、確かにテンション上がります。
見た目が変わるだけで、素晴らしいアプリに見えてくる!
(全体のイメージは本家スクリーンショットを参照)
ただ、DataGridだけは、カッコ良すぎるというか、業務アプリとしては使いづらいかなと思うわけで。
そんなDataGridのデザイン(Style)を、アレコレ悩んで 弄ったときの記録です。
###環境
.NET Framework 4.6.1
MaterialDesignThemes 2.3.0.823
MahApps.Metro 1.5.0
#素のフォーム
サンプルとして、TextBox, ComboBox, Button と DataGrid を並べたフォームを用意しました。
neue ccさん的に言う、実にギョーミーな画面。
グリッドのデータは、なんちゃって個人情報から拝借しました。
#Material Design In XAML Toolkit導入
こいつに、Material Design In XAML Toolkit と、MahApps.Metro を適用します。
どちらもNuGetで入れました。
App.xaml にStyle定義を行い、Window は MahApps.Metro.Controls.MetroWindow を継承します。
<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>
<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">
using System;
using System.Windows;
using MahApps.Metro.Controls;
namespace WpfApplication1
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : MetroWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}
こうして出来た画面がこちら
ガラリと雰囲気が変わりますな。
TextBoxやButtonなどはコレで良い感じだけど、DataGridは業務アプリとしては見づらい気がします。
ここだけをギョーミーにしたい。
なので、DataGridのStyleをデフォルトのもので継承し直す定義を、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 なスタイルに戻ります。
ただねぇ、このヘッダのデザインはいただけません。
せっかく他の部品がフラット・マテリアルな感じなのに、DataGrid ヘッダだけ、一昔前の3Dなグラデーションです。
すごく野暮ったく見えます。
なわけで、DataGrid個々のパーツのStyleにも手を入れていきます。
ソートマークの付与や、マウスオーバーの色変化、列幅変更のUIなど、機能はそのまま使えるようにして。
#カスタムStyle適用
XAMLのStyle定義だけで試行錯誤した結果が、こちらになりました。
<!--
=======================================================
[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アプリを美しく
MaterialDesignInXamlToolkit - Getting Started