Hen*_*iam 7 c# wpf grid drawing 2d
我正在尝试使用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的初学者,所以我正在寻求如何最好地实现这一目标的建议.从我读过的内容来看,我可能会使用a DrawingGroup将每个单元格的图像组合成一个图像进行显示.然后,我可以为整个图像使用单击事件处理程序,并通过鼠标位置计算单击的单元格的坐标.但这似乎很混乱,我只是不知道是否有更好的方法.
思考?
更新1:
我接受了朋友的建议,并转而使用Canvasa 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
最初绘制网格似乎更快,后续更新肯定更快,但仍然存在一些问题.
无论我鼠标移动的区域有多小,每当我将鼠标悬停在网格上时,CPU使用率仍然会超过几百个单元格.
更新仍然太慢,所以当我按住向上箭头键来更改Z级别(一个常见的用例)时,程序一次冻结几秒钟,然后看起来一次跳跃50个Z级别.
一旦网格保持~5000个单元格,更新大约需要一秒钟.这非常慢,5000个单元适合典型的使用案例.
我还没有尝试过这种UniformGrid方法,因为我认为它可能会遇到我遇到过的同样问题.不过,一旦我用尽了一些选项,我可能会尝试一下.
Ste*_*ung 17
让我们改一下你的问题吧.这些是你的问题限制:
从这些限制来看,您可以立即看到您使用了错误的方法.
快速刷新帧速率+每帧多次更改+大量单元格+每个单元格一个WPF对象=失败.
除非你有非常快的图形硬件和非常快的CPU,否则你的帧速率总是会随着网格尺寸的增加而受到影响.
你的问题所决定的更像是一个动态缩放的视频游戏或CAD绘图程序.它不像普通的桌面应用程序.
换句话说,您需要"立即模式"绘图,而不是"保留模式"绘图(WPF是保留模式).这是因为您的约束不需要将每个单元格视为单独的WPF对象而提供的大部分功能.
例如,您不需要布局支持,因为每个单元格的位置都是确定的.您不需要命中测试支持,因为位置也是确定性的.您不需要容器支持,因为每个单元格都是一个简单的矩形(或图像).您不需要复杂的格式支持(例如透明度,圆角等),因为没有重叠.换句话说,每个单元格使用Grid(或UniformGrid)和一个WPF对象没有任何好处.
为了实现您所需的帧速率,基本上您将绘制到一个大位图(覆盖整个屏幕) - 或"屏幕缓冲区".对于您的单元格,只需绘制到此位图/缓冲区(可能使用GDI).命中测试很容易,因为细胞位置都是确定性的.
这种方法很快,因为只有一个对象(屏幕缓冲区位图).您可以刷新每个帧的整个位图,也可以仅更新那些更改的屏幕位置,或者更新这些位置的智能组合.
请注意,虽然您在此处绘制"网格",但不使用"网格"元素.根据您的问题约束选择您的算法和数据结构,而不是看起来是明显的解决方案 - 换句话说,"网格"可能不是绘制"网格"的正确解决方案.
WPF基于DirectX,因此基本上它已经在场景后面使用了屏幕缓冲区位图(称为后台缓冲区).
在WFP中使用立即模式绘制的方法是将单元格创建为GeometryDrawing(而不是Shape的,这是保留模式).GemoetryDrawing通常非常快,因为GemoetryDrawing对象直接映射到DirectX基元; 它们没有作为框架元素单独布局和跟踪,因此它们非常轻量级 - 您可以拥有大量它们而不会对性能产生负面影响.
选择GeometryDrawing进入DrawingImage(这实际上是你的后缓冲区),你会得到一个快速变化的图像.在场景后面,WPF完全按照您的预期进行操作 - 即绘制每个更改到图像缓冲区的矩形.
同样,不要使用Shape - 这些是框架元素,并且在参与布局时会产生很大的开销.例如,不要使用Rectangle,而是使用RectangleGeometry.
您可以考虑更多优化:
OnRender根据答案中的建议覆盖,为此问题授予赏金.然后你基本上在画布上绘制整个场景.
或者,如果您想要对每个帧进行绝对控制,请考虑使用原始DirectX.
| 归档时间: |
|
| 查看次数: |
6688 次 |
| 最近记录: |