来自不同线程的 WPF NotifyPropertyChange
WPF NotifyPropertyChange from different thread
我的 VM 实现了 INotifyPropertyChanged 接口。我创建了另一个线程 T 来填充我绑定到 Xaml 的列表。填充列表后,我在线程 T 中调用 PropertyChanged,我的 UI 得到正确刷新。
我的问题是在什么情况下我需要使用 Dispatcher?为什么我不需要在我的案例中使用 Dispatcher?我认为当其他线程中的代码想要通过将更改排队到 UI 刷新队列来通知对 UI 线程的更改时使用 Dispatcher,例如从另一个线程向 ObservableCollection 添加项目,以及 UI 线程然后会从队列中拉取数据。
private List<string> _ListData;
public List<String> ListData
{
get
{
if (_ListData == null)
Initialise( () => ListData = ReturnSlow());
return _ListData;
}
set { _ListData = value; }
}
private List<string> ReturnSlow()
{
List<string> Test = new List<string>();
Test.Add("1");
Test.Add("2");
Thread.Sleep(2000);
return Test;
}
public void Initialise(Action initialiser)
{
Task t = new Task(() =>
{
initialiser();
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ListData"));
});
t.Start();
}
您的应用有一个 UI 主线程(通常 ManagedThreadId==1
)。如果你想从一个在其他线程上拉取的事件更新 UI,你必须使用调度程序。这里一个有用的测试是 Dispatcher.CheckAccess()
方法,如果代码在 UI 线程上,return 为真,如果在其他线程上,则为假。典型的调用类似于:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
如果您在主 window 中,您可以使用:
Dispatcher.BeginInvoke(...
如果您在其他环境中,例如视图模型,则使用:
Application.Current.Dispatcher.BeginInvoke(
调用与 BeginInvoke
如果您希望当前线程等到 UI 线程处理完调度代码,请使用 Invoke
,或者如果您希望当前线程继续而不等待操作在 [=55] 上完成,请使用 BeginInvoke
=]线程。
MessageBox、调度程序和 Invoke/BeginInvoke:
Dispatcher.Invoke
将阻止您的线程,直到 MessageBox 被关闭。
Dispatcher.BeginInvoke
将允许您的线程代码继续执行,而 UI 线程将阻塞 MessageBox 调用,直到它被解除。
CurrentDispatcher 与 Current.Dispatcher!
请注意 Dispatcher.CurrentDispatcher
,因为我对此的理解是 return 当前线程的 Dispatcher 而不是 UI 线程。一般来说,您是否对 UI 线程上的调度程序感兴趣 - Application.Current.Dispatcher
总是 returns this.
补充说明:
如果您发现您必须经常检查调度程序 CheckAccess,那么一个有用的辅助方法是:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
可以这样称呼:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});
我的 VM 实现了 INotifyPropertyChanged 接口。我创建了另一个线程 T 来填充我绑定到 Xaml 的列表。填充列表后,我在线程 T 中调用 PropertyChanged,我的 UI 得到正确刷新。
我的问题是在什么情况下我需要使用 Dispatcher?为什么我不需要在我的案例中使用 Dispatcher?我认为当其他线程中的代码想要通过将更改排队到 UI 刷新队列来通知对 UI 线程的更改时使用 Dispatcher,例如从另一个线程向 ObservableCollection 添加项目,以及 UI 线程然后会从队列中拉取数据。
private List<string> _ListData;
public List<String> ListData
{
get
{
if (_ListData == null)
Initialise( () => ListData = ReturnSlow());
return _ListData;
}
set { _ListData = value; }
}
private List<string> ReturnSlow()
{
List<string> Test = new List<string>();
Test.Add("1");
Test.Add("2");
Thread.Sleep(2000);
return Test;
}
public void Initialise(Action initialiser)
{
Task t = new Task(() =>
{
initialiser();
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ListData"));
});
t.Start();
}
您的应用有一个 UI 主线程(通常 ManagedThreadId==1
)。如果你想从一个在其他线程上拉取的事件更新 UI,你必须使用调度程序。这里一个有用的测试是 Dispatcher.CheckAccess()
方法,如果代码在 UI 线程上,return 为真,如果在其他线程上,则为假。典型的调用类似于:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
如果您在主 window 中,您可以使用:
Dispatcher.BeginInvoke(...
如果您在其他环境中,例如视图模型,则使用:
Application.Current.Dispatcher.BeginInvoke(
调用与 BeginInvoke
如果您希望当前线程等到 UI 线程处理完调度代码,请使用 Invoke
,或者如果您希望当前线程继续而不等待操作在 [=55] 上完成,请使用 BeginInvoke
=]线程。
MessageBox、调度程序和 Invoke/BeginInvoke:
Dispatcher.Invoke
将阻止您的线程,直到 MessageBox 被关闭。
Dispatcher.BeginInvoke
将允许您的线程代码继续执行,而 UI 线程将阻塞 MessageBox 调用,直到它被解除。
CurrentDispatcher 与 Current.Dispatcher!
请注意 Dispatcher.CurrentDispatcher
,因为我对此的理解是 return 当前线程的 Dispatcher 而不是 UI 线程。一般来说,您是否对 UI 线程上的调度程序感兴趣 - Application.Current.Dispatcher
总是 returns this.
补充说明:
如果您发现您必须经常检查调度程序 CheckAccess,那么一个有用的辅助方法是:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
可以这样称呼:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});