はじめに
前回の記事で Textbox のスタイルのカスタマイズ方法について解説しました。
今回は DataGrid のスタイルをカスタマイズしていきます。
アプリケーションの完成イメージです。
前回同様、リソースファイルを作成してスタイルをカスタマイズします。
リソースを作成
Resources フォルダにDataGridStyle.xaml
を作成します。
ビジュアルデザイナより DataGrid のスタイルのテンプレートをリソースディクショナリへコピーします。
次に追加テンプレートの編集よりスタイルをカスタマイズする要素を選択します。
今回は以下のスタイル要素にフォーカスを当てて解説します。
- CellStyle
- RowStyle
CellStyle
CellStyle
では各セルの外観をカスタマイズします。以下のように XAML を指定します。
前回の記事では出てこなかったプロパティに絞って設定している内容を見ていこうと思います。
<!--#region DataGridCellStyle-->
<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="VerticalAlignment" Value="Center" />
<!-- セルがフォーカスを受け取ったときのスタイルを無効にする -->
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="15,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}"
BorderThickness="0"
SnapsToDevicePixels="True">
<!-- TextBlockがフォーカスを受け取らないように設定する -->
<TextBlock Focusable="False">
<!-- ContentPresenter: コントロールのコンテンツを表示するために使用される -->
<!-- DataGridCell の SnapsToDevicePixels プロパティにバインドする -->
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- トリガーの設定 -->
<Style.Triggers>
<!-- 選択されたときのスタイルを定義 -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
<!--#endregion-->
Border
で指定しているSnapsToDevicePixels="True"
というプロパティですが、WPF のレンダリングエンジンがピクセルスナップを行うかどうかを制御するために使用されます。
ピクセルスナップとは、レンダリングされた要素がデバイスの物理ピクセルに正確に一致するように調整されることを意味します。
これにより、視覚的にシャープでクリアな表示が可能になります。
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
でバインディングしているプロパティですが、これは DataGridCell の SnapsToDevicePixels プロパティにバインドしています。
Border
で設定されているSnapsToDevicePixels
プロパティの値にバインディングされているのではありません。
RowStyle
次にRowStyle
の内容を見ていきます。
<!--#region DataGridRowStyle-->
<Style x:Key="DataGridRowStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="White" />
<Setter Property="Margin" Value="0,4,0,0" />
<!-- 要素をホバーさせたときのマウスカーソルを手のアイコンに設定 -->
<Setter Property="Cursor" Value="Hand" />
<!-- ピクセルスナップを有効化 -->
<Setter Property="SnapsToDevicePixels" Value="true" />
<!-- バリデーションエラーのテンプレートを無効にする -->
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<!-- バリデーションエラーが発生した場合に表示されるテンプレートを定義 -->
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate>
<!-- 赤い感嘆符("!")を表示させる -->
<TextBlock Margin="2,0,0,0"
VerticalAlignment="Center"
Foreground="Red"
Text="!" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<!-- DataGridRow のカスタムテンプレートを定義 -->
<ControlTemplate TargetType="{x:Type DataGridRow}">
<Border x:Name="DGR_Border"
Padding="0,8"
Background="{TemplateBinding Background}"
BorderThickness="0"
CornerRadius="5"
SnapsToDevicePixels="True">
<!-- スクロールの動作を制御するためのグリッド -->
<SelectiveScrollingGrid>
<SelectiveScrollingGrid.ColumnDefinitions>
<!-- その列に含まれるコンテンツの幅に自動的に調整される -->
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</SelectiveScrollingGrid.ColumnDefinitions>
<SelectiveScrollingGrid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</SelectiveScrollingGrid.RowDefinitions>
<!-- セルの内容を表示する -->
<DataGridCellsPresenter Grid.Column="1"
ItemsPanel="{TemplateBinding ItemsPanel}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<!-- 行の詳細情報を表示する -->
<DataGridDetailsPresenter Grid.Row="1"
Grid.Column="1"
SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
Visibility="{TemplateBinding DetailsVisibility}" />
<!-- 行ヘッダーを表示する -->
<DataGridRowHeader Grid.RowSpan="2"
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
</SelectiveScrollingGrid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- トリガーの設定 -->
<Style.Triggers>
<!-- マウスホバー時のスタイル -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#d9efff" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<!-- 行が選択されているときのスタイル -->
<Trigger Property="IsSelected" Value="True">
<!-- 選択状態の背景色を設定 -->
<Setter Property="Background" Value="#d9efff" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<MultiTrigger>
<!-- スタイルを適応する場合の条件を指定する -->
<MultiTrigger.Conditions>
<!-- 行が選択されている状態 -->
<Condition Property="IsSelected" Value="True" />
<!-- 選択がアクティブでない状態(DataGridがフォーカスを持っていない状態) -->
<Condition Property="Selector.IsSelectionActive" Value="False" />
</MultiTrigger.Conditions>v
<!-- 上記の条件が満たされた場合にスタイルが設定される -->
<Setter Property="Background" Value="#dfdfdf" />
</MultiTrigger>
</Style.Triggers>
</Style>
<!--#endregion-->
大体コメントに書いた通りの動作なのですが、RelativeSource
で指定されている内容について見ていきます。
まずRelativeSource
とはバインディングのソースを相対的な位置にある要素に設定するために使用されます。
相対的な位置とは自分から見たその他の位置という意味で、ここで言うとDataGridDetailsPresenter
要素から見たその他の要素ということです。
AncestorType
は特定の祖先要素を指定するためのプロパティで、ここではAncestorType={x:Type DataGrid}
となっているためDataGrid
要素まで遡って参照するという設定になります。
また、RelativeSource={RelativeSource AncestorType={x:Type DataGrid}, Mode=FindAncestor}
このようにMode
を指定することが出来ます。
RelativeSource クラスの実装を確認すると、Mode
を指定しなかった場合は初期値としてFindAncestor
が指定されます。
因みにRelativeSource={RelativeSource AncestorType={x:Type DataGrid}
のような記法のことをマークアップ拡張機能と言います。
XAML の記述を簡単にしたもので、反対に複雑なオブジェクトやネストされた要素を記述する際にはオブジェクト要素構文という記述で書きます。
この書き方のほうがよく見かけるかもしれません。
マークアップ拡張機能で記述された XAML 要素をオブジェクト要素構文で書くと以下のようになります。
<!--#region DataGridRowStyle-->
<!-- 省略 -->
<!--<DataGridDetailsPresenter Grid.Row="1"
Grid.Column="1"
SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
Visibility="{TemplateBinding DetailsVisibility}" />-->
<DataGridDetailsPresenter Grid.Row="1" Grid.Column="1">
<DataGridDetailsPresenter.Visibility>
<TemplateBinding Property="DetailsVisibility" />
</DataGridDetailsPresenter.Visibility>
<SelectiveScrollingGrid.SelectiveScrollingOrientation>
<Binding Converter="{x:Static DataGrid.RowDetailsScrollingConverter}"
ConverterParameter="{x:Static SelectiveScrollingOrientation.Vertical}"
Path="AreRowDetailsFrozen">
<Binding.RelativeSource>
<RelativeSource AncestorType="{x:Type DataGrid}" />
</Binding.RelativeSource>
</Binding>
</SelectiveScrollingGrid.SelectiveScrollingOrientation>
</DataGridDetailsPresenter>
<!--#endregion-->
その他のスタイル
今まで見てきたスタイルの指定方法と大体同じなので、少しコードが長いですが一気に記載します。
<!--#region DataGridStyle-->
<Style x:Key="DataGridStyle" TargetType="{x:Type DataGrid}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#222528" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" />
<Setter Property="ScrollViewer.CanContentScroll" Value="true" />
<Setter Property="ScrollViewer.PanningMode" Value="Both" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="Margin" Value="0,10,0,0" />
<Setter Property="SelectionMode" Value="Single" />
<Setter Property="SelectionUnit" Value="FullRow" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="CanUserDeleteRows" Value="False" />
<Setter Property="CanUserResizeRows" Value="False" />
<Setter Property="CanUserReorderColumns" Value="False" />
<Setter Property="FontSize" Value="13" />
<Setter Property="ColumnWidth" Value="*" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
Command="{x:Static DataGrid.SelectAllCommand}"
Focusable="false"
Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle,
TypeInTargetAssembly={x:Type DataGrid}}}"
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter"
Grid.Column="1"
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
Grid.Row="1"
Grid.ColumnSpan="2"
CanContentScroll="{TemplateBinding CanContentScroll}" />
<ScrollBar x:Name="PART_VerticalScrollBar"
Grid.Row="1"
Grid.Column="2"
Maximum="{TemplateBinding ScrollableHeight}"
Orientation="Vertical"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ScrollBar x:Name="PART_HorizontalScrollBar"
Grid.Column="1"
Maximum="{TemplateBinding ScrollableWidth}"
Orientation="Horizontal"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true" />
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</MultiTrigger>
</Style.Triggers>
</Style>
<!--#endregion-->
<!--#region ColumnHeaderGripperStyle-->
<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 Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--#endregion-->
<!--#region ComponentResourceKey-->
<Style x:Key="{ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="Border"
Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
SnapsToDevicePixels="True" />
<Polygon x:Name="Arrow"
Margin="8,8,3,3"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Fill="Black"
Opacity="0.15"
Points="0,10 10,10 10,0"
Stretch="Uniform" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Stroke" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Fill" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Arrow" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--#endregion-->
<!--#region DataGridColumnHeaderStyle-->
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#69717d" />
<Setter Property="Margin" Value="15,0,15,10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<theme:DataGridHeaderBorder Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsClickable="{TemplateBinding CanUserSort}"
IsHovered="{TemplateBinding IsMouseOver}"
IsPressed="{TemplateBinding IsPressed}"
SeparatorBrush="{TemplateBinding SeparatorBrush}"
SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
SortDirection="{TemplateBinding SortDirection}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</theme:DataGridHeaderBorder>
<Thumb x:Name="PART_LeftHeaderGripper"
HorizontalAlignment="Left"
Style="{StaticResource ColumnHeaderGripperStyle}" />
<Thumb x:Name="PART_RightHeaderGripper"
HorizontalAlignment="Right"
Style="{StaticResource ColumnHeaderGripperStyle}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--#endregion-->
<!--#region gridEditButton-->
<Style x:Key="gridEditButton" TargetType="Button">
<Setter Property="Background" Value="#1D5C96" />
<Setter Property="BorderBrush" Value="#1D5C96" />
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Padding="5"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#7DB0DE" />
</Trigger>
</Style.Triggers>
</Style>
<!--#endregion-->
<!--#region gridRemoveButton-->
<Style x:Key="gridRemoveButton"
BasedOn="{StaticResource gridEditButton}"
TargetType="Button">
<Setter Property="Background" Value="#d32f2f" />
<Setter Property="BorderBrush" Value="#b71c1c" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#f44336" />
</Trigger>
</Style.Triggers>
</Style>
<!--#endregion-->
<!--#region gridButtonIcon-->
<Style x:Key="gridButtonIcon" TargetType="Icon:PackIconMaterial">
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<!--#endregion-->
MainWindow.xaml
に今回作成したスタイルの適応を確認します。ViewModel やデータの取得方法については割愛します。
MVVM で DataGrid の扱い方については以下の記事で解説しています。
<!-- 省略 -->
<DataGrid x:Name="membersDataGrid"
Grid.Row="1"
CellStyle="{DynamicResource DataGridCellStyle}"
ColumnHeaderStyle="{DynamicResource DataGridColumnHeaderStyle}"
ItemsSource="{Binding FilterMemberData}"
RowStyle="{DynamicResource DataGridRowStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
Style="{DynamicResource DataGridStyle}">
<DataGrid.Columns>
<!--#region チェックボックス-->
<DataGridCheckBoxColumn Width="Auto"
CanUserResize="False"
ElementStyle="{StaticResource CheckBoxStyle1}">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Style="{StaticResource CheckBoxStyle1}" />
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
</DataGridCheckBoxColumn>
<!--#endregion-->
<!--#region IDカラム-->
<DataGridTemplateColumn Width="Auto"
CanUserResize="False"
Header="#"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,0,1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Id}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--#endregion-->
<!--#region 氏名カラム-->
<DataGridTemplateColumn Width="Auto"
Header="氏名"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,0,1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--#endregion-->
<DataGridTextColumn Width="Auto"
Binding="{Binding Age}"
Header="年齢"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding Gender}"
Header="性別"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding Email}"
Header="メールアドレス"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding PhoneNumber}"
Header="電話番号"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding PostalCode.Value}"
Header="郵便番号"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding Address}"
Header="住所"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding CompanyName}"
Header="会社名"
IsReadOnly="True" />
<DataGridTextColumn Width="Auto"
Binding="{Binding CreatedAt.DisplayJapaneseEraFullFromat}"
Header="登録日"
IsReadOnly="True" />
<DataGridTemplateColumn Width="Auto"
Header="編集"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource gridEditButton}">
<Icon:PackIconMaterial Kind="PencilOutline" Style="{StaticResource gridButtonIcon}" />
</Button>
<Button Margin="5,0,0,0" Style="{StaticResource gridRemoveButton}">
<Icon:PackIconMaterial Kind="DeleteOutline" Style="{StaticResource gridButtonIcon}" />
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ここまで実装したらビルドして動作を確認します。
その他に Window のスタイルをカスタマイズしている箇所もありますが割愛します。
今回作成したアプリケーションのGitHub リポジトリはこちらです。