問題点
WPF の DataGrid で DataGridTemplateColumn のテンプレートで TextBox(カスタムしたものなど)を設定しているとします。
わかりにくいけど、キーボードの「1」を押下してフォーカスを TextBox に与えていますが、TextBox は GotFocus イベントは発生していますが、KeyDown イベントは発生していません。
データグリッドを操作して、(キーボードで)数字やテキストを入力すると選択中のセル (DataGridCell) に数字やテキストを入力……となるのですが、このとき TextBox は KeyDown や PreviewKeyDown などのイベントを実行することなく、数値やテキストを受け取っていることがあります。
なにが不味いかというと、例えば、KeyDown イベントなどで入力された内容を訂正するようなチェックメソッドを入れていると、入力内容のチェックをせずに内容を変更できてしまいます。
マウスで DataGridCell を直接選択したときは、気にすることないです。
備考
この現象は Windows Forms のころからあるみたいです。問題が発生する理由としては、TextBox をホストしている親コントロールが KeyDown イベントを受信してしまって、子にあたる TextBox にイベントが伝播しないようです。
データグリッドは、セルの表示モードや編集モードなどがあるため、イベントの伝播がややこしい設計になっているのだと思います。
イベントを発生させるための対策
まず、DataGrid でキーボードのイベントを受け取るようにします。
<DataGrid ItemsSource="{Binding Samples}" PreviewKeyDown="DataGrid_PreviewKeyDown">
つぎに、受け取ったイベントを子要素に伝播させるコードを追加します。
private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e) { var datagrid = sender as DataGrid; var pressedCell = e.OriginalSource as DataGridCell; var key = e.Key; if (datagrid == null || pressedCell == null) return; if (key == Key.Up || key == Key.Down || key == Key.Left || key == Key.Right) return; var nextFocus = pressedCell.PredictFocus(FocusNavigationDirection.Left); var inputElement = nextFocus as IInputElement; if (inputElement != null && nextFocus != null) { if (pressedCell.IsAncestorOf(nextFocus)) { inputElement.Focus(); // 子要素に発生したイベントを渡す if (inputElement is TextBox) { var target = inputElement as TextBox; var ev = new KeyEventArgs(Keyboard.PrimaryDevice, PresentationSource.FromVisual(target), e.Timestamp, e.Key); ev.RoutedEvent = e.RoutedEvent; (inputElement as TextBox).RaiseEvent(ev); e.Handled = true; } } // Enter キーの入力によるフォーカス設定はキー入力のみ無効化する(入力で再度フォーカスが外れるため) if (key == Key.Enter) { e.Handled = true; } } }
MVVM でイベントをコマンド実行にしていても似たような対応が可能だと思います。
サンプル
テストプログラムは GitHub の「Samples」に公開しています。今回のプログラムは「FixedDecimalPointTextBoxSample」です。
参考
- stackoverflow - How can I programmatically generate keypress events in C#?
- stackoverflow - DataGridView keydown event not working in C#
- stackoverflow - ProcessCmdKey analog in WPF
- stackoverflow - Single click edit in WPF DataGrid
C#WPFの道【XAMLに慣れる編】: WindowsFormsプログラマーがWPFを書く方法
- 作者:ピーコックアンダーソン
- 発売日: 2019/02/04
- メディア: Kindle版