防止在 WPF 代码中使用 Dispatcher.Invoke [英] Prevent using Dispatcher.Invoke in WPF code

查看:21
本文介绍了防止在 WPF 代码中使用 Dispatcher.Invoke的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我天生就是一名网络和后端程序员.通常我会尽量避免制作 Windows 程序.现在我必须制作一个 WPF 客户端.

I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.

我有一个后台任务,每次都会引发一个事件.(它像轮询器一样工作,当满足条件时会引发事件).作为菜鸟,我编写了附加到事件的代码以更新 UI.

I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.

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

这是一个例外,因为我不在同一个线程上.经过一番谷歌搜索后,我发现我应该更改代码:

This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:

    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. Are there better ways to do this?

推荐答案

关于这个:

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

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

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

Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.

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

Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.

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

This means that there should be nothing like this:

UserWindow.Visibility = Visibility.Hidden;

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

anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.

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

Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:

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

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

Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.

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

Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.

这样做时,还可以方便地将来自该接口的 PropertyChanged 事件编组到 UI 线程,这样您就不必再担心设置使用 Dispatcher.

When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.

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

Therefore our first step is to have all our ViewModels inherit from a class like this:

(摘自这个答案):

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 和这是数据绑定期望:

Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:

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

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

Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:

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

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

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

As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.

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

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

One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.

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

You can read more about that in the link and also the MSDN DataBinding page linked above.

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

Let me know if you need further details.

这篇关于防止在 WPF 代码中使用 Dispatcher.Invoke的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆