業務アプリでは一覧表示がよく行われます。
WindowsFormで開発をしていた頃、DataGridViewの1セルに複数の情報を表示する、いわゆる多段表示をしてほしいという要望を受ける事が多かったのですが、Visual Studioの標準機能では難易度が高く、結果的にコストの関係で諦めてもらうか、GrapeCity等のサードパーティ製DataGridコントロールを購入して使うなどの方法を良く使っていました。
WPFでは、簡単にDataGridのヘッダやセルにコントロールを登録することができるので、多段明細はそんなに難しくありません。
まあ、レイアウトは凝れば凝るほど難易度は上がってコストも掛かりますが、それほどレイアウトに凝らなければ、短期間で十分開発可能です。
今回は、DataGridのDataGridTemplateColumn を使って多段表示をしてみたので、その方法について解説したいと思います。
同じことをユーザーコントロールで実現する方法については、【WPF】ここまで出来る!DataGridで多段表示!(UserControl編)で詳しく紹介していますので、併せてご覧ください。
DataGridTemplateColumn を使ったDataGridの多段表示画面サンプル
今回紹介する多段表示のレイアウトは次の様になります。
全てXAML上で記述していて、DataTableのデータをBindingで表示しています。
WPFだとこのような画面を比較的簡単に作成することができます。
比較的簡単にと言っても、いきなりXAMLのソースコードを見ると、量が多くて圧倒されてしまうかもしれません。
そこで、あらかじめ図を使って、どのような構造になっているかの概要を理解しておきたいと思います。
DataGridのレイアウト構造(1段)
まず、一般的なDataGridのレイアウト構造を理解しておきましょう。
DataTableの内容を単純に表示するだけであれば、<DataGrid ItemSource="{Binding}" /> という簡単な記述だけで事が足りますが、例えばカラムのヘッダやセルのレイアウトを変更したい場合は、次の様にDataGrid.ColumnHeaderStyleやDataGrid.Columnsのタグにレイアウトを記述する必要があります。
ポイントとしては、DaDataGrid.COlumnsは、個々のカラムに対してスタイルが適応されるのですが、DataGrid.ColumnHeaderStyleは、全てのカラムのヘッダに適応されるという点です。
DataGrid.Columns を特に指定しない場合、デフォルトのレイアウトが適用されることになりますので、レイアウトを変更したい箇所のみ記述すればOKです。
では、実際のColumnHeaderStyleの記述方法ですが、次の様にSetter Property タグを使って、必要なプロパティに値を設定していきます。
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="MintCream"/>
</Style>
</DataGrid.ColumnHeaderStyle>
セルのスタイルを設定する場合は、DataGridTextColumn.ElementStyle タグを使います。
<DataGridTextColumn Header="日付" Width="100" Binding="{Binding Path=日付,StringFormat={}{0:yyyy年MM月dd日}}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
セルには、DataGridTextColumn以外に、コンボボックスやチェックボックスなど、いくつかのクラスが用意されています。
セルで使えるクラス名 | 機能 |
---|---|
DataGridTextColumn | セルにテキストボックスを表示する |
DataGridCheckBoxColumn | セルにチェックボックスを表示する |
DataGridComboBoxColumn | セルにコンボボックスを表示する |
DataGridHyperlinkColumn | セルにハイパーリンクを表示する |
DataGridTemplateColumn | セルに自分の好きなコントロールを配置して表示する |
DataGirdのレイアウト構造(多段)
多段の場合は、DataGridTemplateColumn というタグを使います。
DataGridTemplateColumn には、そのカラムのヘッダレイアウトと、セルのレイアウトを記述することができます。
今回のサンプル画面では1つのセルに3行を入れていますが、これは DataGridTemplateColumn.CellTemplate の中に TextBlock を3つ記述し、TextBlockとTextBlockの間をBorderで仕切っています。
<DataGridTemplateColumn>
<!-- カラムヘッダのレイアウト設定 -->
<DataGridTemplateColumn.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="メーカー"/>
<TextBlock Text="商品コード"/>
<TextBlock Text="商品名"/>
</StackPanel>
</DataGridTemplateColumn.Header>
<!-- セルのレイアウト設定 -->
<DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" Margin="0" VerticalAlignment="Center">
<TextBlock Text="{Binding メーカー}" Padding="5,0,5,0" />
<Border BorderThickness="0,0,0,1" BorderBrush="Gray"/>
<TextBlock Text="{Binding 商品コード}" Padding="5,0,5,0" />
<Border BorderThickness="0,0,0,1" BorderBrush="Gray"/>
<TextBlock Text="{Binding 商品名}" Padding="5,0,5,0" FontWeight="Bold"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
このように、一見複雑そうなXAMLのコードであっても、単純なブロックを積み重ねてレイアウトを構成しているだけに過ぎません。
以上の事を念頭に、次のXAMLのコードを見て頂ければ、やっている事が理解しやすいかと思います。
XAMLのコード
実際に多段表示を行うためのXAMLは次の様になります。
<Window x:Class="DataGridLayoutSample.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:DataGridLayoutSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<!-- DataGridの定義 -->
<DataGrid x:Name="uxDataGrid" HeadersVisibility="Column" AlternatingRowBackground="Aqua" CanUserAddRows="False" AutoGenerateColumns="False" ItemsSource="{Binding}" Margin="10,36,10,10">
<!-- DataGrid全体のヘッダスタイル設定 -->
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<!-- 日付項目の表示 -->
<DataGridTextColumn Header="日付" Width="100" Binding="{Binding Path=日付,StringFormat={}{0:yyyy年MM月dd日}}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<!-- メーカー、商品コード、商品名、の3段表示 -->
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="メーカー"/>
<TextBlock Text="商品コード"/>
<TextBlock Text="商品名"/>
</StackPanel>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" Margin="0" VerticalAlignment="Center">
<TextBlock Text="{Binding メーカー}" Padding="5,0,5,0" />
<Border BorderThickness="0,0,0,1" BorderBrush="Gray"/>
<TextBlock Text="{Binding 商品コード}" Padding="5,0,5,0" />
<Border BorderThickness="0,0,0,1" BorderBrush="Gray"/>
<TextBlock Text="{Binding 商品名}" Padding="5,0,5,0" FontWeight="Bold"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- 単価、数量の2段表示 -->
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="単価"/>
<TextBlock Text="数量"/>
</StackPanel>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="1"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding 単価,StringFormat={}{0:N}}" TextAlignment="Right" Padding="2"/>
<Border Grid.Row="1" BorderThickness="0,0,0,1" BorderBrush="Gray"/>
<TextBlock Grid.Row="2" Text="{Binding 数量}" TextAlignment="Right" Padding="2"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- 単価、数量の2段表示 -->
<DataGridTextColumn Header="合計金額" Width="100" Binding="{Binding Path=合計,StringFormat={}{0:N}}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<!-- センサー、画素数、サイズの2列2段表示-->
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="センサー/画素数"/>
<TextBlock Text="サイズ" HorizontalAlignment="Center"/>
</StackPanel>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="1"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="1"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding センサー}" Padding="5,0,5,0" />
<Border Grid.Column="1" BorderThickness="1,0,0,0" BorderBrush="Gray"/>
<TextBlock Grid.Column="2" Text="{Binding 画素数,StringFormat={}{0}万画素}" Padding="5,0,5,0" />
</Grid>
<Border Grid.Row="1" BorderThickness="0,0,0,1" BorderBrush="Gray"/>
<TextBlock Text="{Binding サイズ}" Grid.Row="2" TextAlignment="Center" Padding="2"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="uxShowData" Content="表示" HorizontalAlignment="Right" Height="21" Margin="0,8,17,0" VerticalAlignment="Top" Width="101" Click="uxShowData_Click"/>
<Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="0" Margin="944,121,-157,0" Stroke="Black" VerticalAlignment="Top" Width="5"/>
</Grid>
</Window>
C#のソースコード
C#のソースコードは次の通りです。
DataTable形式でサンプルデータを作成して、DataGridのDataContextプロパティにセットしているだけのシンプルなプログラムです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data;
namespace DataGridLayoutSample
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// 表示ボタンがクリックされたときの処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxShowData_Click(object sender, RoutedEventArgs e)
{
uxDataGrid.DataContext = CreateDataTable();
}
/// <summary>
/// 表示用のサンプルデータを作成する
/// </summary>
/// <returns></returns>
private DataTable CreateDataTable()
{
var dt = new DataTable();
dt.Columns.Add("日付",typeof(DateTime));
dt.Columns.Add("メーカー");
dt.Columns.Add("商品コード");
dt.Columns.Add("商品名");
dt.Columns.Add("センサー");
dt.Columns.Add("画素数");
dt.Columns.Add("サイズ");
dt.Columns.Add("単価",typeof(int));
dt.Columns.Add("数量",typeof(int));
dt.Columns.Add("合計",typeof(int));
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/10/11", "キャノン", "4147C001", "EOS R6000", "Full","2400","15x20x80",150000, 3 ,450000});
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/10/12", "キャノン", "4147C012", "EOS R5000", "Full", "3000", "20x15x60", 160000, 6 ,960000});
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/11/15", "ニコン", "4960759127693", "COOLPIX S9999", "1/2.3型", "2400", "16x10x45", 20000, 1 ,20000});
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/11/23", "ニコン", "4960759127716", "COOLPIX S6666", "1/2.3型", "2400", "10x12x40", 20000, 4,80000 });
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/11/09", "SONY", "S26347C001", "Cyber-shotHX444V", "1/2.3型", "2400", "20x15x30", 123000, 8,984000 });
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/12/01", "SONY", "S30447C004", "Cyber-shotWX777", "1型", "2400", "20x15x30", 45000, 12 ,540000});
dt.Rows.Add(dt.NewRow().ItemArray = new object[] { "2020/12/11", "富士フィルム", "FJ55505", "FUJIFILM X-S111", "Full", "2400", "18x20x30", 120000, 3,360000 });
return dt;
}
}
}
まとめ
今回は、WPFのDataGridについて、多段表示の方法について解説しました。
他にも様々な方法があると思いますが、構造を理解しておけば、コード量は多くはなりますが、それほど難易度は高くないと思います。
DataGridTemplateColumn にStackPanelやGridなどのレイアウト系コントロールを乗せて、その上に様々なコントロールを配置してきます。
こうすることによって、1つのセルの中に複数の項目を表示することができるようになります。
1つのセルに複数項目を表示する場合、Borderを使って区切り線を表現してみましたが、レイアウトのデザインに応じて色々なアプローチがあると思いますので、これに拘らずに色々な方法を試してみて下さい。
コメント