gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 18:56:35 25 4
gpt4 key购买 nike

我天生就是一名网络和后端程序员。通常我尽量避免制作 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;
});
}

这行得通,但这不是唯一的事件,因此使我的代码非常难看。有没有更好的方法来做到这一点?

最佳答案

关于这个:

This works, but this is not the only event and thus makes my code horrible ugly

是的,除非您了解并接受 The WPF Mentality,否则您基于 WPF 的代码肯定会非常糟糕。

基本上,您的自定义逻辑(又名业务逻辑或应用程序逻辑)与 WPF UI 之间的所有交互应该声明 DataBinding 的形式体现,而不是传统的命令式方法。

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

UserWindow.Visibility = Visibility.Hidden;

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

相反,WPF 方法是以声明方式将 UI 元素 (IN XAML) 的 Visibility 属性数据绑定(bind)到您可以操作的相关 bool 属性外面,像这样:

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

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

请注意,为了正确支持双向 WPF 数据绑定(bind),您的 ViewModel 必须 Implement the INotifyPropertyChanged interface

这样做时,还可以方便地将来自该接口(interface)的 PropertyChanged 事件编码到 UI 线程,这样您就不必再担心设置使用 Dispatcher 的 ViewModel 属性。

因此我们的第一步是让我们所有的 ViewModel 都继承自这样一个类:

(取自 this answer ):

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));
}));
}
}

一旦我们将 Property Change Notification Dispatch 发送到 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!!!
}
}
}

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

public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.

DataContext = new UserViewModel(); //here we set the DataContext
}

如您在此示例中所见,实际上不需要在程序代码中操作 UI 元素的属性。这很好,不仅因为它解决了 Thread Affinity 问题(因为现在您可以从任何线程设置 ShowUserWindow 属性),还因为它使您的 ViewModels 和逻辑完全与 UI 分离,因此可测试且更具可扩展性。

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

我需要提及的一个细节是,我正在使用 Combining MarkupExtension and IValueConverter 技术来减少使用转换器时涉及的 XAML 样板文件。

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

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

关于c# - 防止在 WPF 代码中使用 Dispatcher.Invoke,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21883735/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com