我正在尝试使用 WPF 绘制图像/图标网格。网格尺寸会有所不同,但通常范围从 10x10 到 200x200。用户应该能够点击单元格,有些单元格需要每秒更新(更改图像)10-20 次。网格应该能够在所有四个方向上增长和收缩,并且应该能够切换到它所代表的 3D 结构的不同“切片”。我的目标是根据这些要求找到一种适当有效的方法来绘制网格。
我当前的实现使用 WPF Grid
.我在运行时生成行和列定义并用 Line
填充网格(对于网格线)和 Border
(对于单元格,因为它们当前只是开/关)在适当的行/列的对象。 (Line
对象一直跨越。)
在扩展网格(按住 Num6)时,我发现它的绘制速度太慢而无法在每次操作时重新绘制,因此我对其进行了修改以简单地添加一个新的 ColumnDefinition
, Line
和一套 Border
每个增长列的对象。这解决了我的增长问题,并且可以使用类似的策略来快速收缩。为了在模拟中更新单个单元格,我可以简单地存储对单元格对象的引用并更改显示的图像。即使更改为新的 Z 级别也可以通过仅更新单元格内容而不是重建整个网格来改进。
然而,在我进行所有这些优化之前,我遇到了另一个问题。每当我将鼠标悬停在网格上时(即使以低速/正常速度),应用程序的 CPU 使用率也会飙升。我从网格的子元素中删除了所有事件处理程序,但这没有任何效果。最后,控制 CPU 使用率的唯一方法是设置 IsHitTestVisible = false
为 Grid
. (为 Grid
的每个子元素设置此项没有任何作用!)
我相信使用单个控件来构建我的网格过于密集且不适合此应用程序,并且使用 WPF 的 2D 绘图机制可能更有效。不过,我是 WPF 的初学者,所以我正在寻求有关如何最好地实现这一目标的建议。从我读过的一点点来看,我可能会使用 DrawingGroup
将每个单元格的图像合成到单个图像上进行显示。然后我可以对整个图像使用单击事件处理程序,并通过鼠标位置计算单击单元格的坐标。不过,这看起来很困惑,我只是不知道是否有更好的方法。
想法?
更新 1:
我听取了 friend 的建议,转而使用 Canvas
与 Rectangle
对于每个单元格。当我第一次绘制网格时,我存储对所有 Rectangle
的引用。在二维数组中,然后当我更新网格内容时,我只需访问这些引用。
private void UpdateGrid()
{
for (int x = simGrid.Bounds.Lower.X; x <= simGrid.Bounds.Upper.X; x++)
{
for (int y = simGrid.Bounds.Lower.Y; y <= simGrid.Bounds.Upper.Y; y++)
{
CellRectangles[x, y].Fill = simGrid[x, y, ZLevel] ? Brushes.Yellow : Brushes.White;
}
}
}
绘制网格最初看起来更快,后续更新肯定更快,但仍然存在一些问题。
我还没试过
UniformGrid
方法,因为我认为它可能会出现我已经遇到的相同问题。不过,一旦我用尽了更多选择,我可能会尝试一下。
最佳答案
你的问题
让我们重新表述你的问题。这些是您的问题限制:
从这些限制条件来看,您可以立即看出您使用了错误的方法。
需求:快速刷新确定性位置,交互性很小
快速刷新帧率+每帧许多变化+大量单元格+每个单元格一个WPF对象=灾难。
除非您拥有非常快的图形硬件和非常快的 CPU,否则您的帧速率总是会随着网格尺寸的增加而受到影响。
您的问题更像是视频游戏或具有动态缩放功能的 CAD 绘图程序。它不像一个普通的桌面应用程序。
立即模式与保留模式绘图
换句话说,您需要“立即模式”绘图,而不是“保留模式”绘图(WPF 是保留模式)。这是因为您的约束不需要通过将每个单元格视为单独的 WPF 对象来提供的许多功能。
例如,您不需要布局支持,因为每个单元格的位置都是确定性的。您将不需要 HitTest 支持,因为同样,位置是确定性的。您不需要容器支持,因为每个单元格都是一个简单的矩形(或图像)。您不需要复杂的格式支持(例如透明度、圆角边框等),因为没有任何重叠。换句话说,每个单元格使用网格(或 UniformGrid)和一个 WPF 对象没有任何好处。
立即模式绘制缓冲位图的概念
为了达到您需要的帧速率,基本上您将绘制一个大位图(覆盖整个屏幕)——或“屏幕缓冲区”。对于您的单元格,只需绘制到此位图/缓冲区(可能使用 GDI)。 HitTest 很容易,因为单元格位置都是确定性的。
这种方法会很快,因为只有一个对象(屏幕缓冲区位图)。您可以为每一帧刷新整个位图,或者只更新那些发生变化的屏幕位置,或者这些的智能组合。
请注意,尽管您在此处绘制了“网格”,但并未使用“网格”元素。根据您的问题约束是什么来选择您的算法和数据结构,而不是看起来是显而易见的解决方案——换句话说,“网格”可能不是绘制“网格”的正确解决方案。
WPF中的立即模式绘图
WPF 基于 DirectX,因此本质上它已经在幕后使用屏幕缓冲区位图(称为后台缓冲区)。
在 WFP 中使用即时模式绘图的方法是将单元格创建为 GeometryDrawing(而不是 Shape,这是保留模式)。 GemoetryDrawing 通常非常快,因为 GemoetryDrawing 对象直接映射到 DirectX 基元;它们不是作为框架元素单独布置和跟踪的,因此它们非常轻量级——您可以拥有大量它们而不会对性能产生不利影响。
将 GeometryDrawing 选择到 DrawingImage(这实际上是您的后台缓冲区)中,您将获得一个快速变化的屏幕图像。在幕后,WPF 完全符合您的预期——即将每个矩形绘制到图像缓冲区上。
同样,不要使用形状——这些是框架元素和 将 当他们参与布局时会产生大量的开销。例如,请勿使用矩形 ,但使用 矩形几何反而。
优化
您可能会考虑更多优化:
编辑:逐帧渲染
覆盖
OnRender
正如答案中所建议的那样,为这个问题授予了赏金。然后你基本上在 Canvas 上绘制整个场景。使用 DirectX 进行绝对控制
或者,如果您想对每一帧进行绝对控制,请考虑使用原始 DirectX。
关于c# - 使用 WPF 绘制图像网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5668181/