2011-10-06 31 views
15

Có giao diện giống như INotifyPropertyChanged trong đó sự kiện args chứa giá trị cũ của thuộc tính bị thay đổi hay tôi phải mở rộng giao diện đó để tạo một giao diện?NotifyPropertyThay đổi sự kiện trong đó sự kiện args chứa giá trị cũ

Ví dụ:

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)); 
    } 

Tôi cũng sẽ giải quyết cho một sự kiện PropertyChanging giống như cung cấp thông tin này, có hay không nó hỗ trợ e.Cancel.

Trả lời

30

Như được chỉ ra bởi câu trả lời, tôi phải thực hiện giải pháp của riêng mình. Vì lợi ích của người khác, tôi đã trình bày nó ở đây:

Các mở rộng PropertyChanged Event

Sự kiện này đã được thiết kế đặc biệt để tương thích ngược với các sự kiện PropertyChanged cũ. Nó có thể được sử dụng thay thế lẫn nhau với các PropertyChangedEventArgs đơn giản bởi người gọi. Tất nhiên, trong những trường hợp như vậy, đó là trách nhiệm của trình xử lý sự kiện để kiểm tra xem các PropertyChangedEventArgs đã thông qua có thể bị downcast thành một PropertyChangedExtendedEventArgs hay không, nếu chúng muốn sử dụng nó. Không có downcasting là cần thiết nếu tất cả những gì họ quan tâm là thuộc tính 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; 
    } 
} 

Các Extended PropertyChanged Interface

Nếu, các lập trình viên muốn tạo ra một sự kiện mà lực lượng các tính chất thông báo để bao gồm một giá trị cũ và một giá trị mới, họ cần chỉ thực hiện giao diện sau :

// 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); 

Ví dụ 1

Người dùng bây giờ có thể chỉ định một NotifyPropertyChanged phương pháp tiên tiến hơn cho phép setters tài sản để vượt qua trong giá trị cũ của họ:

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

đâu phương pháp NotifyPropertyChanged mới của bạn trông như thế này:

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

OnPropertyChanged giống như mọi khi:

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

Ví dụ 2

Hoặc nếu bạn thích sử dụng các biểu thức lambda và loại bỏ chuỗi tên thuộc tính mã hóa cứng hoàn toàn, bạn có thể sử dụng như sau:

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

nào được hỗ trợ bởi sự kỳ diệu sau :

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; 
} 

Performance

Nếu hiệu suất là một mối quan tâm, hãy xem câu hỏi này: Implementing NotifyPropertyChanged without magic strings.

Tóm lại, chi phí tối thiểu. Thêm giá trị cũ và chuyển sang sự kiện mở rộng là khoảng 15% chậm lại, vẫn cho phép theo thứ tự một triệu thông báo thuộc tính mỗi giây và chuyển sang biểu thức lambda chậm hơn 5 lần cho phép khoảng một trăm nghìn thông báo thuộc tính cho mỗi thứ hai. Những con số này là xa để có thể hình thành một nút cổ chai trong bất kỳ ứng dụng điều khiển UI nào.

+0

Dấu hiệu microbench của tôi đã chứng minh là tạo ra kết quả tương tự, tuy nhiên tùy thuộc vào lượng rác mà mỗi thông báo tạo ra, tôi thấy rằng quá mức gọi INPC với các biểu thức đặt thêm áp lực lên GC và kết quả là gây ra nhiều bộ sưu tập Gen1 hơn. Mặc dù thiết kế không tuyệt vời (và nhiều thứ khác có thể được cải thiện), việc thay đổi ngược lại thành chuỗi đã cho chúng ta một cải tiến hiệu suất rõ ràng trong một ứng dụng WPF cụ thể. –

+0

Tôi đã cố gắng sử dụng mã này và có ViewModel để thực hiện giao diện 'INotifyPropertyChangedExtended ' thay vì thông thường 'INotifyPropertyChanged', nhưng tôi đã không nhận được một ràng buộc hai chiều. – xavigonza

+2

Hai cách ràng buộc hoạt động đối với tôi. Có lẽ có điều gì đó bạn đã bỏ lỡ ... Nhưng hãy nhớ rằng bạn KHÔNG cần triển khai giao diện INotifyPropertyChangedExtended . Trong lớp học của bạn, bạn vẫn có thể thực hiện INotifyPropertyChanged, do đó, định nghĩa lớp học của bạn không thay đổi ... Tôi thấy rằng một phần với INotifyPropertyChangedExtended một chút bối rối mặc dù. Điểm là, KHÔNG triển khai giao diện đó, chỉ cần sử dụng PropertyChangedExtendedEventArgs. – lightxx

1

Nếu bạn chỉ muốn giá trị cũ, bạn có thể gọi sự kiện trước khi thay đổi giá trị của thuộc tính. Nhưng đó sẽ là một sự ra đi của sự kiện này thường được sử dụng như thế nào, vì vậy tôi sẽ tạo ra một giao diện chuyên dụng và args cho nó.

+0

Thông báo thay đổi thuộc tính yêu cầu thuộc tính phải được gán giá trị mới trước khi được gọi. Xem tài liệu .Net Framework: http://msdn.microsoft.com/en-us/library/ms743695.aspx. Tất cả những gì tôi muốn là bao gồm thông tin bổ sung. – Alain

+0

Bạn nói đúng, tôi chỉ đang chỉnh sửa. –

3

Có vẻ như bạn muốn sử dụng số INotifyPropertyChanging kết hợp với INotifyPropertyChanged. Tài liệu Msdn http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanging.aspx

+0

Không có sự kiện nào cho hai sự kiện đó cho phép trình xử lý xác định giá trị đang thay đổi từ hai hoặc hai. Hơn nữa, lớp xử lý không thể được mong đợi là 1) lưu trữ danh sách tất cả các thuộc tính kích hoạt sự kiện thay đổi, 2) Sử dụng sự phản chiếu để nhận giá trị của tất cả các thuộc tính đó khi các sự kiện được kích hoạt để chúng có thể 3) So sánh giá trị được lưu trữ của thuộc tính đã được thay đổi trước và sau khi sự kiện thay đổi và thay đổi được kích hoạt. – Alain

+0

1) Tại sao một đối tượng đang lắng nghe 'INotifyPropertyChanging' không chịu trách nhiệm tại sao nó lắng nghe sự kiện này? Từ một quan điểm trừu tượng, tôi nghĩ nó sẽ là không khôn ngoan đối với bất kỳ lập trình viên nào để giả định tại sao một người nào đó đang lắng nghe một sự kiện (ngoài việc biết sự kiện đã xảy ra và tại sao). Nếu bạn dự định sử dụng lại 'INotifyPropertiesChangedAndChangingWithValues' của bạn thì hãy mở rộng.Nếu nó đang được sử dụng một lần, một giao diện mới có vẻ như công việc bổ sung cho ít nếu có lợi thế. –

+1

Vì 1) phải mất trình tự xử lý N thời gian để làm điều đó và thứ tự trình thông báo sự kiện 1 lần. 2) Trình thông báo sự kiện là chuyên gia thông tin. 3) Có trình xử lý theo dõi tất cả các thuộc tính trong đối tượng mà sự kiện thay đổi thuộc tính của nó là giám sát là phạm vi creep và thiết kế xấu. – Alain

1

Không, bạn phải tự tạo của mình từ đầu.

Tôi đã từng làm điều tương tự trong dự án nghiên cứu của mình, Granite, nhưng tôi đã đi đến conlcusion rằng nó không đáng giá. Quá nhiều thuộc tính tôi làm việc với được tính toán, và phải chạy chúng hai lần chỉ để nâng cao một sự kiện là quá tốn kém.

+0

@Jonathan_Allen: Tôi đã làm như vậy, cảm ơn. – Alain

Các vấn đề liên quan