2011-10-06 74 views
15

是否有類似INotifyPropertyChanged的界面,其中事件參數包含要更改的屬性的舊值,還是必須擴展該界面才能創建一個?NotifyPropertyChanged事件參數中包含舊值

例如:

public String ProcessDescription 
    { 
     get { return _ProcessDescription; } 
     set 
     { 
      if(value != ProcessDescription) 
      { 
       String oldValue = _ProcessDescription; 
       _ProcessDescription = value; 
       InvokePropertyChanged("ProcessDescription", oldvalue); 
      } 
     } 
    } 

    InvokePropertyChanged(String PropertyName, OldValue) 
    { 
     this.PropertyChanged(new ExtendedPropertyChangedEventArgs(PropertyName, OldValue)); 
    } 

我也滿足於PropertyChanging狀事件,其提供該信息,它是否支持e.Cancel。

回答

30

正如答案所示,我必須實現我自己的解決方案。對於其他人的利益,我在這裏提出的是:

的擴展PropertyChanged事件

此活動已專門設計的,與舊的PropertyChanged事件的向後兼容。它可以與調用者的簡單PropertyChangedEventArgs交替使用。當然,在這種情況下,事件處理程序負責檢查傳遞的PropertyChangedEventArgs是否可以向下轉換爲PropertyChangedExtendedEventArgs,如果他們想使用它。如果所有他們感興趣的都是PropertyName屬性,則不需要向下轉換。

public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs 
{ 
    public virtual T OldValue { get; private set; } 
    public virtual T NewValue { get; private set; } 

    public PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue) 
     : base(propertyName) 
    { 
     OldValue = oldValue; 
     NewValue = newValue; 
    } 
} 

的擴展的PropertyChanged接口

如果程序員希望創建一個事件通知特性,包括一箇舊值和一個新的價值,他們只需要實現以下接口:

// Summary: Notifies clients that a property value is changing, but includes extended event infomation 
/* The following NotifyPropertyChanged Interface is employed when you wish to enforce the inclusion of old and 
* new values. (Users must provide PropertyChangedExtendedEventArgs, PropertyChangedEventArgs are disallowed.) */ 
public interface INotifyPropertyChangedExtended<T> 
{ 
    event PropertyChangedExtendedEventHandler<T> PropertyChanged; 
} 

public delegate void PropertyChangedExtendedEventHandler<T>(object sender, PropertyChangedExtendedEventArgs<T> e); 

例1

用戶現在可以指定一個更先進的NotifyPropertyChanged方法,允許屬性setter到他們原來的值傳遞:

public String testString 
{ 
    get { return testString; } 
    set 
    { 
     String temp = testString; 
     testValue2 = value; 
     NotifyPropertyChanged("TestString", temp, value); 
    } 
} 

您的新NotifyPropertyChanged方法是這樣的:

protected void NotifyPropertyChanged<T>(string propertyName, T oldvalue, T newvalue) 
{ 
    OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(propertyName, oldvalue, newvalue)); 
} 

OnPropertyChanged與往常一樣:

public virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    PropertyChangedEventHandler handler = PropertyChanged; 
    if (handler != null) 
     handler(sender, e); 
} 

例2

或者,如果你喜歡使用lambda表達式,並用硬編碼的屬性名稱字符串做掉完全,您可以使用以下命令:

public String TestString 
{ 
    get { return testString; } 
    private set { SetNotifyingProperty(() => TestString, ref testString, value); } 
} 

這是由以下魔法支持:

protected void SetNotifyingProperty<T>(Expression<Func<T>> expression, ref T field, T value) 
{ 
    if (field == null || !field.Equals(value)) 
    { 
     T oldValue = field; 
     field = value; 
     OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(GetPropertyName(expression), oldValue, value)); 
    } 
} 
protected string GetPropertyName<T>(Expression<Func<T>> expression) 
{ 
    MemberExpression memberExpression = (MemberExpression)expression.Body; 
    return memberExpression.Member.Name; 
} 

性能

如果關注性能,請參閱此問題:Implementing NotifyPropertyChanged without magic strings

總之,開銷很小。添加舊值並切換到擴展事件大約有15%的減速,仍然允許每秒一百萬次的屬性通知,並且切換到lambda表達式是一個5倍的放緩,允許大約10萬次屬性通知第二。這些數字遠遠無法在任何UI驅動的應用程序中形成瓶頸。

+0

我的microbench標記被證明能夠產生類似的結果,但是根據每個通知產生多少垃圾,我發現過度使用表達式調用INPC會給GC帶來額外的壓力,並最終導致更多的Gen1集合。儘管設計並不好(還有很多其他的東西可以改進),但改回字符串使我們在一個特定的WPF應用程序中獲得了明顯的性能提升。 –

+0

我試圖使用此代碼,並有ViewModel實現接口'INotifyPropertyChangedExtended '而不是通常的'INotifyPropertyChanged',但我沒有得到雙向綁定。 – xavigonza

+2

雙向綁定適用於我。可能有些東西你錯過了... 但是請記住,你不需要實現INotifyPropertyChangedExtended 接口。在你的類中,你仍然可以實現INotifyPropertyChanged,所以你的類定義不會改變...我發現INotifyPropertyChangedExtended 雖然有點混亂。要點是,不要實現該接口,只需使用PropertyChangedExtendedEventArgs。 – lightxx

1

如果您只想更改舊值,則可以在更改屬性值之前調用該事件。但是,這將是這個事件通常被使用的背景,所以我會創建一個專用的接口併爲它指定參數。

+0

屬性更改通知要求在調用屬性之前爲其分配新的值。請參閱.Net Framework文檔:http://msdn.microsoft.com/en-us/library/ms743695.aspx。我只想要包含更多信息。 – Alain

+0

你說得對,我只是在編輯。 –

3

聽起來像你想使用INotifyPropertyChanging連同INotifyPropertyChanged。 Msdn文檔http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanging.aspx

+0

這兩個事件的事件參數都不允許處理程序確定值從兩個變爲兩個。此外,處理類不能期望1)存儲所有引發更改事件的屬性的列表,2)使用反射在事件觸發時獲取所有這些屬性的值,以便它們可以3 )比較更改和更改事件之前和之後更改的屬性的存儲值已被觸發。 – Alain

+0

1)爲什麼一個正在監聽'INotifyPropertyChanging'的對象不會負責它爲什麼要監聽事件?從抽象的角度來看,我認爲任何程序員都不明白爲什麼有人在聽某個事件(除了瞭解事件發生的原因以及爲什麼)之外是不明智的。如果你打算重新使用你的'INotifyPropertiesChangedAndChangingWithValues'然後擴展。如果它被使用一次,一個新的界面看起來像額外的工作,幾乎沒有什麼優勢。 –

+1

因爲1)它需要處理器訂單N時間來執行它並且事件通知程序訂單1次。 2)事件通知者是信息專家。 3)讓處理程序跟蹤對象的屬性更改事件的所有屬性,它監視的是範圍蠕變和糟糕的設計。 – Alain

1

不,你必須從頭創建自己的。

我以前在我的研究項目Granite中也做過同樣的事情,但我認爲它不值得花費。與我一起工作的太多房產都計算過了,爲了籌集一個活動而不得不兩次運行它們的成本太高。

+0

@Jonathan_Allen:我這樣做了,謝謝。 – Alain