如何在 WPF 中绘制 1216 canvas 个元素而不挂起应用程序

How to draw 1216 canvas elements without hanging application in WPF

我正在开发一个应用程序,我想添加一些很酷的图标。因为我正在使用漂亮的 MahApps 库,所以我想在 MahApps.Metro/MahApps.Metro.Resources/Icons.xaml 中的图标上有一个视觉效果,所以我做了一些字符串操作来获取x:Key<Canvas x:Key="appbar_3d_3ds" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0"> 行的一部分。简而言之,我所做的所有字符串操作最终得到以下 1216 个副本:

<controls:Tile
Title="appbar_zune" Count="1215" Grid.Row="121" Grid.Column="15" TiltFactor="2" Width="1*" Height="1*"  VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Rectangle Margin="0" Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill"
Visual="{StaticResource appbar_zune}" />
</Rectangle.OpacityMask>
</Rectangle>
</controls:Tile>

请注意,<control:Tile 的每个副本都正确设置了适当的属性 CountGrid.RowGrid.Column

但是,我总是以 Application not responding window 消息结束。现在正如我所说,我的动机只是为了直观地看到 collection 漂亮的图标,而不是让应用程序崩溃。我只是想知道是否有一种方法可以显示如此巨大的 collection 而不会导致任何人的计算机崩溃(注意:系统的 RAM 非常低:我的一台测试机器 运行 inside virtualbox)。

首先,使您的 Fill 绑定具有 Mode=OneWay。我打赌你不需要它是双向的,它可能是你设置中的默认值。 TwoWay 绑定成本更高。

其次,考虑使用更苛刻的版本:Mode=OneTime。由于图标不太可能更改,因此您根本不需要任何更改跟踪。这将为您节省更多资源。

对于您的情况,First+Second 可能不会给您带来巨大的提升,但值得尝试并记住。

第三,你的VisualBrush怎么样?他们都使用相同的 Visual="{StaticResource appbar_zune}" 吗?那你为什么要创建数千个实例?不要复制粘贴,只创建一个实例并使所有项目都使用该实例。您可能会节省很多时间和内存。

第四,也是最重要的,通常会提供最大的加速,是 - 你有 的项目。我打赌你有一些滚动,水平或垂直。但是你如何生成和显示这些项目呢?一次创建它们是..浪费的。它们并不适合所有屏幕,对吧?

你有生成那一千个项目的 ItemsControl 吗?调查该 ItemsControl 的 ItemsPanel 属性 并打开该面板上的 virtualizing 选项。这将导致它 link 到滚动条,并且它将开始动态地只创建那些在屏幕上的项目并销毁移出屏幕的项目。好吧,我把它简单化了,但假设它是这样工作的。请注意,像 ListBox(以及许多其他容器)这样的容器也是一个 ItemsControl,因此它也适用于此处。

或者您可能有巨大的显式 XAML 文件,在某些 StackPanel 中包含上千个控件,但没有 ItemsControl?那真的不明智。但是哦,好吧..你仍然可以在那个 StackPanel 上打开 virtualization

如果您的项目超过几十个,打开虚拟化通常是个好主意。往往有一百个是必须的,达到几千以上也是必须的。但是,虚拟化成本:它经常 resets/reinitializes 项。如果你的ItemTemplate真的很复杂,虚拟化可能会导致滚动变成"jaggy/laggy",我不知道怎么用英文表达,sorry。合成器线程可能根本没有足够的时间来重新计算和重新布局所有快速移动的项目。如果遇到该问题,请尝试将 Items 的 Height 设置为不变的真正固定常量值。它有助于加快布局速度。但是如果您的 ItemTemplate 真的非常复杂,它也可能无济于事。在这种死胡同的情况下,您唯一的选择是...重新设计并简化项目模板。

编辑:

当然,如果您没有滚动条并且您试图一次显示大量项目,那么所有这些都不会给您带来任何好处。在这种情况下,努力简化或去除Bindings、Templates、组件嵌套(有时手动计算位置比使用三个嵌入的Grid更好)、use rendering cache或(...)..对不起,我开始做太多了猜测,选项太多..

编辑:

我刚刚注意到 Width="1*"Stretch,所以您可能在顶部有一个 Grid,而不是 StackPanel。由于您希望它们大小相同,因此 UniformGrid 可能具有更好的性能。此外,通过一些工作,您也可以将虚拟化添加到网格中:

哦,关于虚拟化的最后一句话:请记住,当 ScrollView 在虚拟化模式下工作时,Position 不再计入 pixels/points。在v-mode下,scrollbar的Position是在items中统计的,即position=2.5表示scroller在第三个item的中途(2个item通过了一半),而不是在pos= 2.5 "pixels".


旁注:"million point canvas":https://blogs.msdn.microsoft.com/kaelr/2010/08/11/zoomableapplication2-a-million-items/