読者です 読者をやめる 読者になる 読者になる

Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

WPF DataGridで編集中のセルの値を拾う方法を考える

WindowsFormのDataGridViewだと、こんな感じで拾える。

DataGridViewでセルが編集中の時にキーイベントを捕捉する: .NET Tips: C#, VB.NETにあるように、EditingControlShowingというイベントハンドラがあって、その中でDataGridTextBoxColumnの内部にあるTextBoxを捕まえることが出来るので、Textプロパティを触ることが出来る。

が、データバインド全盛のWPFならば、イベントハンドラを拾う必要もない。

下記の例では、品番に入っている値が変更されるとViemModelのセルのグリッドの1行に相当するモデル(懐かしい言葉を使うとData Transfer Object的なやつ)とバインドして、値が変更されると変更通知が飛ぶようになっている。編集したセルの値が変更されたら通知してくれるから、バリデーションなんかも簡単。Setterの中で値を捕まえてコマンドを実行すればいいだけのはず。

ModeをTwowayにするとそのカラムは編集可能になる。Oneway(ViewModelからの一方通行)だと編集できない。気が利く。

    <DataGrid  
    ItemsSource="{Binding VMOrderEntry.OrderList}" 
              AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn
                        CanUserReorder="False" CanUserResize="True"
                       CanUserSort="False" Header="品番" MinWidth="180"
 Binding="{Binding Path=ItemCode,
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
            </DataGridTextColumn>
            <DataGridTextColumn
                        CanUserReorder="False" CanUserResize="True"
                       CanUserSort="False" Header="品名" 
MinWidth="180" Binding="{Binding Path=Name, Mode=OneWay}"/>
            <DataGridTextColumn
                        CanUserReorder="False" CanUserResize="True"
                       CanUserSort="False" Header="色柄"
 MinWidth="50" Binding="{Binding Path=Color, Mode=OneWay}" />
            <DataGridTextColumn
                        CanUserReorder="False" CanUserResize="True"
                       CanUserSort="False" Header="JANコード" 
MinWidth="120"  Binding="{Binding Path=Jancode,Mode=TwoWay}" />
            <DataGridTextColumn
                        CanUserReorder="False" CanUserResize="True"
                       CanUserSort="False" Header="数量" 
MinWidth="50" Binding="{Binding Path=Ordernum,Mode=TwoWay}"/>
         </DataGrid.Columns>
    </DataGrid>

品番のセルでReturnが押されたら、そのセルに入っている値を拾って品番実在チェックを走らせたい。

まずは、PreviewKeydownイベントをGridレベルで捕まえるようにする。ここまでは簡単。べたに書くとこんな感じになるのかな。

 private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                DataGrid g = (DataGrid)sender;
                Model.OrderDetailModel o =
          g.CurrentCell.Item as Model.OrderDetailModel;
                if(g.CurrentCell.Header == "品番") {
                      //ロジックを実行する
                } 
                //他にも編集可能なセルで特定のKeyが押されたら
                //ロジックを走らせたい場合はelseの山になるのがダサい。
            }
        }

できれば、こんなイメージ処理したい。

 private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
          Model.OrderDetailModel o = g.CurrentCell.Item as Model.OrderDetailModel;
             g.CurrentCell.Validation.doCommand(o,e);
        }

Validatioモデルでやることは、「どこのセルか」「セルの値は何か」「どのキーが押されたか」を渡すと予め登録されたKeyEventに応じてDelegateしてるメソッドが走るみたいな感じ。セルにバリデーション用のオブジェクトを忍ばせて、そのオブジェクトにKeyEventに応じたバリデーションメソッドを登録するイメージでカバーしたい。

これを実現しようとすると、昔のDataGridにあったTagプロパティのようなものを依存関係プロパティとして与えないといけないのかな。

ま、実際には品番以外のセルで特定のKeyが押されたときにイベントが走ることは無いんだけど。もうちょい考えます。今日はここまで。