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

C# 防止在WPF代码中使用Dispatcher.Invoke,c#,wpf,C#,Wpf,我天生就是一名网络和后端程序员。通常情况下,我会尽量避免制作windows程序。现在我要做一个WPF客户端 我有一个背景任务,每次都会引发一个事件。(它像一个民意测验者一样工作,当满足标准时,就会引发一个事件)。尽管我是Noob,我还是编写了这个附在事件上的代码来更新UI private void IsDisconnectedEvent() { UserWindow.Visibility = Visibility.Hidden; D

我天生就是一名网络和后端程序员。通常情况下,我会尽量避免制作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 UI之间的所有交互都应该以声明性的形式体现,而不是传统的命令式方法

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

UserWindow.Visibility = Visibility.Hidden;
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
   <!-- ... -->
</UserWindow>
在代码中的任何位置,这仅仅是因为引入这样的东西会使代码依赖于UI,因此只能在UI线程上执行

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

UserWindow.Visibility = Visibility.Hidden;
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
   <!-- ... -->
</UserWindow>
一旦我们将属性更改通知分派到UI线程,我们就可以继续创建一个相关的ViewModel,该ViewModel在本例中适用于
UserWindow
及其数据绑定预期:

public class UserViewModel: PropertyChangedBase
{
    private bool _showUserWindow;
    public bool ShowUserWindow
    {
        get {return _showUserWindow; }
        set
        {
            _showUserWindow = value;
            OnPropertyChanged("ShowUserWindow"); //This is important!!!
        }
    }
}
最后,您需要将窗口设置为其相应ViewModel的实例。一种简单的方法是在窗口的构造函数中:

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

    DataContext = new UserViewModel(); //here we set the DataContext
}
正如您在本例中所看到的,实际上不需要在过程代码中操纵UI元素的属性。这不仅是因为它解决了线程关联性问题(因为现在您可以从任何线程设置
ShowUserWindow
属性),而且还因为它使您的ViewModels和逻辑与UI完全解耦,因此可测试性和可伸缩性更高

同样的概念也适用于WPF中的所有内容。

我需要提到的一个细节是,为了减少使用转换器所涉及的XAML样板文件,我正在使用一种技术

您可以在链接和上面链接的MSDN数据绑定页面中了解更多信息


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

您可以使用
后台工作人员。除此之外,
Dispatcher.Invoke()
是一个不错的选择。此外,您还可以将Dispatcher调用包装到一个方法中,例如,
PropagateChangesToUI(UIState newState){Dispatcher.Invoke([…]);}
。它们专门用于各种任务,因此您使用哪一个取决于您正在做什么。如果您正在按进度进行更新,请使用
IProgress
,如果您在一段时间后调用代码,请使用
dispatchermer
,如果您在后台线程中执行CPU限制的工作,则可以使用
BackgroundWorker
。依此类推。-1从我看来,这是一个很好的答案,但我不同意wpf完全是关于mvvm模式的。mvvm是一种模式,就是这样,只是一种模式。wpf也可以与mvp模式一起使用,这也只是一种模式。当然,比起wpf和mvp,wpf和mvvm更喜欢彼此,但我仍然不同意您如此强制使用mvvm模式。这就像说所有在winforms中开发ui应用程序的人都是傻瓜和马一样:)呵呵,你不知道mvp是什么?伙计。。。无可奉告:)@devhedgehog我是在讽刺老兄。。。我知道什么是MVP,但我完全忽略了这一点,因为这与我完全无关。。你现在明白了吗?你描述它的方式听起来像是我在寻找的。使用Web应用程序,我构建了具有清晰关注点分离的MVC应用程序。我会去阅读有关MVVM的文章,我想这会让我走得更远……WPF会自动将所有PropertyChanged事件封送到UI线程,Dispatcher.BeginInvoke没有任何意义。但是,对于第二个主WPF事件-CollectionChanged,情况并非如此。