なべひろBlog

プログラミングをメインに仕事に関するアレコレを発信しています。

MVVMなWPFでDataGridの内容変更をキャッチする

編集可能なDataGridでユーザが編集したら、内容を即座にキャッチしたい事がよくあります。

内容が変更されたらXMLファイルに保存するとか、内容をチェックするとか使い道は色々あります。

MVVMなWPFアプリでReactivePropertyを使えばTextBoxなどはSubscribeで簡単に実現可能ですが、独自Classのデータ群をDataGridのItemsSourceへBindingした場合などの手法が分からず、ReactivePropertySlim<List<MyClass>>なんて変数を作ってSubscribeでキャッチできるだろうか?などを試みましたが、あえなく撃沈...

TwitterでつぶやいたらReactivePropertyならこの人しかいないと私が思っている「かずき」さんから有り難いアドバイスを頂きました。

大雑把に言うと

1.ObserveElementPropertyChangedを使う。

2.MyClassのメンバーのsetにはSetPropertyを使う。

XAML

BindingするのはViewModelで宣言しているObservableCollectionです。

<DataGrid ItemsSource="{Binding Member}"/>
ViewModel
変数宣言

このObservableCollectionがViewにBindingさせる変数です。

public ObservableCollection Member { get; } = new ObservableCollection();
コンストラクタ

コレクションの中身が変更されたか否かを監視する拡張メソッドが「ObserveElementPropertyChanged」です。

そして中身が変更されたら「Change」メソッドが実行されます。

Member.ObserveElementPropertyChanged().Subscribe(Changed).AddTo(Disposable);
内容変更で呼ばれるメソッド
private void Changed(SenderEventArgsPair<People, PropertyChangedEventArgs> pair)
{
     MessageBox.Show("Changed");
 }
Classを追加
public class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
    {
         if (EqualityComparer<T>.Default.Equals(field, value)) { return false; }
         field = value;
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
         return true;
    }
}

表示するBindableBaseを継承したデータ群のClass

public class People : BindableBase
{
    private string _Name;
    public string Name
    {
         get { return _Name; }
         set { SetProperty(ref _Name, value); }
    }
}

GitHubにはついでにプロパティのset getをシンプルに記述する手法も掲載しています。