冒泡INotifyPropertyChanged和嵌套属性

时间:2011-03-04 20:12:30

标签: c# inotifypropertychanged

如果我有以下布局:


public class A : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public B { get; set; }
}

public class B { public C { get; set; } }
public class C { public D { get; set; } }
public class D { public E { get; set; } }

//... add n classes

public class Z
{
    public int Property
    {
        set
        {
            if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Property"));
        }
    }
}

当A.B.C.D.E ... Z.Property发生变化时,我最简单的通知A的方式是什么?

当A内部的任何内容发生变化时,我希望它被标记为“脏”,这样我就可以告诉系统A需要保存。

3 个答案:

答案 0 :(得分:1)

我最近正在研究这个完全相同的问题。我的方法是简单地让B,C,D等管理他们自己的Dirty状态,然后修改A的IsDirty属性:

public bool IsDirty
{
   get
   {
        return _isDirty || B.IsDirty || C.IsDirty /* etc */;
   }
}

对我而言,这不仅简单,而且最有意义。如果的任何属性发生了变化, 就会变脏,而B,C,D等都是A的属性。

答案 1 :(得分:1)

我没有对它进行测试,但是应该对它进行测试。我不记得为什么,但我认为你无法处理PropertyChanged事件。您应该声明自己的委托(VoidHandler)。

public delegate void VoidHandler(object sender);

public class B // also C,D,E,...
{
  // A.ItemChanged() will be wired to this SomethingChangedHandler.
  // I heard you are saving. Exclude SomethingChangedHandler from save.
  [field: NonSerialized]
  public VoidHandler SomethingChangedHandler;

  private c;
  public C
  {
    set
    {
      // unwire handler from old instance of C
      if(c != null)
        c.SomethingChangedHandler -= ItemChanged;

      // wire handler to new instance of C
      value.SomethingChangedHandler += ItemChanged;

      c = value;

      // setting c is also change which require notification
      ItemChanged(this);
    }
    get{}
  }

  // notify A about any change in B or in C
  void ItemChanged(object sender)
  {
    if(SomethingChangedHandler != null)
      SomethingChangedHandler(this);
  }
}

答案 2 :(得分:1)

对于具有公共基类的业务线应用程序,我按照

执行此操作

Implementing INotifyPropertyChanged - does a better way exist?

进行一些修改以检查“冒泡”属性。

基类

 public bool HasAlteredState { get; protected set; }

 public event PropertyChangedEventHandler PropertyChanged;

 private void propertyObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.OnPropertyChanged(e.PropertyName);
        }

 protected virtual void RegisterSubPropertyForChangeTracking(INotifyPropertyChanged propertyObject)
        {
            propertyObject.PropertyChanged += new PropertyChangedEventHandler(propertyObject_PropertyChanged);
        }

 protected virtual void DeregisterSubPropertyForChangeTracking(INotifyPropertyChanged propertyObject)
        {
            propertyObject.PropertyChanged -= propertyObject_PropertyChanged;
        }

  protected virtual void OnPropertyChanged(string propertyName)
    {
        this.HasAlteredState = true;
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }

    protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
    {
         if (EqualityComparer<T>.Default.Equals(field, value)) return false;

        if (field is INotifyPropertyChanged)
        {
            if (field != null) { this.DeregisterSubPropertyForChangeTracking((INotifyPropertyChanged)field); }
        }
        if (value is INotifyPropertyChanged)
        {
            if (value != null) { this.RegisterSubPropertyForChangeTracking((INotifyPropertyChanged)value); }
        }

        field = value;
        OnPropertyChanged(selectorExpression);
        return true;
    }

子类

private IndividualName _name;
public IndividualName PersonName
        {
            get { return _name; }
            set { SetField(ref _name, value, () => PersonName); }
        }

<强>提供

  1. 简单的财产变更通知
  2. 复杂属性更改通知
  3. 来自对象图中更深层次的INotifyPropertyChanged实现的“冒泡”
  4. 编制时间检查您的财产“名称”实际上是指您的财产。即使使用字符串时,请避免与拼写属性名称相关的令人讨厌的错误。
  5. <强>性能

    此方法的性能受到影响......比使用字符串慢约20%。虽然指标和跟踪表明速度较慢但我实际上并没有说出差异,所以命中率值得重视:应用程序维护我参与开发的各种应用程序。

    替代实施

    1. 如果基类不是一个选项,你可以去扩展方法路由。
    2. 为了获得更好的性能,您可以使用两种不同的SetField方法;第一个SetNotifyField将处理自己实现INotifyPropertyChanged(如上所述)的属性,第二个SetField将处理简单属性。即切出

      if(field为INotifyPropertyChanged)...