WPF DataGridを使ってみた

基本的なことはググるとすぐに見つかると思うが、いろんなやり方があるっぽい
データの形とかで長所短所があると思うのでいくつかまとめてみた

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

```csharp
    public class ViewModel
    {
        public Model _Model { get; } = new Model();
    }
    public class Model
    {
        public List<Goods> _Goods { get; set; } = new List<Goods>();
        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](https://qiita-image-store.s3.amazonaws.com/0/145898/05dd0e8e-8a0f-8efb-4017-450a9a6b6015.jpeg)


このとき、列のヘッダーはプロパティ名になる
また、列に表示されるコントロールはプロパティの型によって自動的に決定される
string型やint型はそのまま(テキスト)だが、bool型のものはチェックボックスに、enum型はドロップダウンが表示される

ただのリストを表示する時はこれだけでOK
しかし、これだと「リストの中でもこのプロパティは表示させたくない」とか、「このプロパティは◯列目に表示させたい」
とかが指定できない

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

【いいとこ】
　細かいとこも設定できる
【悪いとこ】
　めんどくさい・ややこしい

ここでは手動で列を指定するので、まずAutoGenerateColumnsをFalseにする
ItemsSourceへのバインドは同じ
そんでXaml
一気にめんどくさくなる...

```xml
<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](https://qiita-image-store.s3.amazonaws.com/0/145898/ba045709-7781-faaf-be9c-33f19223deff.jpeg)


DataGrid.Columns内で表示する列を定義していて、表示する列の種類毎でプロパティが違ったりする
各ColumnではHeaderでヘッダー名を設定できる
他にもStyleでいろいろいじったりもできる

###DataGridTextColumnとDataGridCheckBoxColumn

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

###DataGridComboBoxColumn

ドロップダウン表示のやつ
かなりめんどくさい
まず、表示させるために別のクラスが必要になる

```csharp
    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にイベントを追加する
このイベント内で設定をいじる

```csharp
        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](https://qiita-image-store.s3.amazonaws.com/0/145898/557fb4d3-a4cc-eb01-b2e4-a495944af697.jpeg)

####DataTableについて

Excelとかみたいな表形式でデータをいれれる
上の表のデータはこんな感じでセットしている

```csharp
    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は細かくデザインまでしようと思ったらかなりめんどくさいやつ
もう少しカスタムしやすかったらいいのにね
他にもあれば随時追加していくと思う