我天生就是一个网络和后端程序员。通常我会尝试使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的方法是声明Visibility性地将UI元素( IN XAML )的属性绑定到可以从外部操作的相关bool属性,如下所示:
Visibility
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}"> <!-- ... --> </UserWindow>
然后,您需要创建一个相关类,其中包含UI希望绑定到的属性。这称为 ViewModel 。
请注意,为了正确地支持双向WPF数据绑定,您的ViewModels必须实现INotifyPropertyChangedinterface。
INotifyPropertyChanged
这样做时,将PropertyChanged来自该接口的事件 编组到UI线程 也很方便,这样您就不必担心使用来设置ViewModel的属性Dispatcher。
PropertyChanged
Dispatcher
因此,我们的第一步是让我们所有的ViewModels都从这样的类继承:
(摘自此答案):
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的期望:
UserWindow
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分离,因此可测试且更具可伸缩性。
ShowUserWindow
同样的概念也适用于WPF中的一切。
有一个细节,我需要提到的是,我利用的技术的结合MarkupExtension,并IValueConverter以减少涉及到使用转换器的XAML样板。
MarkupExtension
IValueConverter
您可以在链接以及上面链接的MSDN DataBinding页面中了解有关此内容的更多信息。
让我知道您是否需要更多详细信息。