没有setter的viewmodel中的WPF mvvm属性?

我正在处理一些使用和坚持MVVM模式的WPF问题。

我的大多数属性看起来像这样:

public string Period { get { return _primaryModel.Period; } set { if (_primaryModel.Period != value) { _primaryModel.Period = value; RaisePropertyChanged("Period"); } } } 

这很好用。

但是我也有一些像这样的属性:

 public bool EnableConsignor { get { return (ConsignorViewModel.Id != 0); } } 

它没有setter,因为id“自动”更改(每次调用ConsignorViewModel的保存时。但这会导致“系统”不知道bool何时从false变为true的问题(如没有RaisePropertyChanged )。

对于这些类型的属性,您只需在依赖数据更改时引发PropertyChanged 。 就像是:

 public object ConsignorViewModel { get { return consignorViewModel; } set { consignorViewModel = value; RaisePropertyChanged("ConsignorViewModel"); RaisePropertyChanged("EnableConsignor"); } } 

可以在任何方法中调用RaisePropertyChanged ,因此只需执行任何会改变EnableConsignor返回值的操作即可。 以上只是一个例子。

我刚才写了这篇文章,它一直很好用

 [AttributeUsage(AttributeTargets.Property, Inherited = false)] public class CalculatedProperty : Attribute { private string[] _props; public CalculatedProperty(params string[] props) { this._props = props; } public string[] Properties { get { return _props; } } } 

ViewModel基础

 public class ObservableObject : INotifyPropertyChanged { private static Dictionary> calculatedPropertiesOfTypes = new Dictionary>(); private readonly bool hasComputedProperties; public ObservableObject() { Type t = GetType(); if (!calculatedPropertiesOfTypes.ContainsKey(t.FullName)) { var props = t.GetProperties(); foreach (var pInfo in props) { var attr = pInfo.GetCustomAttribute(false); if (attr == null) continue; if (!calculatedPropertiesOfTypes.ContainsKey(t.FullName)) { calculatedPropertiesOfTypes[t.FullName] = new Dictionary(); } calculatedPropertiesOfTypes[t.FullName][pInfo.Name] = attr.Properties; } } if (calculatedPropertiesOfTypes.ContainsKey(t.FullName)) hasComputedProperties = true; } public event PropertyChangedEventHandler PropertyChanged; public virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); if (this.hasComputedProperties) { //check for any computed properties that depend on this property var computedPropNames = calculatedPropertiesOfTypes[this.GetType().FullName] .Where(kvp => kvp.Value.Contains(propertyName)) .Select(kvp => kvp.Key); if (computedPropNames != null) if (!computedPropNames.Any()) return; //raise property changed for every computed property that is dependant on the property we did just set foreach (var computedPropName in computedPropNames) { //to avoid stackoverflow as a result of infinite recursion if a property depends on itself! if (computedPropName == propertyName) throw new InvalidOperationException("A property can't depend on itself"); OnPropertyChanged(computedPropName); } } } protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } } 

例:

 public class ViewModel : ObservableObject { private int _x; public int X { get { return _x; } set { SetField(ref _x, value); } } private int _y; public int Y { get { return _y; } set { SetField(ref _y, value); } } //use the CalculatedProperty annotation for properties that depend on other properties and pass it the prop names that it depends on [CalculatedProperty("X", "Y")] public int Z { get { return X * Y; } } [CalculatedProperty("Z")] public int M { get { return Y * Z; } } } 

注意:

  • 它每种类型只使用一次reflection
  • 如果有新值, SetField将设置字段并引发属性更改
  • 只要你在setter中调用它就不需要将属性名称传递给SetField ,因为[CallerMemberName]自c#5.0起为你做了。
  • 如果你在setter之外调用SetField ,那么你将不得不传递属性名称
  • 根据我的上一次更新,您可以通过直接设置字段然后调用OnPropertyChanged(“PropertyName”)来避免使用SetField ,它将为所有依赖于它的属性引发PropertyChanged。
  • 在c#6中,您可以使用nameof运算符来获取属性名称,例如nameof(Property)
  • 如果存在计算属性,OnPropertyChanged将重新调用自身

XAML用于测试