2011-03-04 57 views
3

如果我有以下布局:鼓泡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需要保存。

回答

1

我最近刚刚在研究这个完全相同的问题。我的方法是简单地让B,C,d等来管理自己的肮脏状态,然后修改了的IsDirty财产如:

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

对我来说,这不仅是简单的,但它使最有意义的。 A is如果它的任何属性发生了变化,并且B,C,D等都是A的属性,则脏。

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); 
    } 
} 
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. 编译时检查你的财产“名”实际上指的是你的财产。即避免使用字符串时与拼写属性名称错误有关的令人讨厌的错误。

性能

有一个相关的性能命中这种方法......不仅仅是使用字符串慢20%。这就是说,尽管衡量指标和跟踪说速度较慢,但​​我实际上并没有分辨出差异,所以命中值得重新考虑:针对我参与开发的各种应用程序进行应用程序维护。

替代实现

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

    如果(场是INotifyPropertyChanged的)...