Wpf 对UI绑定依赖项属性的异步处理
我正在开发一个WPF应用程序,它的业务逻辑由类库处理(没有MVVM)。业务逻辑的大多数属性都是依赖性属性,这允许轻松地将数据绑定到WPF UI 我有一个datagrid,它显示一组项(类的依赖属性):Wpf 对UI绑定依赖项属性的异步处理,wpf,asynchronous,task,dependency-properties,dispatcher,Wpf,Asynchronous,Task,Dependency Properties,Dispatcher,我正在开发一个WPF应用程序,它的业务逻辑由类库处理(没有MVVM)。业务逻辑的大多数属性都是依赖性属性,这允许轻松地将数据绑定到WPF UI 我有一个datagrid,它显示一组项(类的依赖属性):ObservableCollection EntryCollection 目标是为EntryCollection中的每个项异步调用ItemEntryUpdateAnalyzer.Analyze(ItemTemplate、Company、entry)静态方法,因为处理需要几秒钟 我首先做了以下工作:
ObservableCollection EntryCollection
目标是为EntryCollection中的每个项异步调用ItemEntryUpdateAnalyzer.Analyze(ItemTemplate、Company、entry)
静态方法,因为处理需要几秒钟
我首先做了以下工作:
private async void AnalyzeButton_OnClick(object sender, RoutedEventArgs e)
{
List<Task> tasks = EntryCollection.Select(entry => Task.Run(() => AnalyzeItemEntries())).ToList();
await Task.WhenAll(tasks);
}
private void AnalyzeItemEntries()
{
Log.Debug("Begin");
Thread.Sleep(500);
Log.Debug("End");
}
private void AnalyzeItemEntries(ItemEntry entry)
{
Log.Debug("Begin");
/*tried with InvokeAsync as well*/
Dispatcher?.BeginInvoke((Action) (() =>
{
ItemEntryUpdateAnalyzer.Analyze(ItemTemplate, Company, entry);
}));
Log.Debug("End");
}
这是因为Analyze方法的参数属于主UI线程。因此,我尝试使用dispatcher通过执行以下操作来提供正确的上下文:
private async void AnalyzeButton_OnClick(object sender, RoutedEventArgs e)
{
List<Task> tasks = EntryCollection.Select(entry => Task.Run(() => AnalyzeItemEntries())).ToList();
await Task.WhenAll(tasks);
}
private void AnalyzeItemEntries()
{
Log.Debug("Begin");
Thread.Sleep(500);
Log.Debug("End");
}
private void AnalyzeItemEntries(ItemEntry entry)
{
Log.Debug("Begin");
/*tried with InvokeAsync as well*/
Dispatcher?.BeginInvoke((Action) (() =>
{
ItemEntryUpdateAnalyzer.Analyze(ItemTemplate, Company, entry);
}));
Log.Debug("End");
}
但这并没有真正的帮助,因为这会锁定主线程。问题是参数是由依赖属性绑定到UI的,普通属性似乎不会引发异常
编辑:
我尝试使用DeepCloner NuGet()将ItemTemplate和ItemEntry深度复制到局部变量:
private async void AnalyzeButton_OnClick(对象发送方,RoutedEventArgs e)
{
Log.Debug($”===主线程ID{thread.CurrentThread}==”;
ItemTemplate localTemplate=ItemTemplate.DeepClone();
ObservableCollection localEntryCollection=EntryCollection.DeepClone();
foreach(localEntryCollection中的ItemEntry条目)
{
等待任务。运行(()=>AnalyzeItemEntries(localTemplate,entry));
}
}
私有void AnalyzeItemEntries(ItemTemplate模板,ItemEntry条目)
{
Debug($“Begin{entry.ItemCode}”);
分析(模板、公司、条目);
Log.Debug($“End{entry.ItemCode}”);
}
我还是会犯同样的错误。问题似乎只与依赖项属性有关,因为访问
条目。ItemCode
(标准属性)有效,而访问条目。操作
无效 依赖项属性是UI级对象。后台线程(包括Task.Run
使用的线程)无法直接访问UI对象
一种解决方法是将UI属性复制到本地变量中,并将这些变量传递给后台线程,如下所示:
private async void AnalyzeButton_OnClick(object sender, RoutedEventArgs e)
{
var itemTemplate = ItemTemplate;
var company = Company;
List<Task> tasks = EntryCollection.Select(entry => Task.Run(() => AnalyzeItemEntries(itemTemplate, company, entry))).ToList();
await Task.WhenAll(tasks);
}
private async void AnalyzeButton_OnClick(对象发送方,RoutedEventArgs e)
{
var itemTemplate=itemTemplate;
var公司=公司;
List tasks=EntryCollection.Select(entry=>Task.Run(()=>AnalyzeItemEntries(itemTemplate,company,entry)).ToList();
等待任务。何时(任务);
}
根据
项目模板
/公司
/条目
的形状,您可能需要将其制作成“深度”副本。此外,根据AnalyzeItemEntries的行为,您可能需要更改该方法以返回值,而不是作为副作用更新对象。依赖项属性是UI级对象。后台线程(包括Task.Run
使用的线程)无法直接访问UI对象
一种解决方法是将UI属性复制到本地变量中,并将这些变量传递给后台线程,如下所示:
private async void AnalyzeButton_OnClick(object sender, RoutedEventArgs e)
{
var itemTemplate = ItemTemplate;
var company = Company;
List<Task> tasks = EntryCollection.Select(entry => Task.Run(() => AnalyzeItemEntries(itemTemplate, company, entry))).ToList();
await Task.WhenAll(tasks);
}
private async void AnalyzeButton_OnClick(对象发送方,RoutedEventArgs e)
{
var itemTemplate=itemTemplate;
var公司=公司;
List tasks=EntryCollection.Select(entry=>Task.Run(()=>AnalyzeItemEntries(itemTemplate,company,entry)).ToList();
等待任务。何时(任务);
}
根据
项目模板
/公司
/条目
的形状,您可能需要将其制作成“深度”副本。此外,根据AnalyzeItemEntries的行为,您可能需要更改该方法以返回值,而不是作为副作用更新对象。感谢大家的帮助,理想的解决方案是使用常规属性而不是依赖性属性,但在我的应用程序中我无法做到这一点
我已将DispatcherPriority
设置为ApplicationIdle
。这会给UI带来一些响应
await Dispatcher?.InvokeAsync(() =>
{
ItemEntryUpdateAnalyzer.Analyze(ItemTemplate, Company, entry);
},
DispatcherPriority.ApplicationIdle);
感谢大家的帮助,理想的解决方案是使用常规属性而不是依赖性属性,但我不能在我的应用程序中这样做 我已将
DispatcherPriority
设置为ApplicationIdle
。这会给UI带来一些响应
await Dispatcher?.InvokeAsync(() =>
{
ItemEntryUpdateAnalyzer.Analyze(ItemTemplate, Company, entry);
},
DispatcherPriority.ApplicationIdle);
异常具有堆栈跟踪,请检查它以获取有关错误发生的位置/原因的更多详细信息。这不是答案,但添加
.ConfigureAwait(false)时是否也会发生同样的情况出于好奇,我还尝试将.ConfigureAwait(false)
添加到whalll
中,不幸的是它没有起作用。@XAMIMAX:UI元素没有在Analyze
方法中传递。UI控件绑定到位于ItemTemplate
和ItemEntry
对象中的依赖项属性。这被认为是糟糕的设计吗?ItemTemplate的实现和ItemEntry是什么样子的?如果这些是您的模型/视图模型,那么您是否不需要DPs?通常的INPC应该足够了。您是否使用这些道具以前的值?这就是为什么要在模型上使用DP。或者您是否将xaml中的值绑定到您的模型?异常有堆栈跟踪,请查看它以获取有关错误发生的位置/原因的更多详细信息。这不是答案,但添加时是否也会发生同样的情况。ConfigureWait(false)出于好奇,我还尝试将.ConfigureAwait(false)
添加到whalll
中,不幸的是它没有起作用。@XAMIMAX:UI元素没有在Analyze
方法中传递。UI控件绑定到位于It中的依赖项属性