【问题标题】:WPF performance when updating elements更新元素时​​的 WPF 性能
【发布时间】:2015-02-06 11:24:40
【问题描述】:

我目前正在为Ising model(德国维基百科,因为只有右边的图片真正重要)开发一个 GUI,它应该由大约 200x200 的旋转元素组成。我通过以下方式实现了这一点:

<UniformGrid Name="grid" .... />

并为代码中的每个旋转添加一个矩形,如果旋转的值发生变化,我会在该矩形后面进行更新。这不知何故非常慢,我改变了它,所以它使用绑定

 <ItemsControl Name="IsingLattice" ItemsSource="{Binding Spins}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Name="grid" ...
            ...
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Rectangle Fill={Binding Color} ...

但这又是 - 非常慢。我尝试调试和改进了 3 天,但到目前为止没有成功。

现在的问题是:我的方法错了吗?如果是这样,我应该改用什么?如果没有,我该如何提高性能?

如果相关,我会更新这篇文章,提供我的模型实现的一些细节。

编辑:应该可以通过与元素交互来改变单次旋转。这可以通过在实际图形顶部的透明层来完成,但无论如何可能并不难。

【问题讨论】:

  • ps:10x10 旋转的性能很好 - 使用 30x30 元素会变慢。
  • 200x200,也就是40000,可以全部显示在屏幕上吧?
  • @oerpli:获取一些分析数据可能会很有趣。看看这个msdn.microsoft.com/en-us/library/aa969767(v=vs.110).aspx 和这个codeproject.com/Articles/784529/…
  • @kennyzx 是的,它是一个 600 像素的正方形 - 所以对于 200x200,每个旋转都是 9 像素宽(假设 WPF 将一个像素映射到一个它不知道的像素)。
  • @bic 如果它告诉我任何有趣的事情,我会运行分析器。

标签: c# .net wpf performance xaml


【解决方案1】:

您可以编写一个自定义元素(派生自 FrameworkElement),在内部存储自旋数据,然后通过覆盖 OnRender 方法一次性渲染数据:

public sealed class IsingModel : FrameworkElement
{
    readonly bool[] _spinData = new bool[200 * 200];

    protected override void OnRender(DrawingContext dc)
    {
        // use methods of DrawingContext to draw appropriate
        // filled squares based on stored spin data
    }

    protected override void OnMouseDown(MouseButtonEventArgs e)
    {
        base.OnMouseDown(e);

        // work out which "cell" was clicked on and change
        // appropriate spin state value in Boolean array

        InvalidateVisual(); // force OnRender() call
    }
}

这种方法应该比拥有数千个单独元素更快。我不知道要快多少。

【讨论】:

  • 感谢您的回答 - 我今天实现了它并玩了一点 - 在 90x90 格之前性能很好,在 120x120 上仍然可以 - 已经有了很大的改进。
  • 注意:冻结不会更改的资产很重要,例如ColorBrushPen 对象。
  • InvalidateVisual() 除非控件的大小发生变化,否则不应使用。如果您只想更新依赖属性上的视觉使用AffectsRender,或者在OnRender() 期间将DrawingGroup 放在视觉树中,然后稍后使用DrawingGroup.Open() 进行更新。
【解决方案2】:

ItemsControl 旨在基于数据源重复 UI 控件。 UI 控件必须是可定制的、布局更改时的响应式和交互性。这都不是你的情况。 你实际上只想渲染一张图片。

This - 似乎是一个位图,所以你应该把它当作一个位图来威胁。代替 ItemsControl 使用 Image 代替 ItemsSource 使用 WritableBitmap 作为图像来源。

当涉及到您的原始代码时,它可能存在几个性能瓶颈:

  1. 生成用作 ItemsSource 的类可能需要一些时间
  2. UniformGrid 需要测量大小并计算每个元素的位置。这可能需要一段时间。使用 Canvas,您可以获得更好的结果
  3. ItemsControl 可能需要一些时间才能从 DataTemplate 生成 40 000 个项目。您可以手动创建这些矩形并将其添加到画布后面的代码中
  4. 绑定有性能成本。如果您的每个项目都是数据绑定的,那么您的绑定需要评估 40 000 次。您可以在后面的代码中手动设置属性,而不是绑定。

使用画布并且没有绑定,我能够在 500 毫秒内渲染网格。但是,使用 WritableBitmap 或其他“基于像素”的方法,您可以显示更大的网格。

【讨论】:

  • 我提到了其他“基于像素”的方法,例如,这可能是@Steven Rands 的答案。
  • 我现在用可写位图实现了它,而且速度非常快(即使我不得不使用不安全的代码进行更新)。现在对于 600x600 网格来说已经足够快了。
  • 我很高兴它对你有用。如果需要,您可以通过将每个旋转映射到可写位图中的单个像素来进一步提高性能。然后,您可以根据需要设置拉伸图像。例如 [Image Height="600" Width="600" Strech="Uniform"] 拉伸在图形卡上完成,速度更快。你可以有响应式布局。
【解决方案3】:

图形用户界面,任何类型的图形用户界面技术,无论是 WPF 或 Windows 窗体或其他任何东西,都不适用于处理繁重的图形。它旨在轻松开发简单的图形。

如果您需要图形功能(例如动态更新 40.000 个单元格),那么您需要一个图形框架。最有可能的是,游戏框架会做,选择您的选择之一。

或者,您可以尝试自己模拟它,仅绑定到单个图片并在单元格更改时自己绘制该图片。也许这就够了,你必须测试一下。

【讨论】:

  • 这是“繁重的图形”吗:i.imgur.com/CYb08qT.png 我还希望能够在单击时更改单次旋转 - 这对于当前的实现非常容易(我只是更改发送者的 DataContext) -您对哪个图形框架允许这样做有什么建议吗?
  • @oerpli 不,那是简单的图形。当您想根据时间对其进行更新时,它会变成沉重的图形。我没有 C# 框架的经验,SFML 非常好,我听说它有一个 C# 包装器。但正如我所说,也许先把它画到一张照片上已经足够了。
【解决方案4】:

您是否考虑过使用BitmapCache 来提高渲染速度?

我的理解是,当绘制复杂控件或同时在屏幕上有多个控件实例时,这可以显着提高渲染速度。您可能希望在网格级别启用缓存,而不是在每个单独的微调器上。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-17
    • 2013-11-30
    • 2011-01-04
    • 1970-01-01
    相关资源
    最近更新 更多