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));
}
Và 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.
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ể. –
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
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