繁体   English   中英

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

[英]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 中的一切。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM