绘制的图像与WPF网格网格、图像、WPF

2023-09-08 09:19:42 作者:薄衫少年、

我想画的图像/图标,WPF中的网格。网格尺寸将有所不同,但通常从10×10到200×200的范围。用户应该能够点击细胞,和一些细胞将需要更新(变化图像)每秒10-20次。网格应能增大和缩小在所有四个方向,并且它应该是能够切换​​到3D结构它重新presents的一个不同的切片。我的目标是要找到一个合适有效的方法绘制给出这些要求的网格。

I'm trying to draw a grid of images/icons with WPF. The grid dimensions will vary but will typically range from 10x10 to 200x200. The user should be able to click on cells, and some cells will need to update (change image) 10-20 times per second. The grid should be able to grow and shrink in all four directions, and it should be able to switch to a different "slice" of the 3D structure it represents. My goal is to find a suitably efficient method for drawing the grid given those requirements.

我目前的实现使用WPF 电网。我生成行和列定义在运行时填充网格与(用于网格线)和边框(对细胞,因为它们是目前仅有的开/关)对象在合适的行/列。 (该对象横跨所有的方式。)

My current implementation uses a WPF Grid. I generate row and column definitions at runtime and populate the grid with Line (for the gridlines) and Border (for the cells, since they're currently just on/off) objects at the appropriate row/column. (The Line objects span all the way across.)

在扩大电网(按住NUM6),我发现它吸引太慢重绘对每一个操作,所以我修改了它简单地添加新的 Col​​umnDefinition ,并设置边框成长的每一列的对象。这解决了我的成长问题,类似的策略可以用于制造快速萎缩也是如此。用于更新单个细胞中期仿真,我可以简单地存储的引用到细胞对象并改变显示的图像。即使更改为新的Z级可以通过只更新单元格的内容,而不是重建整个电网得到改善。

While expanding the grid (holding down Num6) I found that it draws too slowly to redraw on every operation, so I modified it to simply add a new ColumnDefinition, Line and set of Border objects for each column of growth. That solved my growth issue, and a similar tactic could be used to make shrinking fast as well. For updating individual cells mid-simulation, I could simply store references to the cell objects and change the displayed image. Even changing to a new Z-level could be improved by only updating cell contents instead of rebuilding the entire grid.

不过,我还没来得及让所有的优化,我遇到了另一个问题。每当我鼠标的网格(即使在慢/正常速度)的应用程序的CPU使用高峰。予除去从网格的子元素的所有事件处理程序,但没有效果。最后,保持CPU使用率在检查的唯一方法就是设置 IsHitTestVisible = FALSE 电网。 (为电网的每个子元素设定,这并没有什么!)

However, before I could make all of those optimizations, I ran into another problem. Whenever I mouse over the grid (even at slow/normal speeds) the application's CPU usage spikes. I removed all event handlers from the grid's child elements, but that had no effect. Finally, the only way to keep CPU usage in check was to set IsHitTestVisible = false for the Grid. (Setting this for every child element of the Grid did nothing!)

我相信,使用单独的控制,以建立自己的网格过于密集,不适合此应用程序,而且使用WPF的2D绘图机制可能更有效。我是个初学者WPF,虽然如此,我正在寻找关于如何最好地实现这个建议。从一点点我读过,我可能会使用 DrawingGroup 来共同构成每个细胞的图像到一个单一的图像进行显示。那么我可以用一个单击事件处理程序的整个图像,并通过鼠标的位置计算点击单元格的坐标。这似乎凌乱,不过,我只是不知道是否有更好的方法。

I believe that using individual controls to build my grid is too intensive and inappropriate for this application, and that using WPF's 2D drawing mechanisms might be more efficient. I'm a beginner to WPF, though, so I'm seeking advice on how to best achieve this. From what little I've read, I might use a DrawingGroup to compose each cell's image together onto a single image for display. I could then use a click event handler for the entire image and compute the coordinates of the clicked cell by the mouse location. That seems messy, though, and I just don't know if there's a better way.

思考?

更新1:

我参加了一个朋友的建议,并切换到使用画布矩形的每个单元格。当我第一次画格,我存储引用的所有矩形在一个二维数组,然后当我更新了网格内容,我只是访问这些引用。

I took a friend's advice and switched to using a Canvas with a Rectangle for each cell. When I first draw the grid, I store references to all the Rectangle in a two-dimensional array, and then when I update the grid contents, I simply access those references.

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;
        }
    }
}

绘制网格最初似乎速度更快,并且后续更新肯定快,但仍然存在一些问题。

Drawing the grid initially seems faster, and subsequent updates are definitely faster, but there are still a few problems.

无论多么小的面积我鼠标是,CPU使用率仍然钉鞋,每当我鼠标移到网格时它已经超过几百个细胞。

No matter how small the area I mouse over is, CPU usage still spikes whenever I mouse over the grid when it has more than a few hundred cells.

更​​新还是太慢,所以当我按住向上箭头键来改变Z-级(一个常见的​​情况),该程序在同一时间冻结了秒,然后出现跳跃50 Z-水平在一次。

Updates are still too slow, so when I hold down the up arrow key to change the Z-level (a common use case) the program freezes for seconds at a time and then appears to jump 50 Z-levels at once.

在网格持有〜5000个细胞,更新采取一秒的量级。这非常慢,和5000个细胞典型应用案例的夹缝之中。

Once the grid holds ~5000 cells, updates take on the order of one second. This is prohibitively slow, and 5000 cells fits within typical use cases.

我还没有尝试过的 UniformGrid 的办法,因为我觉得它可能会出现我已经遇到了同样的问题。我可能会尝试一下,一旦我已经用尽了一些更多的选择,虽然。

I haven't yet tried the UniformGrid approach because I think it may exhibit the same problems I've already encountered. I might give it a try once I've exhausted a few more options, though.

推荐答案

让我们重新表述您的问题。这是你的问题的制约:

Your Question

Let's rephrase your question. These are your problem constraints:

您想绘制动态大小的网格 在每个单元格更改/关迅速 在电网规模迅速改变 有大量的细胞(即网格尺寸不是微不足道) 您希望所有这些变化会出现一个快速的帧速率(例如每秒30帧) 网格的定位和布局,并细胞是确定的,简单的,不是很交互式

从这些制约因素来看,你可以立即看到你正在使用错误的做法。

Judging from these constraints, you can immediately see that you're using the wrong approach.

快速刷新的帧速率+每帧+大量细胞的许多变化+每单元一个WPF对象= dissaster。

Fast refresh frame rate + many changes per frame + large number of cells + one WPF object per cell = dissaster.

除非你有非常快的图形硬件和非常快的CPU,你的帧速率总是会患上增加网格尺寸。

Unless you have very fast graphics hardware and a very fast CPU, your frame rate is always going to suffer with increases in grid dimensions.

你的问题的决定更像是一个视频游戏或具有动态缩放CAD绘图程序。这是lesss像一个正常的桌面应用程序。

What your problem dictates is more like a video game or a CAD drawing program with dynamic zooming. It is lesss like a normal desktop application.

换句话说,你要快速模式图,而不是保留模式引(WPF保留模式)。那是因为你的约束并不需要太多的每个单元视为一个单独的WPF对象提供的功能的。

In other words, you want "immediate mode" drawing, not "retained mode" drawing (WPF is retained mode). That is because your constraints do not require much of the functionalities provided by treating each cell as a separate WPF object.

例如,您将不再需要布局的支持,因为每个单元的位置是确定的。你不会需要命中测试支持,因为,再一次,位置是确定的。您不需要容器支持,因为每个单元是一个简单的矩形(或图像)。您不需要复杂格式的支持(例如透明,圆润的边框等),因为没有任何重叠。换句话说,没有好处使用网格(或UniformGrid),而每个单元1的WPF对象

For example, you won't need layout support because each cell's position is deterministic. You won't need hit-testing support because, again, positions are deterministic. You won't need container support, because each cell is a simple rectangle (or an image). You won't need complex formatting support (e.g. transparency, rounded borders etc.) because nothing overlap. In other words, there is no benefit to use a Grid (or UniformGrid) and one WPF object per cell.

为了达到你所需要的帧速率,从根本上你会画上一个大的位图(覆盖整个屏幕) - 或屏幕缓冲区。对于你的细胞,简单地画到这个位图/缓存(可能使用GDI)。点击测试十分简单,因为小区位置都是确定的。

In order to achieve the frame rate you required, essentially you'll be drawing to a large bitmap (which covers the whole screen) -- or a "screen buffer". For your cells, simply draw to this bitmap/buffer (perhaps using GDI). Hit testing is easy as the cell positions are all deterministic.

此方法将是快,因为仅存在一个对象(屏幕缓冲区位图)。你可以刷新整个位图对每一帧,或仅更新那些屏幕位置即发生变化,或这些智能组合

This method will be fast because there is only one object (the screen buffer bitmap). You can either refresh the entire bitmap for each frame, or update only those screen positions that change, or an intelligent combination of these.

注意,尽管你是画一个网格在这里,你不使用网格的元素。选择你的算法和基于你的问题,约束你的数据结构,而不是它的看起来的喜欢是显而易见的解决方案 - 换句话说,一个网格可能不是绘制正确的解决方案一个网格。

Notice that although you are drawing a "grid" here, you don't use a "Grid" element. Choose your algorithm and your data structures based on what your problem constraints are, not what it looks like to be the obvious solution -- in other words, a "Grid" may not be the right solution for drawing a "grid".

WPF基于DirectX,因此本质上,它已在使用屏幕缓冲区位图(被称为背缓冲区)在幕后。

WPF is based on DirectX, so essentially it is already using a screen buffer bitmap (called the back-buffer) behind the scene.

您使用即时模式绘制世界粮食计划署的方法是创建细胞GeometryDrawing的(没有形状的,这是保留模式)。 GemoetryDrawing通常是非常快的,因为GemoetryDrawing对象直接映射到DirectX元;他们没有铺好并单独跟踪的框架元素,所以他们是非常轻的重量 - 你可以有大量的人而不会影响性能。

The way you to use immediate mode drawing in WFP is to create the cells as GeometryDrawing's (not Shape's, which is retained mode). GemoetryDrawing is usually extremely fast because GemoetryDrawing objects map directly to DirectX primitives; they are not laid out and tracked individually as Framework Elements, so they are very light-weight -- you can have a large number of them without adversely affecting performance.

选择的部份GeometryDrawing的成DrawingImage(这基本上是你的背部缓冲区),你会得到一个快速变化的图像屏幕。在幕后,WPF不正是你希望它做的事情 - 即得出每一个改变到图像缓冲区的矩形

Select ths GeometryDrawing's into a DrawingImage (this is essentially your back-buffer) and you get a fast-changing image for your screen. Behind the scene, WPF does exactly what you expect it to do -- i.e. draw each rectangle that changes onto the image buffer.

此外,不要使用形状的 - 这些都是框架元素和会招致显著的开销,因为他们参与布局。例如,不要使用矩形,但使用 RectangleGeometry 代替。

Again, do not use Shape's -- these are Framework Elements and will incur significant overheads as they participate in layout. For instance, DO NOT USE Rectangle, but use RectangleGeometry instead.

若干更多的优化,你可以考虑:

Several more optimizations you may consider:

在重用GeometryDrawing对象 - 只是改变位置和尺寸 如果网格具有的最大尺寸,pre-创建对象 只修改那些改变对象GeometryDrawing - 所以WPF将不必刷新它们 填充的位图中的阶段 - 即,对于不同的缩放等级,始终更新到一个网格是比previous 1大得多,并使用缩放来缩放回来。例如,从10×10网格移动直接向一个20×20网格,但它缩减55%,显示11×11个方块。这样一来,从11×11一路放大到20×20的时候,你的GeometryDrawing对象永远不会改变;仅在该位图缩放被改变,使得它非常快速更新。

覆盖的OnRender 作为答案建议授予了赏金这一问题。然后,你基本上是绘制整个场景在画布上。

Do Frame by Frame Rendering

Flash中如何使用网格绘制图形

Override OnRender as suggested in the answer awarded the bounty for this question. Then you essentially draw the entire scene on a canvas.

另外,如果你想绝对控制权的每一帧考虑使用原始的DirectX。

Alternatively, consider using raw DirectX if you want absolute control over each frame.