Một giải pháp gần đây tôi đã đưa ra là để đóng gói logic sự kiện văn vào một lớp chuyên dụng.
Lớp có một phương pháp nào gọi là Handle
trong đó có chữ ký giống như PropertyChangedEventHandler
đại biểu có nghĩa là nó có thể được đăng ký sự kiện PropertyChanged
của bất kỳ lớp mà thực hiện giao diện INotifyPropertyChanged
.
Lớp này chấp nhận các đại biểu như thường được sử dụng DelegateCommand
được sử dụng bởi hầu hết các triển khai WPF có nghĩa là nó có thể được sử dụng mà không phải tạo các lớp con.
Lớp trông như thế này:
public class PropertyChangedHandler
{
private readonly Action<string> handler;
private readonly Predicate<string> condition;
private readonly IEnumerable<string> properties;
public PropertyChangedHandler(Action<string> handler,
Predicate<string> condition, IEnumerable<string> properties)
{
this.handler = handler;
this.condition = condition;
this.properties = properties;
}
public void Handle(object sender, PropertyChangedEventArgs e)
{
string property = e.PropertyName ?? string.Empty;
if (this.Observes(property) && this.ShouldHandle(property))
{
handler(property);
}
}
private bool ShouldHandle(string property)
{
return condition == null ? true : condition(property);
}
private bool Observes(string property)
{
return string.IsNullOrEmpty(property) ? true :
!properties.Any() ? true : properties.Contains(property);
}
}
Sau đó bạn có thể đăng ký một tài sản thay đổi xử lý sự kiện như thế này:
var eventHandler = new PropertyChangedHandler(
handler: p => { /* event handler logic... */ },
condition: p => { /* determine if handler is invoked... */ },
properties: new string[] { "Foo", "Bar" }
);
aViewModel.PropertyChanged += eventHandler.Handle;
Các PropertyChangedHandler
chăm sóc kiểm tra PropertyName
của PropertyChangedEventArgs
và đảm bảo rằng handler
được gọi bởi các thay đổi thuộc tính phù hợp.
Lưu ý rằng PropertyChangedHandler cũng chấp nhận một vị từ để đại biểu xử lý có thể được gửi đi có điều kiện. Lớp này cũng cho phép bạn chỉ định nhiều thuộc tính để một trình xử lý đơn lẻ có thể được liên kết với nhiều thuộc tính trong một lần. Điều này có thể dễ dàng được mở rộng bằng cách sử dụng một số phương pháp mở rộng để đăng ký trình xử lý thuận tiện hơn cho phép bạn tạo trình xử lý sự kiện và đăng ký sự kiện PropertyChanged
trong một cuộc gọi phương thức duy nhất và chỉ định các thuộc tính sử dụng biểu thức thay cho chuỗi trông như thế này:
aViewModel.OnPropertyChanged(
handler: p => handlerMethod(),
condition: p => handlerCondition,
properties: aViewModel.GetProperties(
p => p.Foo,
p => p.Bar,
p => p.Baz
)
);
này về cơ bản nói rằng khi một trong hai Foo
, Bar
hoặc Baz
tính thay đổi handlerMethod
sẽ được gọi nếu handlerCondition
là đúng.
Quá tải phương thức OnPropertychanged
được cung cấp để đáp ứng các yêu cầu đăng ký sự kiện khác nhau.
Nếu, ví dụ, bạn muốn đăng ký một handler mà được gọi là cho bất kỳ thay đổi sự kiện bất động sản và luôn luôn thực hiện bạn chỉ có thể làm như sau:
aViewModel.OnPropertyChanged(p => handlerMethod());
Nếu, ví dụ, bạn muốn đăng ký một handler đó là luôn luôn thực hiện nhưng chỉ cho một sự thay đổi tài sản cụ thể duy nhất bạn có thể làm như sau:
aViewModel.OnPropertyChanged(
handler: p => handlerMethod(),
properties: aViewModel.GetProperties(p => p.Foo)
);
tôi đã tìm thấy phương pháp này rất hữu ích khi viết các ứng dụng WPF MVVM. Hãy tưởng tượng bạn có một kịch bản mà bạn muốn vô hiệu hóa một lệnh khi bất kỳ thuộc tính nào thay đổi. Sử dụng các phương pháp thông thường bạn sẽ phải làm một cái gì đó như thế này:
void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "Foo":
case "Bar":
case "Baz":
FooBarBazCommand.Invalidate();
break;
....
}
}
Nếu bạn thay đổi tên của bất kỳ thuộc tính ViewModel bạn sẽ cần phải nhớ để cập nhật các xử lý sự kiện để chọn các thuộc tính chính xác.
Sử dụng lớp PropertyChangedHandler
đã nêu ở trên, bạn có thể đạt được kết quả tương tự với những điều sau:
aViewModel.OnPropertyChanged(
handler: p => FooBarBazCommand.Invalidate(),
properties: aViewModel.GetProperties(
p => p.Foo,
p => p.Bar,
p => p.Baz
)
);
này bây giờ có thời gian biên dịch an toàn nên nếu bất kỳ thuộc tính ViewModel được đổi tên chương trình sẽ không biên dịch.
(Mặc dù thẳng thắn, nếu bạn thực sự muốn an toàn loại thích hợp, có lẽ bạn nên xem xét không sử dụng 'PropertyChangedEventArgs' ở tất cả và thay vào đó tuyên bố một trong những của riêng bạn mà chứa các 'đối tượng PropertyInfo' thay vì một chuỗi.) – Timwi