WPF has a powerful data binding mechanism, by which it is easy to implement MVVM and MVC pattern for UI application.
But thing gets not so beautiful once you introduced multi-thread into the app. Somehow, it means you have to manually call dispatcher method to synchronize the thread context, or else INotifyPropertyChanged contract causes cross thread violation, which is likely to drive UI component throw exception. But tons of calls to Dispatcher.Invoke or Dispatcher.BeginInvoke make you code ugly and hard to maintain. And it is boring to call dispatcher everytime you try to write view model properties. Is there any solution to this embarrassed situation?
The answer is Sync Notify pattern.By analyzing the WPF data binding data flow, you can find the best point to behave the thread synchronization is at the boundary of your code and WPF implementation, which is the property changed event raiser. To sync the thread context here makes your code clean and effective. Typically, you might implement the INotifyPropertyChanged in following way:
And you might implement the property of view model as following:
This implementation works perfect in single thread context , but fails in multi-thread context. So we introduce a new event raiser implementation, which synchronize the thread context before raising the event:
But thing gets not so beautiful once you introduced multi-thread into the app. Somehow, it means you have to manually call dispatcher method to synchronize the thread context, or else INotifyPropertyChanged contract causes cross thread violation, which is likely to drive UI component throw exception. But tons of calls to Dispatcher.Invoke or Dispatcher.BeginInvoke make you code ugly and hard to maintain. And it is boring to call dispatcher everytime you try to write view model properties. Is there any solution to this embarrassed situation?
The answer is Sync Notify pattern.By analyzing the WPF data binding data flow, you can find the best point to behave the thread synchronization is at the boundary of your code and WPF implementation, which is the property changed event raiser. To sync the thread context here makes your code clean and effective. Typically, you might implement the INotifyPropertyChanged in following way:
#region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void Notify(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #endregion
#region Notify Property ViewModelProperty private string viewModelPropertyBackField; public string ViewModelProperty { get { return viewModelPropertyBackField; } set { if (viewModelPropertyBackField == value) return; viewModelPropertyBackField = value; Notify("ViewModelProperty"); } } #endregion
#region Sync INotifyPropertyChanged Members protected void SyncNotify(string propertyName, bool wait = false, Dispatcher dispatcher = null) { if (PropertyChanged == null) return; dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher; if (dispatcher.Thread == Thread.CurrentThread) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } else { if (wait) { dispatcher.Invoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName)); } else { dispatcher.BeginInvoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName)); } } }
Posted via email from 米良的实验室