防止使用调度程序。WPF代码中调用

时间:2022-08-28 07:43:24

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.

我天生就是一个网络和后端程序员。正常情况下,我试着做windows程序的avaoid。现在我要创建一个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.

我有一个后台任务,每次都有一个事件发生。(它像一个轮询器一样工作,当标准被满足时,就会产生一个事件)。当我是Noob时,我编写了这个附加到事件以更新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?

这是可行的,但这不是唯一的事件,因此使我的代码变得非常糟糕。有更好的方法吗?

1 个解决方案

#1


13  

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 Declarative DataBinding as opposed to the traditional imperative approach.

基本上,您的自定义逻辑(即业务逻辑或应用程序逻辑)和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元素(在XAML中)的可见性绑定到相关的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. This is called a ViewModel.

然后,您需要创建一个相关的类,其中包含UI希望绑定到的属性。这被称为视图模型。

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线程,我们就可以继续创建一个相关的视图模型,在这种情况下,它适合UserWindow,并且它正在对期望进行数据绑定:

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. One simple way to do that is in the Window's constructor:

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

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. 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.

正如您在本例中所看到的,实际上不需要在过程代码中操作UI元素的属性。这很好,不仅因为它解决了线程关联问题(因为现在您可以从任何线程中设置ShowUserWindow属性),而且还因为它使您的视图模型和逻辑完全与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.

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

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

您可以在链接和上面链接的MSDN数据库页面中了解更多。

Let me know if you need further details.

如果你需要更多的细节,请告诉我。

#1


13  

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 Declarative DataBinding as opposed to the traditional imperative approach.

基本上,您的自定义逻辑(即业务逻辑或应用程序逻辑)和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元素(在XAML中)的可见性绑定到相关的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. This is called a ViewModel.

然后,您需要创建一个相关的类,其中包含UI希望绑定到的属性。这被称为视图模型。

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线程,我们就可以继续创建一个相关的视图模型,在这种情况下,它适合UserWindow,并且它正在对期望进行数据绑定:

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. One simple way to do that is in the Window's constructor:

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

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. 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.

正如您在本例中所看到的,实际上不需要在过程代码中操作UI元素的属性。这很好,不仅因为它解决了线程关联问题(因为现在您可以从任何线程中设置ShowUserWindow属性),而且还因为它使您的视图模型和逻辑完全与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.

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

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

您可以在链接和上面链接的MSDN数据库页面中了解更多。

Let me know if you need further details.

如果你需要更多的细节,请告诉我。