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

问题描述 投票:1回答:2

我正在使用WPF应用程序,该应用程序的业务逻辑由类库(无MVVM)处理。业务逻辑的大多数属性是依赖项属性,它允许轻松地将数据绑定到WPF UI。

我有一个显示项目集合的数据网格(类的依赖属性:ObservableCollection<ItemEntry> 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");
    }

效果很好,但是添加处理方法会在ItemTemplate的依赖项属性上引发System.InvalidOperationException

    private void AnalyzeItemEntries(ItemEntry entry)
    {
        Log.Debug("Begin");
        ItemEntryUpdateAnalyzer.Analyze(ItemTemplate, Company, entry); //InvalidOperationException
        Log.Debug("End");
    }

这是由于Analyze方法的参数属于UI主线程这一事实。因此,我尝试使用调度程序通过执行以下操作来提供正确的上下文:

    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(https://github.com/force-net/DeepCloner)将ItemTemplate和ItemEntry深层复制到局部变量中:

    private async void AnalyzeButton_OnClick(object sender, RoutedEventArgs e)
    {
        Log.Debug($"==== Main thread ID {Thread.CurrentThread} ===");

        ItemTemplate localTemplate = ItemTemplate.DeepClone();
        ObservableCollection<ItemEntry> localEntryCollection = EntryCollection.DeepClone();
        foreach (ItemEntry entry in localEntryCollection)
        {
            await Task.Run(() => AnalyzeItemEntries(localTemplate, entry));
        }
    }

    private void AnalyzeItemEntries(ItemTemplate template, ItemEntry entry)
    {
        Log.Debug($"Begin {entry.ItemCode}");
        ItemEntryUpdateAnalyzer.Analyze(template, Company, entry);
        Log.Debug($"End {entry.ItemCode}");
    }

我仍然遇到相同的错误。该问题似乎仅与依赖项属性有关,因为访问entry.ItemCode(标准属性)有效,而访问entry.Action则无效。

wpf asynchronous task dependency-properties dispatcher
2个回答
0
投票

依赖关系属性是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);
}

取决于ItemTemplate /Company/ Entry的形状,您可能需要将它们制成“深”副本。另外,根据AnalyzeItemEntries的行为,可能需要更改该方法以返回值,而不是作为副作用更新对象。


0
投票

感谢大家的帮助,理想的解决方案是使用常规属性而不是依赖属性,但是我不能在我的应用程序中这样做。

我已经将DispatcherPriority设置为ApplicationIdle。这会给UI带来一些响应。

            await Dispatcher?.InvokeAsync(() =>
            {
                ItemEntryUpdateAnalyzer.Analyze(ItemTemplate, Company, entry);

            },
            DispatcherPriority.ApplicationIdle);
© www.soinside.com 2019 - 2024. All rights reserved.