【问题标题】:WPF NotifyPropertyChange from different thread来自不同线程的 WPF NotifyPropertyChange
【发布时间】:2015-10-13 15:00:28
【问题描述】:

我的虚拟机实现了 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();
    }

【问题讨论】:

  • 只有当您的集合是 ObservableCollection 时,您才必须使用它

标签: c# wpf refresh


【解决方案1】:

您的应用有一个主 UI 线程(通常是 ManagedThreadId==1)。如果您想从一个在其他线程上拉取的事件更新 UI,您必须使用调度程序。这里一个有用的测试是 Dispatcher.CheckAccess() 方法,如果代码在 UI 线程上,则返回 true,如果在其他线程上,则返回 false。典型的调用如下所示:

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));
    }));
}

如果您在主窗口中,您可以使用:

Dispatcher.BeginInvoke(...

如果您在其他上下文中,例如视图模型,请使用:

Application.Current.Dispatcher.BeginInvoke(  

Invoke 与 BeginInvoke
如果您希望当前线程等待 UI 线程处理完调度代码,请使用 Invoke;如果您希望当前线程继续运行而不等待 UI 线程上的操作完成,请使用 BeginInvoke

MessageBox、Dispatchers 和 Invoke/BeginInvoke:
Dispatcher.Invoke 将阻止您的线程,直到 MessageBox 被解除。
Dispatcher.BeginInvoke 将允许您的线程代码继续执行,同时UI 线程将阻塞 MessageBox 调用,直到它被解除。

CurrentDispatcher 与 Current.Dispatcher!
请注意Dispatcher.CurrentDispatcher,因为我对此的理解是,它将为当前线程而不是 UI 线程返回一个 Dispatcher。通常你对 UI 线程上的调度程序感兴趣 - Application.Current.Dispatcher 总是返回这个。

补充说明:
如果您发现必须经常检查调度程序 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));
});

【讨论】:

  • 但就我而言,为什么我不需要使用 Dispatcher?
  • 在您的特定情况下,您完全依赖 INotifyPropertyChanged 来通知控件更改模型 - 那么不,您不需要调度程序。但是,如果您在 VM 中公开任何非线程安全的属性(例如 ObservableCollection 或 WritableBitmap 等),您可能需要使用 Dispatcher 在主 (UI) 线程上调用它们的方法。
  • @olitee,请看我的编辑。线程安全是什么意思,例如在 ObservableCollection 中? INotifyPropertyChanged 机制如何与 UI 窗口中的刷新队列协同工作?
  • @YaelB,您能否详细解释一下 Dispatcher 如何处理 UI 窗口的刷新队列?以及 INotifyProepertyChanged 如何从不同线程通知 UI?
猜你喜欢
  • 1970-01-01
  • 2019-07-14
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-03
相关资源
最近更新 更多