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



我天生就是一名web和后端程序员。通常我会尝试制作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的代码肯定会非常可怕。

基本上,自定义逻辑(也称为业务逻辑或应用程序逻辑)和WPF UI之间的所有交互都应该以声明性数据绑定的形式表现出来,而不是传统的命令式方法。

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

UserWindow.Visibility = Visibility.Hidden;

在代码中的任何位置,因为引入这样的东西会使代码依赖于UI,因此只能在UI线程上执行。

相反,WPF的方法是声明性地将UI元素(IN XAML)的Visibility属性数据绑定到可以从外部操作的相关布尔属性,如下所示:

<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>

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

请注意,为了正确支持双向WPF数据绑定,ViewModels必须实现INotifyPropertyChanged接口。

执行此操作时,还可以方便地将该接口中的PropertyChanged事件编组到UI线程,这样您就不必再担心使用Dispatcher设置ViewModel的属性了。

因此,我们的第一步是让我们所有的ViewModels从这样的类继承:

(取自此答案):

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属性),还因为它使ViewModels和逻辑与UI完全解耦,从而可测试且更具可扩展性。

同样的概念也适用于WPF中的EVERYTHING

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

您可以在链接以及上面链接的MSDN DataBinding页面中了解更多信息。

如果你需要更多的细节,请告诉我。

最新更新