我正在开始一个新线程,并尝试通过我的View模型中定义的属性更新UI元素,我能够毫无错误地执行此操作,但如果我尝试通过代码隐藏更新UI元素,则会抛出已知的UI访问错误("调用线程无法访问此对象,因为另一个线程拥有它。")。第一个问题是......两种方法的区别是什么?第二个问题是我在理想情况下在ViewModel中使用Disptacher吗?
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread th = new Thread(new ThreadStart(delegate()
{
textbox.Text = "Rajib";
}
));
th.Start();
}
//inside XAML
<TextBox x:Name="textbox" Text="{Binding UserInput, Mode=TwoWay}" />
public string UserInput
{
get { return _UserInput; }
set { _UserInput = value; OnPropertyChanged("UserInput"); }
}
//单击按钮时通过ICommand属性调用 public void ExecuteCommand(object obj) { InvokeCallThroughAnonymousDelegateThread(); }
private void InvokeCallThroughAnonymousDelegateThread()
{
ThreadStart start = delegate()
{
UserInput = "Calling from diff thread";
};
new Thread(start).Start();
}
答案 0 :(得分:7)
任何更新UI的尝试都必须在调度程序线程中完成。但是,对于属性更改事件,当从后台线程引发事件时,WPF 会自动为您分派。您可以在Bea Costa(以前的WPF数据绑定PM)博客上阅读更多相关信息:
http://bea.stollnitz.com/blog/?p=34
他们将为INotifyCollectionChanged
事件做同样的事情,但在之前的版本中从未接触到它。对于4.5 they will now be synchronizing collection changed events automatically以及INotifyPropertyChanged
。
答案 1 :(得分:2)
NotifyPropertyChanged通过事件由WPF更改了其线程上下文,但是后面的代码不会将线程上下文更改为UI线程。在您的代码隐藏中,请改用:
Task.Factory.StartNew(() =>
{
// Background work
}).ContinueWith((t) => {
// Update UI thread
}, TaskScheduler.FromCurrentSynchronizationContext());
关于何时直接使用Dispatcher,我有一个中型项目,我没有在任何ViewModel中使用Dispatcher。我用它来处理Xaml资源,弱事件处理,并在MefedMVVM和Prism中使用它,我也使用它。