簡體   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