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

本文关键字:元素 应用程序 挂起 布元素 WPF 绘制 1216 | 更新日期: 2023-09-27 18:36:56

我正在开发一个应用程序,我想添加一些很酷的图标。因为我使用的是漂亮的MahApps库,所以我想在MahApps.Metro/MahApps.Metro.Resources/Icons.xaml中的图标上有一个视觉效果,所以我做了一些字符串操作来抓取每行<Canvas x:Key="appbar_3d_3ds" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">x:Key部分。简而言之,我所做的所有字符串操作最终都得到了以下 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窗口消息结束。现在正如我所说,我的动机只是为了看到那组漂亮的图标,相反,我得到了一个胖的应用程序崩溃。我只想知道是否有办法在不使任何人的计算机崩溃的情况下显示如此庞大的集合(注意:系统的 RAM 真的很低:我在 virtualbox 中运行的测试机器之一)。

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

首先,使填充绑定具有 Mode=OneWay。我敢打赌你不需要它是双向的,它可能是你设置中的defalt。双向绑定的成本要高得多。

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

就您而言,First+Second 可能不会给您带来巨大的提升,但值得尝试并记住。

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

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

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

或者,也许您有一个巨大的显式 XAML 文件,其中包含某个没有 ItemsControl 的StackPanel中的一千个控件?这其实不明智。但是哦,好吧..您仍然可以在该堆栈面板上打开virtualization

如果您有超过几十个项目,则打开虚拟化通常是一个好主意。你通常必须有一百个,如果你达到数千个甚至更多,这是必须的。但是,虚拟化成本:它经常重置/重新初始化项目。如果您的 ItemTemplate 真的很复杂,虚拟化可能会导致滚动变得"锯齿状/滞后",我不知道如何用英语表达,抱歉。合成器线程可能根本没有足够的时间来重新计算和重新布局所有快速移动的项目。如果遇到该问题,请尝试将 Items 的Height设置为不变的真正固定常量值。它极大地有助于加快布局速度。但是,如果您的 ItemTemplate 非常复杂,它也可能无济于事。在这种死胡同的情况下,您唯一的选择是...重新设计并简化项模板。

编辑:

当然,如果您没有滚动条并且尝试一次显示大量项目,所有这些都不会为您带来任何好处。在这种情况下,努力简化或删除绑定、模板、组件嵌套(有时手动计算位置比使用三个嵌入式网格更好)、使用渲染缓存或 (...)。对不起,我开始做太多的猜测,太多的选择。

编辑:

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

  • 从 4.5 及更高版本开始,它要容易得多 - 文章:WPF 4.5 新的虚拟化功能
  • 下面,它需要更多的工作,请参阅Dan Crevier的4部分系列博客:
    • 一:http://blogs.msdn.com/dancre/archive/2006/02/06/implementing-a-virtualized-panel-in-wpf-avalon.aspx
    • 二:http://blogs.msdn.com/dancre/archive/2006/02/13/531550.aspx
    • 三:http://blogs.msdn.com/dancre/archive/2006/02/14/532333.aspx
    • 四:http://blogs.msdn.com/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part-4-the-goods.aspx如果虚拟化网格还不够,请尝试移动到画布并手动强制一些宽度/高度/位置。删除自动布局有时会节省很多。然后,您可以使用VirtualizedCanvas,如果您真的在那里放置了恒定大小的项目,您可能会尽快获得它。但这是最后的选择。前面提到的事情应该运作良好。

哦,关于虚拟化的最后一句话:请记住,当 ScrollView 在虚拟化模式下工作时,Position不再以像素/点为单位计算。在 v 模式下,滚动条的位置以 items 为单位计数,即 position=2.5 表示滚动条处于第三项的一半(通过 2 个项目和半项目),而不是在 pos=2.5 "像素"。


旁注:"百万点画布":https://blogs.msdn.microsoft.com/kaelr/2010/08/11/zoomableapplication2-a-million-items/