2011-03-04 55 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的)...