Wpf 对UI绑定依赖项属性的异步处理

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)静态方法,因为处理需要几秒钟 我首先做了以下工作:

我正在开发一个WPF应用程序,它的业务逻辑由类库处理(没有MVVM)。业务逻辑的大多数属性都是依赖性属性,这允许轻松地将数据绑定到WPF UI

我有一个datagrid,它显示一组项(类的依赖属性):
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中的依赖项属性