[英]Prevent using Dispatcher.Invoke in WPF code
我本质上是一名网络和后端程序员。 通常我会尽量避免制作 Windows 程序。 现在我必须制作一个 WPF 客户端。
我有一个后台任务,每次都会引发一个事件。 (它像轮询器一样工作,当满足条件时会引发事件)。 我是菜鸟,我编写了附加到事件的代码以更新 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 之间的所有交互都应以声明性数据绑定的形式表现出来,而不是传统的命令式方法。
这意味着不应该有这样的事情:
UserWindow.Visibility = Visibility.Hidden;
在你的代码中的任何地方,仅仅因为引入这样的东西会使你的代码依赖于 UI,因此只能在 UI 线程上执行。
相反,WPF 方法是将 UI 元素 ( IN XAML ) 的Visibility
属性声明性地绑定到您可以从外部操作的相关 bool 属性,如下所示:
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>
然后,您需要创建一个相关类,其中包含 UI 期望绑定到的属性。 这称为ViewModel 。
请注意,为了正确支持双向 WPF 数据绑定,您的 ViewModel 必须实现INotifyPropertyChanged
接口。
这样做时,将来自该接口的PropertyChanged
事件编组到 UI 线程也很方便,这样您就不必再担心使用Dispatcher
设置 ViewModel 的属性。
因此,我们的第一步是让我们所有的 ViewModel 都继承自这样的类:
(取自这个答案):
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 元素的属性。 这很好,不仅因为它解决了线程关联性问题(因为现在您可以从任何线程设置ShowUserWindow
属性),还因为它使您的 ViewModel 和逻辑与 UI 完全分离,从而可测试且更具可扩展性。
这个相同的概念适用于 WPF 中的一切。
我需要提及的一个细节是,我正在使用一种结合MarkupExtension
和IValueConverter
的技术,以减少使用转换器时涉及的 XAML 样板。
您可以在链接以及上面链接的 MSDN DataBinding 页面中阅读更多相关信息。
如果您需要更多详细信息,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.