wpf - 从后台线程更新 DataContext

标签 wpf async-await ui-thread

我在这样的后台线程中为 wpf 窗口获取数据 [framework 4.0 with async/await]:

async void refresh()
{
    // returns object of type Instances
    DataContext = await Task.Factory.StartNew(() => serviceagent.GetInstances());
    var instances = DataContext as Instances;
    await Task.Factory.StartNew(() => serviceagent.GetGroups(instances));
    // * problem here * instances.Groups is filled but UI not updated
}

当我在 GetInstances 中包含 GetGroups 的操作时,UI 会显示组。
当我在单独的操作中更新时,DataContext 包括正确的组,但 UI 不显示它们。

GetGroups()我包含的方法NotifyCollectionChangedAction.Reset对于ObservableCollection组,这没有帮助。
更奇怪的是我调用NotifyCollectionChangedAction.Reset列表中只有一次,但执行了 3 次,而列表有 10 项?!

我可以通过写来解决这个问题:

DataContext = await Task.Factory.StartNew(() => serviceagent.GetGroups(instances));

但这是通过后台进程更新 DataContxt 和 UI 的常规方式吗?
其实我只想更新现有的DataContext而不重新设置?

编辑:serviceagent.GetGroups(instances)更详细:

public void GetGroups(Instances instances)
{
    // web call
    instances.Admin = service.GetAdmin();

    // set groups for binding in UI
    instances.Groups = new ViewModelCollection<Groep>(instances.Admin.Groups);

    // this code has no effect
    instances.Groups.RaiseCollectionChanged();
}

这里 ViewModelCollection<T>继承自 ObservableCollection<T>我添加了方法:

public void RaiseCollectionChanged()
{
    var handler = CollectionChanged;
    if (handler != null)
    {
        Trace.WriteLine("collection changed");
        var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        handler(this, e);
    }
}

最佳答案

您的代码的 async 部分有几点很突出:

基于这些,我也推荐我的intro to async博文。

关于实际问题...

从后台线程更新数据绑定(bind)代码总是很棘手。我建议您将 ViewModel 数据视为 UI 的一部分(可以说是“逻辑 UI”)。所以可以在后台线程上检索数据,但更新实际 VM 值应该在 UI 线程上完成。

这些更改使您的代码看起来更像这样:

async Task RefreshAsync()
{
  var instances = await TaskEx.Run(() => serviceagent.GetInstances());
  DataContext = instances;
  var groupResults = await TaskEx.Run(() => serviceagent.GetGroups(instances));
  instances.Admin = groupResults.Admin;
  instances.Groups = new ObservableCollection<Group>(groupResults.Groups);
}

public GroupsResult GetGroups(Instances instances)
{
  return new GroupsResult
  {
    Admin = service.GetAdmin(),
    Groups = Admin.Groups.ToArray(),
  };
}

接下来需要检查的是Instances 是否实现了INotifyPropertyChanged。设置 Groups 时不需要引发 Reset 集合更改事件;由于 GroupsInstances 上的一个属性,因此 Instances 有责任引发 INotifyPropertyChanged.PropertyChanged

或者,您可以将 DataContext 设置为最后:

async Task RefreshAsync()
{
  var instances = await TaskEx.Run(() => serviceagent.GetInstances());
  var groupResults = await TaskEx.Run(() => serviceagent.GetGroups(instances));
  instances.Admin = groupResults.Admin;
  instances.Groups = new ObservableCollection<Group>(groupResults.Admin.Groups);
  DataContext = instances;
}

关于wpf - 从后台线程更新 DataContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19903306/

相关文章:

WPF:我如何知道 Binding RelativeSource 是否找到了祖先

c# - WPF Datagrid 上的选定行 LostFocus mvvm 模式

java - 为什么不是 UI 线程的线程不能访问 View ?

从不同纤程调用需要主线程的函数

wpf - 使用Reactiveui订购可观察的集合

javascript - 当我尝试使用 json 获取 API 返回值时出错

javascript - Node.js 和 Express : wait for asynchronous operation before responding to HTTP request

javascript - javascript 中的 async/await 并发函数调用(链函数)

java - 即使我正在处理另一个线程,UI 线程也会卡住

wpf - 更改 WPF 中富文本框的对齐方式