質問:バインドされたUI要素を更新して、リスト内の個々の項目のプロパティが値を変更した後にViewModelでのみ定義されたプロパティの値を表示する適切な方法。基になるモデルデータが変更されたときにViewModelで定義された通知プロパティの変更
INotifyPropertyChangedをクラスの中でリスト内の項目にすると、特定のデータがバインドされているUI要素のみが更新されます。 ListViewアイテムまたはDataGridセルと同様です。そしてそれはうまくいきます、それが私たちが望むものです。しかし、Excelのテーブルのように、合計行が必要な場合はどうすればよいでしょうか。その特定の問題についてはいくつかの方法がありますが、ここでの根底にある問題は、ViewModelでモデルのデータに基づいてプロパティを定義して計算することです。例:
public class ViewModel
{
public double OrderTotal => _model.order.OrderItems.Sum(item => item.Quantity * item.Product.Price);
}
いつ、どのように通知/更新/呼び出されますか?
さらに詳しい例を試してみましょう。
ここでXAMLこれはモデルである
<Grid>
<DataGrid x:Name="GrdItems" ... ItemsSource="{Binding Items}"/>
<TextBox x:Name="TxtTotal" ... Text="{Binding ItemsTotal, Mode=OneWay}"/>
</Grid>
です:
public class Item : INotifyPropertyChanged
{
private string _name;
private int _value;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public int Value
{
get { return _value; }
set
{
if (value.Equals(_value)) return;
_value = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new propertyChangedEventArgs(propertyName));
}
}
public class Model
{
public List<Item> Items { get; set; } = new List<Item>();
public Model()
{
Items.Add(new Item() { Name = "Item A", Value = 100 });
Items.Add(new Item() { Name = "Item b", Value = 150 });
Items.Add(new Item() { Name = "Item C", Value = 75 });
}
}
とのViewModel:私は、これはコードを知っている
public class ViewModel
{
private readonly Model _model = new Model();
public List<Item> Items => _model.Items;
public int ItemsTotal => _model.Items.Sum(item => item.Value);
}
は、簡略化の上に見えますが、それはの一部ですより大きく、難解な困難なアプリケーションです。
DataGridで項目の値を変更するときは、ItemsTotalプロパティでTxtTotalテキストボックスを更新する必要があります。
これまでのところ、ObservableCollectionの使用とCollectionChangedイベントの実装が含まれていました。
にモデルチェンジ:
public class Model: INotifyPropertyChanged
{
public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>();
public Model()
{
Items.CollectionChanged += ItemsOnCollectionChanged;
}
.
.
.
private void ItemsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (Item item in e.NewItems)
item.PropertyChanged += MyType_PropertyChanged;
if (e.OldItems != null)
foreach (Item item in e.OldItems)
item.PropertyChanged -= MyType_PropertyChanged;
}
void MyType_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Value")
OnPropertyChanged(nameof(Items));
}
public event PropertyChangedEventHandler PropertyChanged;
.
.
.
}
そしてへのviewmodelの変更:
public class ViewModel : INotifyPropertyChanged
{
private readonly Model _model = new Model();
public ViewModel()
{
_model.PropertyChanged += ModelOnPropertyChanged;
}
private void ModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
OnPropertyChanged(nameof(ItemsTotal));
}
public ObservableCollection<Item> Items => _model.Items;
public int ItemsTotal => _model.Items.Sum(item => item.Value);
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
このソリューションは動作しますが、それだけでより多くの雄弁な実装を持つ必要がありますハックの周りの仕事のように思えます。私のプロジェクトには、ビューモデルのこれらの合計プロパティのいくつかがあります。それは、更新するプロパティがたくさんあり、多くのオーバーヘッドのような感じのコードを書くことができます。
私はもっと多くの研究が必要ですが、私はこの質問を書いている間にいくつかの興味深い記事が出ました。私は、この問題を他のソリューションへのリンクで更新します。この問題は、私が思ったよりも一般的です。
これは常にxamlにあったビットです。あなたはko.computedと似た何かを探しています。以下は、類似のことを行ういくつかの記事です。http://philchuang.com/mvvm-pain-points-chaineddependentcalculated-properties/ http://www.codeproject.com/Articles/375192/WPF-The-calculated-property-dependency -problem http://www.codeproject.com/Articles/422581/Knockout-Style-Observables-in-XAML –