WPF DataGridでDataTableを行ヘッダー付きで表示する



#作るもの
DataGridの列ヘッダーをカレンダーみたいに日付表示させて
行ヘッダーに人名、セルに○とかを表示させるものを考えた

![2017-05-19_20h33_55.png](https://qiita-image-store.s3.amazonaws.com/0/145898/50ddb1a5-805e-3899-5e8b-aa4c7b601f40.png)

##Model

まずModel

```csharp
public class Model
    {
        public DataTable _DataTable { get; set; } = new DataTable();
        public int _Year { get; set; } = 2017;
        public int _Month { get; set; } = 1;

        public Model()
        {
            string[] lName = {"伊藤","山本","佐藤","鈴木","佐々木","田中","宮本","神谷","池田","岡本" };
            string[] fName = {"大輔","太郎","正和","一郎","聡","豊","徹","涼介","人志","将人" };
            string[] State = { "-", "△", "◯", "✕" };

            Random random = new Random();

            _DataTable.Columns.Add("RowHeader");
            for (int i = 1; i < DateTime.DaysInMonth(_Year,_Month); i++)
            {
                _DataTable.Columns.Add(i.ToString());
            }

            for (int i = 0; i < 20; i++)
            {
                var row = _DataTable.NewRow();
                for (int j = 0; j < _DataTable.Columns.Count; j++)
                {
                    if (j == 0)
                    {
                        row[j] = lName[random.Next(lName.Length)] + fName[random.Next(fName.Length)];
                    }
                    else
                    {
                        row[j] = State[random.Next(State.Length)];
                    }
                }
                _DataTable.Rows.Add(row);
            }
        }
    }
```

DataTableの最初の列には人名を入れるため
`_DataTable.Columns.Add("RowHeader");`としている
あとは指定年月の日数分DataTableに列を追加している
んでRowとCellには配列からランダムに値を入れている
サンプルとかでもこういう遊びがほしくなる('ω')

##ViewModel

いつものViewModelだよ

```csharp
    public class ViewModel
    {
        public Model _Model { get; } = new Model();
    }
```

##View
ここがなかなかややこしいポイント

```xml
        <DataGrid x:Name="myDataGrid" ItemsSource="{Binding _Model._DataTable}" CanUserReorderColumns="False" CanUserSortColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" AutoGeneratingColumn="myDataGrid_AutoGeneratingColumn">
            <DataGrid.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="TextBlock.TextAlignment" Value="Center"/>
                </Style>
            </DataGrid.CellStyle>
            <DataGrid.ColumnHeaderStyle>
                <Style TargetType="{x:Type DataGridColumnHeader}">
                    <Setter Property="HorizontalContentAlignment" Value="Center"/>
                </Style>
            </DataGrid.ColumnHeaderStyle>
            <DataGrid.RowHeaderStyle>
                <Style TargetType="{x:Type DataGridRowHeader}">
                    <Setter Property="Content" Value="{Binding [0]}"/>
                </Style>
            </DataGrid.RowHeaderStyle>
        </DataGrid>
```

まずDataGrid.CellStyle
ここではStyleでCellないのTextが中央に表示されるようにしている
列の幅を変えても中央に表示されるよ(ﾟ∀ﾟ)
んでDataGrid.ColumnHeaderStyle
ここでも設定しているのはセルと同じこと
ヘッダー内の文字が中央にくるようにしている
最後にDataGrid.RowHeaderStyle
これがキモ
Styleを使ってDataGridRowHeaderに渡されるデータの最初の列をバインドしている

しかし、AutoGenerateColumnsがTrueなのでこれを実行するとこうなる

![2017-05-19_21h11_17.png](https://qiita-image-store.s3.amazonaws.com/0/145898/97c348cb-cd06-43ae-4934-c21ea993f931.png)

まあ当然だよね
そこで、AutoGeneratingColumnイベントで1列目の列生成だけキャンセルすることにする

```csharp
        private void myDataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            if (e.PropertyName == "RowHeader")
            {
                e.Cancel = true;
            }
        }
```

これもcounterみたいなのを作ってindexで判定すると汎用性が上がると思う

#まとめ
最初はDataGridに行列の数が不確定なコレクションを表示させたくていろいろ調べた結果こうなった
これだとDataTable側をいじればDataGrid側をいじる必要がなくなるから便利
あとはReactivePropertyとか使ってデータの操作とかもしてみたい

##参考
[WPF Notes/DataGridの行ヘッダに行番号を表示](http://morio2.blogspot.jp/2012/12/datagrid.html)