[英]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 中的一切。
我需要提及的一個細節是,我正在使用一種結合MarkupExtension
和IValueConverter
的技術,以減少使用轉換器時涉及的 XAML 樣板。
您可以在鏈接以及上面鏈接的 MSDN DataBinding 頁面中閱讀更多相關信息。
如果您需要更多詳細信息,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.