【WPF】DataGridをMVVMで使うなら変えておくべきプロパティ

C#

こんにちは、働くC#プログラマーのさんさめです。

DataGrid使ってますか?
DataGridはWPFでアプリケーションを作る時、
使いたくなるケースが多いコントロールです。

さんさめ
さんさめ

すみません
だいぶ盛りました

…より正確には、
「使おうと一度は思うけど
断念することの多いコントロール」
かもしれません。

DataGridは、
とにかく設定できるプロパティが多い上に、
仕様をしっかり把握していないと
なかなか思い通りに動いてくれません。

そこで本記事では、
特にMVVM…つまりItemsSourceへのBindingを使って
DataGridを使う際に、
「最低限これだけは変更しておいた方が良い」
というプロパティを紹介します。

常用している人には、
常識のようなものばかりですが、
初めてDataGridを採用しようかと
考えている人には役立つのではないかと思います。

スポンサーリンク

DataGridは表形式でデータを表示・編集するコントロール

そもそもDataGridって何?
って人は多分この記事を読んでいないと思いますが、
おさらいです。

DataGridは、
Excelのように表形式で、
データを表示したり編集したりすることのできる、
WPF標準で用意されているコントロールです。

余談ですが、
DataGridを使ったことがあるプログラマーは
「〇〇の動作をExcelのようにしてほしい」
と言われると悶絶します。

DataGridを使ってUIを作っていると
Excelの汎用性の高さに気づかされます。
なので、似せろと言われると、
気が遠くなってしまうのですね。

話が逸れました。

さて、このDataGridをMVVMで利用するためには、
ItemsSourceプロパティに
コレクションをBindingする必要があります。

というわけで、
以下のように、3種のプロパティを持ったクラスを作り、
そのコレクションをBindingしてみます。

このように、とりあえず閲覧するところまでは
割と簡単にいくのがDataGridの特徴です。

しかし、ここから
ユーザーの利用に耐えうるものに
仕上げていこうと思うと、
深淵の入り口にしか立っていなかった
ことに気づかされるのです…。

それでは早速、
利用に耐えうるものにするために
必要なプロパティを、
思いつくままにリストアップしていきます。

必須で変更すべきプロパティ

CanUserAddRowsはfalse

CanUserAddRowsはユーザーが
DataGridの最終行に何か書き込むことで、
行を追加できるかどうかを設定するプロパティです。

既定値はtrueで、trueのままの場合、
以下のように最終行に謎の空白行がでてきます。

そして、この行のセルに何か入力すると、
なんと要素のクラスの
デフォルトコンストラクタが呼び出され、
勝手にバインディングしているコレクションに追加されます。

しかしながら、MVVMの場合、
ViewModelのコレクションをバインディングしているケースが
多いでしょう。
モデル層が増えてないのにViewModelだけ
増えてもちょっと意味が分かりませんね。

ユーザー目線で考えても、
行を追加するボタンなどを用意する方が
よほどシンプルです。

というわけでfalseにしておきましょう。
ちなみに、引数無しのコンストラクタが無い場合は、
たとえtrueのままでも謎の空白行は出てきません。

CanUserDeleteRowsはfalse

CanUserDeleteRowsは
見たまんま、Addの逆ですね。
ユーザーがDataGridの行を削除できるかどうか
を設定するプロパティです。

行を選択した状態でDeleteキーを押すと、
その行が削除されます。

しかし、ビューで削除されたことを、
イベント購読できればまだ検討の余地がありますが、
そんなイベントはありません。

バインディングしているコレクションが
ObservableCollectionなどの
コレクション変更を通知するクラスであれば、
CollectionChangedイベントが発火しますが…。

そもそも削除という行為は、
気軽にできてしまうと困ることが多いです。
確認ダイアログの一つでも
出してあげた方が親切ですね。

これもやはり、行を削除するボタンなどを
用意する方がシンプルに済みます。

というわけでこちらもfalseにしましょう。

CanUserAdd(/Delete)Rowsの既定値はtrueです。
MVVMで使うなら基本的にfalseにしましょう。

CanUserResizeRowsはfalse

CanUserResizeRowsは、
ユーザーが行の高さを自由に変更できるかどうかを
指定するプロパティです。

みょ~ん

これは、一概に
「変更できない方が良い」
と言い切れるものではありませんが、

大抵の場合は自由に広げられても
嬉しいことはあまりないため、
falseにしておいた方が良いでしょう。

trueのままにしてユーザーの操作に任せるよりは
RowHeightプロパティを使用して
最初から行を適切な高さにしておくことを
心掛けた方が気が利いたアプリになります。

SelectionUnitはCellかCellOrRowHeader

次に、選択方式を指定する
SelectionUnitプロパティです。

このプロパティは既定値がFullRowなのですが、
これは、あるセルをクリックした時に
必ず行全体のセルが選択状態になるように動作します。

ユーザーの利便性を考えると以下の点が
不満になることが多いです。

  • どのセルにフォーカスしてるのか分かりづらい
  • セル単位でのコピーができない

よって、CellOrRowHeader、もしくはCellにしてしまうことを
おススメします。

これにしておくとセルをクリックした時に、
そのセルのみが選択状態になります。

CellOrRowHeaderにしておくと、
行のヘッダ部分をクリックすると、
FullRowのように行全体を選択状態にできます。

ただし、注意点が一つあります。
SelectionUnitを
CellOrRowHeaderやCellにした場合、
そのままでは選択している行の要素を
ViewModelにバインディングすることができません。

DataGridのSelectedItemsをどんな時でも取得する方法
で紹介しているようなビヘイビアを使って、
バインディングできるようにする必要があります。

RowHeaderWidthは0、もしくは20くらいに広げる

RowHeaderWidthは、
行のヘッダ部分の幅を指定するプロパティです。

先ほど説明した、
CanuserResizeRowsをfalseにしていて、
かつSelectionUnitに
CellOrRowHeaderを使わない場合は、
行のヘッダ部分が見えていても
特に意味がありません。

行のヘッダ部分は、
特に意味が無い状態であっても、
マウスオーバーすると色が変わったりと、
ユーザーに「何かあるんじゃないか」
と思わせる見た目をしています。

そのため、0にしておいた方が
余計な場所を取りませんし、
ユーザーに余計なことを考えさせる必要が
なくなります。

行のヘッダーは見えなくてよい

逆に行ヘッダに何か意味のある処理を持たせたい場合は、
今度は既定値のままでは幅が狭すぎます。
20くらいに広げて触りやすくした方が
親切でしょう。

VirtualizingPanel.ScrollUnitはPixel

VirtualizingPanel.ScrollUnitは、
DataGrid自体のプロパティではなく、
添付プロパティです。

「スクロールがコレクション内の項目、
またはピクセルとして測定されるかどうか」
というのが公式のドキュメントなのですが、
ちょっと分かりにくいですね。

DataGridの場合では、
「行単位でスクロールするかどうか」
を決めるプロパティと捉えてください。

普段はあまり気にする必要のないプロパティですが、
行の高さが一定ではない場合に特に効果を発揮します。

VirtualizingPanel.ScrollUnitが
既定値であるItemのままだと、
次のように、1行だけ高さが大きい行がある時に、
スクロールが跳ねるのです。

スクロールが跳ねる、
と言われても理解が難しいと思います。
gifアニメにしてみました。

まずは、
VirtualizingPanel.ScrollUnitがItemのままの場合です。

このように、1行が丸々隠れるように、
スクロールが行われます。

分かりやすいように
マウスクリックでスクロールを行いましたが、
これがマウスホイールによるスクロールだと、
飛び飛びになってかなり操作が難しくなります。

次に、
VirtualizingPanel.ScrollUnitがPixelの場合です。

Pixelにすると、
行の高さに関わらず必ず
一定量だけスクロールするようになります。

マウスホイールによるスクロールでは、
こちらの方が圧倒的に直感的な操作となります。

諸々を考慮したDataGridのプロパティ指定

上記を考慮すると、
MVVMでDataGridを使うときは
以下のようにプロパティ指定するのが
私のおススメとなります。

<DataGrid
    CanUserAddRows="False"
    CanUserDeleteRows="False"
    CanUserResizeRows="False"
    SelectionUnit="CellOrRowHeader"
    RowHeaderWidth="20"
    VirtualizingPanel.ScrollUnit="Pixel"
    ItemsSource="{Binding People}" />

だいぶ長くなりましたね…。
ですが、ここまでやって、
ようやく「普通に使えるDataGrid」と
言えるかどうか…というところなのです。

先は長いですね。

また、今回は
AutoGenerateColumnsには触れませんでした。

自動で列ができるAutoGenerateColumnsを
使うべきかどうか?

これについては、次の機会に記事にしたいと思います。

まとめ

まとめです。

  • DataGridは強力なコントロールだが
    MVVMで使うには一工夫必要
  • MVVMで使うときに
    変更しておくべきプロパティを紹介
  • 普通に使えるDataGridへの道は長い

最後までお読みいただき、ありがとうございました。

コメント

タイトルとURLをコピーしました