防止在WPF代码中使用Dispatcher.Invoke

我本质上是一个网络和后端程序员。 通常我尝试avaoid制作Windows程序。 现在我必须创建一个WPF客户端。

我有一个后台任务,每隔一段时间就会举办一次活动。 (它像轮询器一样工作,当满足标准时,会引发一个事件)。 Noob和我一样,我写了这个附加到事件的代码来更新UI。

private void IsDisconnectedEvent() { UserWindow.Visibility = Visibility.Hidden; DisconnectWindow.Visibility = Visibility.Visible; } 

这给出了一个例外,因为我不在同一个线程上。 经过一些谷歌搜索后,我发现我应该改变代码:

  private void IsDisconnectedEvent() { Dispatcher.Invoke(() => { UserWindow.Visibility = Visibility.Hidden; DisconnectWindow.Visibility = Visibility.Visible; }); } 

这是有效的,但这不是唯一的事件,因此使我的代码可怕的丑陋。 有没有更好的方法来做到这一点?

关于这个:

这是有效的,但这不是唯一的事件,因此使我的代码可怕的丑陋

是的 ,除非你理解并接受WPF心态,否则基于WPF的代码肯定会非常糟糕。

基本上,您的自定义逻辑(AKA业务逻辑或应用程序逻辑)与WPF UI之间的所有交互都应Declarative DataBinding的forms显示,而不是传统的命令式方法。

这意味着应该没有这样的东西:

 UserWindow.Visibility = Visibility.Hidden; 

代码中的任何地方,只是因为引入类似的东西会使您的代码依赖于UI,因此只能在UI线程上执行。

相反,WPF的方法是声明Visibility地将UI元素( IN XAML )的Visibility属性DataBind转换为可以从外部操作的相关bool属性,如下所示:

    

然后,您需要创建一个包含UI期望绑定到的属性的相关类。 这称为ViewModel

请注意,为了正确支持双向WPF DataBinding,ViewModel必须实现INotifyPropertyChanged接口 。

这样做时,将该接口的PropertyChanged事件编组到UI线程也很方便,这样您就不必再担心使用Dispatcher设置ViewModel的属性了。

因此,我们的第一步是让所有的ViewModelinheritance自这样的类:

(取自这个答案 ):

 public class PropertyChangedBase:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { //Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter: Application.Current.Dispatcher.BeginInvoke((Action) (() => { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); })); } } 

一旦我们对UI线程进行了属性更改通知调度 ,我们就可以继续创建一个适合的ViewModel,在这种情况下,它适合UserWindow及它的DataBinding期望:

 public class UserViewModel: PropertyChangedBase { private bool _showUserWindow; public bool ShowUserWindow { get {return _showUserWindow; } set { _showUserWindow = value; OnPropertyChanged("ShowUserWindow"); //This is important!!! } } } 

最后,您需要将Window的DataContext设置为其对应的ViewModel的实例。 一种简单的方法是在Window的构造函数中:

 public UserWindow() //Window's Constructor { InitializeComponent(); //this is required. DataContext = new UserViewModel(); //here we set the DataContext } 

正如您在此示例中所看到的,实际上不需要在过程代码中操纵UI元素的属性。 这很好,不仅因为它解决了Thread Affinity问题(因为现在你可以从任何线程设置ShowUserWindow属性),而且因为它使你的ViewModel和逻辑与UI完全分离,因此可测试且更具可伸缩性。

同样的概念适用于WPF中的一切。

我需要提到的一个细节是,我正在使用结合MarkupExtensionIValueConverter的技术,以减少使用转换器所涉及的XAML样板。

您可以在链接以及上面链接的MSDN DataBinding页面中阅读有关该内容的更多信息。

如果您需要更多详细信息,请告诉我。