WPF DataGrid でセル編集中と表示時でコントロールを切り替える方法

DataGridTemplateColumn の CellTemplate と CellEditingTemplate を使い分け、表示時と編集中で最適な UI を構成する方法を解説します。

概要

WPF の DataGrid でセルの表示状態と編集状態に異なるコントロールを使う場合は、DataGridTemplateColumnCellTemplateCellEditingTemplate を分離して定義する方法が基本となる。
本記事では、基本実装、実務で多い応用パターン、CellEditingTemplate を採用する判断基準を整理する。

前提・対象環境

本記事の図は、上記の環境で DataGrid のセルに実際に置かれた要素の型を読み出して得たものである。
この環境で確認しているのは次の点である。

問題

DataGridTextColumn のような標準列のみを使うと、表示時と編集中で同一の UI になりやすい。
その結果、一覧画面としては入力欄が多く見えすぎる、あるいは編集時の入力補助が不足するという問題が発生する。

原因・背景

一覧表示で必要な要件は「軽量で読みやすい表示」であり、編集時に必要な要件は「入力制約と操作性」である。
この要件差を単一コントロールで満たそうとすると、どちらかの体験が犠牲になる。
DataGrid は編集開始時にテンプレートを切り替える仕組みを持つため、表示用と編集用を分離する設計が適している。

表示中と編集中で、セルに置かれる要素が実際に入れ替わることは測って確かめられる。

表示中と編集中でセルに置かれる要素を測った表。表示中は TextBlock で cell.IsEditing は False、BeginEdit を呼ぶと ComboBox に変わり cell.IsEditing が True になる。
.NET 10 / Windows 11 で、CellTemplateTextBlockCellEditingTemplateComboBox を置いた DataGridTemplateColumn を測った結果。要素の型は visual ツリーから読み取っている。

要素そのものが差し替わっている。 同じコントロールの見た目が変わるのではなく、CellTemplate の内容が破棄されて CellEditingTemplate の内容が生成される。
このため、編集用テンプレートに重いコントロールを置いても、表示中はその生成コストがかからない。


解決方法

DataGridTemplateColumn を使い、通常時は CellTemplate、編集時は CellEditingTemplate を定義する。
この構成では DataGrid が編集開始と終了に応じてテンプレートを自動で切り替えるため、表示と編集の責務を明確に分離できる。
表示は軽量な TextBlock、編集は TextBoxComboBoxDatePicker など入力向けコントロールを使い分ける。

実装例

まず、最小構成として表示時 TextBlock、編集時 TextBox を定義する。
この実装は表示と編集の切り替えを最も明確に示す。

<DataGrid ItemsSource="{Binding Items}">
  <DataGrid.Columns>
    <DataGridTemplateColumn Header="名前">
      <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding Name}" />
        </DataTemplate>
      </DataGridTemplateColumn.CellTemplate>
      <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
          <TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
      </DataGridTemplateColumn.CellEditingTemplate>
    </DataGridTemplateColumn>
  </DataGrid.Columns>
</DataGrid>

編集開始時は CellEditingTemplate が適用され、編集終了後は CellTemplate に戻る。
切り替え処理をイベントで手動実装する必要はない。

同じ列定義の DataGrid を 2 つ並べた画面。左はすべてのセルがテキスト表示、右は 2 行目の Category セルだけが ComboBox になっている。
同じ DataGridTemplateColumn の表示状態と編集状態。右は 2 行目の Category セルだけを編集状態にしたもので、そのセルだけが CellEditingTemplateComboBox に切り替わっている。他のセルは CellTemplate のままである。

次に、値制約が必要な列では表示 TextBlock と編集 ComboBox の構成を使う。
この構成は入力候補を限定し、誤入力を抑制できる。

<DataGridTemplateColumn Header="種別">
  <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Category}" />
    </DataTemplate>
  </DataGridTemplateColumn.CellTemplate>
  <DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
      <ComboBox
        ItemsSource="{Binding DataContext.Categories, RelativeSource={RelativeSource AncestorType=DataGrid}}"
        SelectedItem="{Binding Category, Mode=TwoWay}" />
    </DataTemplate>
  </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

ComboBox の選択肢は DataGridDataContext から参照し、行アイテムと編集候補を分離して管理する。
選択肢の追加やローカライズが必要な業務画面でも拡張しやすい。

表示と編集を単一テンプレートで切り替える必要がある場合は、DataGridCell.IsEditingDataTrigger で参照する方法も利用できる。
ただし、フォーカス制御と可読性の観点で複雑化しやすい。

<DataTemplate>
  <Grid>
    <TextBlock x:Name="display" Text="{Binding Name}" />
    <TextBox x:Name="editor" Text="{Binding Name, Mode=TwoWay}" Visibility="Collapsed" />
    <DataTemplate.Triggers>
      <DataTrigger
        Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridCell}, Path=IsEditing}"
        Value="True">
        <Setter TargetName="display" Property="Visibility" Value="Collapsed" />
        <Setter TargetName="editor" Property="Visibility" Value="Visible" />
      </DataTrigger>
    </DataTemplate.Triggers>
  </Grid>
</DataTemplate>

この方法は一部の特殊要件で有効だが、基本方針は CellTemplateCellEditingTemplate の分離を優先する。
保守性、再利用性、デバッグ容易性の点で差が出る。

注意点

代替案・比較

まとめ

CellEditingTemplate を利用する主目的は、表示時の見やすさと編集時の入力しやすさを分離することである。
表示が中心の列では軽量な表示 UI を維持し、編集時のみ入力制約や補助 UI を有効化すると、UX と性能を両立しやすい。
判断基準として、表示と編集で UI を変えたい場合、入力制約が必要な場合、編集 UI が重い場合は DataGridTemplateColumn の採用が適する。
一方で、単純テキスト編集だけで完結する列は標準列を選択するほうが実装効率が高い。

関連記事