更新元素时​​的 WPF 性能

WPF performance when updating elements

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

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

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

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

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

现在的问题是:我的做法是不是错了?如果是这样,我应该使用什么?如果没有 - 我怎样才能提高性能?

如果相关,我会用我的模型实施的一些细节更新此 post。

编辑:应该可以通过与元素交互来改变单次旋转。这可以通过在实际图形之上的透明层来完成,但也许并不难。

GUI,任何类型的 GUI 技术,无论是 WPF 还是 Windows Forms 或其他任何技术,都不能处理繁重的图形。开发简单的图形很容易。

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

或者,您可以尝试通过仅绑定到一张图片并在单元格更改时自己绘制该图片来尝试自己模拟它。也许这就足够了,你将不得不测试它。

您可以编写一个自定义元素(派生自 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
    }
}

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

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

This - seems to be a Bitmap, so you should threat it as a Bitmap. Instead of ItemsControl use Image and instead of ItemsSource use WritableBitmap 作为图片来源。

对于您的原始代码,它可能存在几个性能瓶颈:

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

使用 canvas 并且没有绑定,我能够在 500 毫秒内渲染网格。但是,使用 WritableBitmap 或其他 "pixel based" 方法可以显示更大的网格。

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

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