Wpf 从后台线程更新DataContext
我在后台线程中为wpf窗口获取数据,如下所示[framework 4.0 with async/await]:Wpf 从后台线程更新DataContext,wpf,async-await,ui-thread,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;
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()
方法中,我为组的observedcollection
添加了NotifyCollectionChangedAction.Reset
,这没有帮助。更奇怪的是,我只对列表调用了一次
NotifyCollectionChangedAction.Reset
,但执行了三次,而列表有十项
我可以通过写作来解决这个问题:
DataContext = await Task.Factory.StartNew(() => serviceagent.GetGroups(instances));
但这是通过backgound进程更新DataContext和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();
}
似乎对什么是DataContext有点困惑。DataContext不是必须更新的特殊对象。它是对要绑定到UI的一个或多个对象的引用。每当您对这些对象进行更改时,都会通知UI(如果您实现了正确的接口) 因此,除非您显式更改DataContext,否则您的UI无法猜测您现在想要显示一组不同的对象 事实上,在您的代码中,没有理由设置DataContext两次。只需将其设置为要显示的最后一组对象。事实上,由于您处理相同的数据,因此没有理由使用两个任务:
async Task refresh()
{
// returns object of type Instances
DataContext=await Task.Factory.StartNew(() => {
var instances = serviceagent.GetInstances();
return serviceagent.GetGroups(instances);
});
}
注意:
您不应该使用
异步void
签名。它只用于fire and forget事件处理程序,您不关心它们是否成功。原因是不能等待async void
方法,因此没有人知道它是否成功。在代码的async
部分有几点很突出:
- 我在我的MSDN文章中解释。总之,
是void
方法的一种不自然的返回类型,因此它有一些怪癖,特别是在异常处理方面async
- ,正如我在博客上解释的
- 虽然不完全是必需的,但遵循以下指南是一个好主意:;遵循这些命名约定(etc)将有助于其他开发人员维护代码
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(),
};
}
我发现
RaiseCollectionChanged
对DataContext
绑定到的属性Groups
没有影响。我只需要通知:instances.RaisePropertyChanged(“组”)代码>如果XAML文件中的绑定正确,我认为问题出在GetGroups方法中。你能告诉我们这个方法吗?基本上代码就是{instances.Groups=newviewmodelcollection(webcall);RaiseCollectionChanged();}
请把代码放在问题中,而不是注释中。同时添加任何缺失的相关代码。在哪里调用Reset?它与问题的其余部分有什么关系?async void正在工作,为什么我需要async任务?注意GetInstances()不是void方法,只有GetGroups()是void,我想我可以通过引用传递DataContext?感谢w.r.t.TaskEx.Run的伟大建议,我不知道。您还看到myPropertyChanged
中的缺陷。解决方案的一个缺点是需要引用UI项目中的所有数据契约。我只希望这些引用出现在我的ViewModel项目中,而不是instances.Groups=newobservedcollection(groupResults.Groups)
在UI项目中,我在VM项目中做了完全相同的事情,然后是RaisePropertyChanged(“Groups”)代码>。这不是更像是MVVM,而不是在视图中设置列表吗?
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(),
};
}
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;
}