【问题标题】:How to access a WPF control inside System.Threading.Tasks.Parallel-loop如何访问 System.Threading.Tasks.Parallel-loop 中的 WPF 控件
【发布时间】:2017-12-12 12:35:02
【问题描述】:

首先我不得不猜测我对异步没有太多经验。

我正在使用 VS2015 开发 WPF 应用程序,其中我有一个掩码设计器。 在那里,我可以通过拖放、移动和调整它们的大小将控件添加到画布。 大小和位置的更改会暂时存储在字典中,稍后通过单击“保存”按钮将其保存到数据库中。

为了优化更新速度,我认为最好用异步 System.Threading.Tasks.Parallel.ForEach 替换同步 foreach。 但是现在我有一个问题,我不能在那个循环内使用一些 WPF 控件——比如控件所在的画布——因为它们属于主线程。

如何在循环中访问这些控件?

这是我的代码中的一个专家,这让我在这个时候遇到了问题:

Parallel.ForEach(ChangedElements, (changedElement) =>
{
    FrameworkElement element = elementHelper.GetElementFromPanel(changedElement.Value, designerwindow.maskDesignerPanel);
    elementHelper.UpdateElementPositionAndSize(element, designerwindow, inputFieldList);
});

“ChangedElements”是包含已更改 WPF 元素名称的字典。 最初它是类型,但我将其更改为,因此键和值包含控件的名称/标识符。 “designerWindow”是对 WPF 窗口的引用,“maskDesignerPanel”是画布的名称。 在运行时,当我尝试从画布的子集合中获取元素时,方法 GetElementFromPanel 内部出现错误。 在那种情况下,我无法访问子集合,因为它属于主线程。

这里是GetElementFromPanel方法的代码:

    public FrameworkElement GetElementFromPanel(string elementName, Canvas panel)
    {
        FrameworkElement element = null;

        foreach (FrameworkElement child in panel.Children)
        {
            if (child.Name == elementName)
            {
                element = child;
                break;
            }
        }

        return element;
    }

【问题讨论】:

  • 你试过用dispatcher拉取值吗?
  • 。反正你不需要。有几十个重复的问题展示了如何使用 async/await、IPProgress 或根本不同步 - 如果您使用数据将您绑定到 ViewModel,您将不必担心跨线程访问。修改 ViewModel 属性并引发 INotifyPropertyChanged 事件足以让 WPF 知道 需要去拉动更改
  • PS,不要尝试使用调度程序或 .Invoke。这是 a) 不需要,因为引入了 async/await,b) 它是糟糕的 WPF。使用数据绑定,不要尝试直接修改控件
  • For optimizing the speed of the updates 你有性能问题吗?如果你这样做了,多线程不是答案。您看到的允许在屏幕上拖动/移动对象的应用程序不使用多线程。它们限制重绘以避免抖动。例如,他们在操作完成之前更新 UI,或者他们禁用 UI 更新。或者它们将移动和更改存储在列表中,并且仅在拖动操作结束时应用它们。 WPF 本身允许您指定动画、过渡、故事板
  • @Panagiotis Kanavos:我不直接修改控件。我只想将它们的位置和大小保存到我的一个数据库表中。

标签: c# wpf multithreading


【解决方案1】:

UPD 如果您有数千个元素和高频率的更新,那么快速且廉价的方法可以加快 GetElementFromPanel:通过唯一的名称键将所有元素放入 Dictionary 中,这样您就不必每次都扫描所有子元素。

关于多线程

  1. 在进行优化之前,您应该使用分析器证明您的假设,您的代码的哪一部分很重?
  2. 如果您发现 UI 更新缓慢,则无需执行多线程(您的所有线程都将等待主 UI 线程)
  3. 如果您发现算法的某些部分很慢,请对其进行优化(不是 UI 交互,我指的是算法)
  4. 只需将繁重的代码从 UI 中推开
  5. 如果算法仍然很慢,请确保该算法是可并行的,如果是这样,您可以采用以下方法之一:

一个。 Dispatcher.Invoke 方法:只需运行一些刷新 ui 的代码 https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke(v=vs.110).aspx

b.使用像 BlockingCollection 这样的生产者-消费者模式。 推送数据进行处理,在一定数量的线程中处理,然后将结果推送到主线程以显示结果 https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview

c。使用反应式编程 https://msdn.microsoft.com/en-us/library/hh242985(v=vs.103).aspx

d。如果您只想删除 ui 冻结,请直接使用 async/await 或 TPL 从 UI 线程运行繁重的代码。

....还有很多其他方法

另外,在进行多线程之前,最好解开和拆分代码的 ui-get 部分、处理部分和 ui-refresh 部分。

【讨论】:

    猜你喜欢
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    • 2017-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    相关资源
    最近更新 更多