简体   繁体   English

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

[英]Prevent using Dispatcher.Invoke in WPF code

I'm a web and backend programmer by nature.我本质上是一名网络和后端程序员。 Normally I try to avaoid making windows programs.通常我会尽量避免制作 Windows 程序。 Now I have to make a WPF client.现在我必须制作一个 WPF 客户端。

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.我是菜鸟,我编写了附加到事件的代码以更新 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?有没有更好的方法来做到这一点?

Regarding this:关于这一点:

This works, but this is not the only event and thus makes my code horrible ugly这有效,但这不是唯一的事件,因此使我的代码变得可怕丑陋

Yes , your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality .是的,除非您了解并接受WPF 心态,否则您的基于 WPF 的代码肯定会非常糟糕。

Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of DeclarativeDataBinding as opposed to the traditional imperative approach.基本上,您的自定义逻辑(AKA 业务逻辑或应用程序逻辑)和 WPF UI 之间的所有交互都应声明性数据绑定的形式表现出来,而不是传统的命令式方法。

This means that there should be nothing like this:这意味着不应该有这样的事情:

UserWindow.Visibility = Visibility.Hidden;

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.在你的代码中的任何地方,仅仅因为引入这样的东西会使你的代码依赖于 UI,因此只能在 UI 线程上执行。

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:相反,WPF 方法是将 UI 元素 ( IN XAML ) 的Visibility属性声明性地绑定到您可以从外部操作的相关 bool 属性,如下所示:

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

Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to.然后,您需要创建一个相关类,其中包含 UI 期望绑定到的属性。 This is called a ViewModel .这称为ViewModel

Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface .请注意,为了正确支持双向 WPF 数据绑定,您的 ViewModel 必须实现INotifyPropertyChanged接口

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 .这样做时,将来自该接口的PropertyChanged事件编组到 UI 线程也很方便,这样您就不必再担心使用Dispatcher设置 ViewModel 的属性。

Therefore our first step is to have all our ViewModels inherit from a class like this:因此,我们的第一步是让我们所有的 ViewModel 都继承自这样的类:

(taken from 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));
        }));
    }
}

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:一旦我们将属性更改通知调度到 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!!!
        }
    }
}

Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel.最后,您需要将 Window 的DataContext设置为其相应 ViewModel 的实例。 One simple way to do that is in the Window's constructor:一种简单的方法是在 Window 的构造函数中:

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

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

As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code.正如您在此示例中所见,实际上无需在过程代码中操作 UI 元素的属性。 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.这很好,不仅因为它解决了线程关联性问题(因为现在您可以从任何线程设置ShowUserWindow属性),还因为它使您的 ViewModel 和逻辑与 UI 完全分离,从而可测试且更具可扩展性。

This same concept applies to EVERYTHING in WPF.这个相同的概念适用于 WPF 中的一切。

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.我需要提及的一个细节是,我正在使用一种结合MarkupExtensionIValueConverter的技术,以减少使用转换器时涉及的 XAML 样板。

You can read more about that in the link and also the MSDN DataBinding page linked above.您可以在链接以及上面链接的 MSDN DataBinding 页面中阅读更多相关信息。

Let me know if you need further details.如果您需要更多详细信息,请告诉我。

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

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