C#
WPF
VisualStudio

WPF DataGridを使ってみた

More than 1 year has passed since last update.

基本的なことはググるとすぐに見つかると思うが、いろんなやり方があるっぽい

データの形とかで長所短所があると思うのでいくつかまとめてみた

こんな感じのデータを例にやってみる

    public class ViewModel

{
public Model _Model { get; } = new Model();
}
public class Model
{
public List<Person> _Persons { get; set; } = new List<Person>();
public Model()
{
for (int i = 0; i < 10; i++)
{
_Goods.Add(new Goods()
{
_Name ="商品" + i,
_Price = i * 1000,
_isAvailable = (i % 2 == 1) ? true : false,
_Vender = Vendor.取引先A,
});
}
}
}
public class Goods
{
public string _Name { get; set; }
public int _Price { get; set; }
public bool _isAvailable { get; set; }
public Vendor _Vender { get; set; }
}
public enum Vendor
{
取引先A,
取引先B,
取引先C,
取引先D,
}


やってみる


0.DataGridについて

まず、DataGridのAutoGenerateColumnsをTrueにする(デフォルトはTrue)

このAutoGenerateColumnsは列の自動生成機能のことで、こいつをTrueにしとくとItemsSourceに指定したコレクションから

自動でいい感じに列を作ってくれる


1.自動でおまかせ

【いいとこ】

 簡単

【悪いとこ】

 細かい設定ができない

AutoGenerateColumnsがTrueになっているDataGridのItemsSourceにコレクションデータをバインドするだけ

<DataGrid ItemsSource="{Binding _Model._Goods}">

2017-05-19_18h00_49.jpg

このとき、列のヘッダーはプロパティ名になる

また、列に表示されるコントロールはプロパティの型によって自動的に決定される

string型やint型はそのまま(テキスト)だが、bool型のものはチェックボックスに、enum型はドロップダウンが表示される

ただのリストを表示する時はこれだけでOK

しかし、これだと「リストの中でもこのプロパティは表示させたくない」とか、「このプロパティは◯列目に表示させたい」

とかが指定できない


2.列にプロパティを指定する

【いいとこ】

 細かいとこも設定できる

【悪いとこ】

 めんどくさい・ややこしい

ここでは手動で列を指定するので、まずAutoGenerateColumnsをFalseにする

ItemsSourceへのバインドは同じ

そんでXaml

一気にめんどくさくなる...

<DataGrid ItemsSource="{Binding _Model._Goods}" AutoGenerateColumns="False">

<DataGrid.Columns>
<DataGridTextColumn Header="商品名" Binding="{Binding _Name}"/>
<DataGridComboBoxColumn Header="仕入先" SelectedValueBinding="{Binding _Vender}" DisplayMemberPath="_Label" SelectedValuePath="_Value">
<DataGridComboBoxColumn.ItemsSource>
<x:Array Type="{x:Type local:VenderToComboBox}">
<local:VenderToComboBox _Label="取引先1" _Value="取引先A"/>
<local:VenderToComboBox _Label="取引先2" _Value="取引先B"/>
<local:VenderToComboBox _Label="取引先3" _Value="取引先C"/>
<local:VenderToComboBox _Label="取引先4" _Value="取引先D"/>
</x:Array>
</DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>
<DataGridCheckBoxColumn Header="入荷済" Binding="{Binding _isAvailable}"/>
</DataGrid.Columns>
</DataGrid>

こんな感じになるよ

2017-05-19_15h53_26.jpg

DataGrid.Columns内で表示する列を定義していて、表示する列の種類毎でプロパティが違ったりする

各ColumnではHeaderでヘッダー名を設定できる

他にもStyleでいろいろいじったりもできる


DataGridTextColumnとDataGridCheckBoxColumn

これは単純で、Bindingに設定したいプロパティをバインドするだけ


DataGridComboBoxColumn

ドロップダウン表示のやつ

かなりめんどくさい

まず、表示させるために別のクラスが必要になる

    public class VenderToComboBox

{
public string _Label { get; set; }
public Vendor _Value { get; set; }
}

こんな感じで表示させる値とenumが保持できるプロパティをもってればOK

そんで以下のプロパティを定義

・SelectedValueBinding

  現在選択中の項目のプロパティで、データ側のenumプロパティをバインドする

・DisplayMemberPath

  表示するオブジェクトのソースへのパスで、表示用クラスの表示させる値のプロパティ名を定義する

・SelectedValuePath

  ドロップダウンリストに表示するコレクションへのパスで、表示用クラスのenumを保持するプロパティ名を定義する

・ItemsSource

  ドロップダウンリストに表示するオブジェクトのソースで、表示用クラスの配列を持つものを定義する

  x:ArrayはTypeで指定したものをXamlで配列として定義するもの


DataGridTemplateColumn

セルの表示とかも自由にカスタムできる

2段表示とかボタン表示とかもできるけどそのぶんややこしくてわかりづらい

今回は省略するが、需要があればそのうち書くかも


DataGridHyperlinkColumn

ハイパーリンクが表示できるやつ

これもBindにプロパティをバインドすればいいだけっぽい

あとはElementStyleでクリックされたときのイベントをハンドルするっぽい

使ったことがないのでs(ry


3.コードで生成

【いいとこ】

 そこそこ簡単で細かく設定できる

【悪いとこ】

 いっぱい設定してたらコードが長くなりがち

 コードで固めていくと汎用性が低くなる

1.で列の位置の指定ができないとか書いたけど、実はDataGridのAutoGeneratingColumnイベントでコード側からいじったりできる

ItemsSourceはそのままに、DataGridのAutoGeneratingColumnにイベントを追加する

このイベント内で設定をいじる

        private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)

{
switch (e.PropertyName)
{
case "_Name":
e.Column.Header = "商品名";
e.Column.DisplayIndex = 0;
break;
case "_Price":
e.Cancel = true;
break;
case "_isAvailable":
e.Column.Header = "入荷済";
e.Column.DisplayIndex = 1;
break;
case "_Vender":
e.Column.Header = "仕入先";
e.Column.DisplayIndex = 2;
break;
default:
break;
}
}

こんな感じで2と同じ表示ができる

eには列生成時のイベントデータが流れてくる

e.PropertyNameでプロパティ名を取得できるので、それを判定して処理を分岐させている

あとは好きにプロパティを設定すればOK

だが、PropertyNameで判定するとプロパティ名が変わったときに変更させるのがめんどくさくなる

indexとか取れればいいけどそんなプロパティはない

ちなみに、e.Column.DisplayIndexは流れてきた時点では-1が設定されているので使えない

そこで、汎用性を上げれるよう少し考えてみた

private int counter = 0;

とかでint型の変数を作る

そんでAutoGeneratingColumの度にcounterをインクリメントしてそれを判定すればOK

これならプロパティの順番に流れてくるので変更はしやすいはず

あと、AutoGeneratingColumの終了後にはAutoGeneratedColumnsが呼ばれるので、そこでcounterを初期化する


4.DataTableを使う

【いいとこ】

 表形式のデータを表示するのが楽

【悪いとこ】

 データの整形が少しめんどい

 Xamlから特定列の表示をいじったりできない

DataTableをItemsSourceにすれば表形式のデータも簡単にセットできる

2017-05-19_18h23_05.jpg


DataTableについて

Excelとかみたいな表形式でデータをいれれる

上の表のデータはこんな感じでセットしている

    public class Model_DataTable

{
public DataTable _DataTable { get; set; } = new DataTable();
public Model_DataTable()
{
for (int i = 0; i < 10; i++)
{
_DataTable.Columns.Add(i + "列目");
}
for(int i = 0; i < 10; i++)
{
var row = _DataTable.NewRow();
foreach (DataColumn col in _DataTable.Columns)
{
row[col] = col.ColumnName + "-" + i + "行目";
}
_DataTable.Rows.Add(row);
}
}
}

_DataTable.Columns.Addで列を追加している

ここで第2引数にTypeで型を指定すると型指定の列ができる

var row = _DataTable.NewRow();で_DataTableに属する新しい行を作っている

rowには[]でindexか列を指定してデータを入れる

_DataTable.Rows.Add(row);でテーブルに行を追加する

そんでXamlはこのプロパティをItemsSourceにバインドするだけ

簡単でいいね(/・ω・)/


まとめ

DataGridは細かくデザインまでしようと思ったらかなりめんどくさいやつ

もう少しカスタムしやすかったらいいのにね

他にもあれば随時追加していくと思う