[WPF]DataGrid.RowHeight指定のいろいろ

WPF DataGridのレイアウト設定はとかく複雑でわやよ。

今回は行の高さ指定に関する話。
DataGridには行の高さを指定できるプロパティがいくつもあるので
その適用の優先順位とか知っておかないと、
設定してるのに変わってくれないぞ??ってハマることになる。

<DataGrid RowHeight="20"><!-- (A) -->
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <!-- (B) -->
            <Setter Property="Height" Value="30" />
        </Style>
    </DataGrid.RowStyle>
    <DataGrid.CellStyle>
        <Style TargetType="DataGridCell">
            <!-- (C1) -->
            <Setter Property="Height" Value="40" />
        </Style>
    </DataGrid.CellStyle>

    <DataGrid.Columns>
        <DataGridTextColumn Header="列1">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="DataGridCell">
                    <!-- (C2) -->
                    <Setter Property="Height" Value="50" />
                </Style>
            </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

(A) DataGrid.RowHeightプロパティ
(B) DataGridRow.Heightプロパティ
(C1) DataGridCell.Heightプロパティ(DataGrid.CellStyleで指定)
(C2) DataGridCell.Heightプロパティ(DataGridColumn.CellStyleで指定)
 

以下、検証環境は.NET Framework 4.5.2 & Windows 8.1。

まずAとBの優先順について、MSDN「DataGrid.RowHeightプロパティ」には

RowHeight プロパティは、Height プロパティが設定されていない各 DataGridRow に適用されます

とあって、単純にAよりBを優先して採用しますって話に読めるんだけど、
そういう動きにはなっていない。

実際上記コードのようにAに20、Bに30を指定して(C1,C2は削除して)表示してみると
各行のスペースは30(Bの分)あるが、コンテンツ表示領域は20(Aの分)しか無いような
見え方(↓)になる。

Aに20,Bに30を指定したDataGrid

逆にBを20、Aを30にしてやると、行幅は20しかないが、コンテンツ自体は
30の高さを持っているらしく、行間の罫線が見えなくなる。

この見え方から推測するに、DataGridRow.Height(B)で行の外枠を作ってから、
中に表示するコンテンツ(DataGridTextColumnならTextBlockだ)の配置領域を
DataGrid.RowHeight(A)で決めてるんじゃないかなと。

なので、DataGrid.RowHeightをいくら拡張しても
DataGridRow.Heightの値を大きくしないと表示領域は広くならない。

表示内容にあわせて自動で行高が拡張されるようにしたい場合は
DataGridRow.HeightとDataGrid.RowHeightの両方をAuto(既定値)に設定する必要がある。
 

ついでにいうと、RowHeightプロパティについて、MSDNには

コンテンツに合わせて行のサイズを自動調整するには、このプロパティを NaN (XAML では “Auto”) に設定します

とあるが、これも嘘だ。

Aのとこに「RowHeight=”Auto”」と書いてみると、ビルドエラーになる(B,Cは可能)。
RowHeightに明示的にAutoを指定したいときは、XAMLから書く時もDouble.NaNで書く必要がある。

<Window x:Class="DataGridRowHeightSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <DataGrid RowHeight="{x:Static sys:Double.NaN}">
        ...
    </DataGrid>
</Window>

System名前空間の定数は↑こんなふうに指定しますよ。
なかなかこういうのが書けそうで書けない、実は書けるのがXAML。
 

C1とC2の優先順については、C2が設定されている列についてはC2が優先される。
これはまあ違和感ないと思う。

A,BとCの関係については、まず、A,Bによって行自体の高さが決められてから、
その中で各列に配置されるセルの高さがCによって規定されるイメージっぽい。
Cにいくら大きな値を設定しても、A,Bで決められた値以上に
表示領域が拡張されることはない。

あんまり使い道ないと思うが、C2を使えば列ごとにセルの高さを変えることもできる。
先の例のようにAに20、Bに30を指定した状態で、
列2にだけDataGridCell.Height=”10″を指定してやると↓こんな感じ。

Aに20,Bに30,列2のC2に10を指定したDataGrid
 

あともちろん、表示内容にあわせて行高を自動拡張させる場合は
DataGridCell.HeightもAuto(既定値)にする必要がありますよと。
 

以上をふまえて、まとめ。

A:DataGrid.RowHeightをB:DataGridRow.Heightで上書きしたり、
B:DataGridRow.HeightをC:DataGridCell.Heightで上書きすることはできない。

なので、基本的にA,B,Cを併用するケースなんて無いと思ってよい。
行の高さを指定したいときは、AかBかCか、どれか1つだけ指定して、
あとは既定値Autoのままにしておくべし。

「他は行高20なんだけど、このDataGridだけは行高30にしたい」
というような場合は、BasedOnでStyleを継承するか、
Styleよりも直接プロパティ設定が優先されるというルールを利用すべし。