Drawing a grid of images with WPF

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.

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.)

Current grid implementation

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.

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!)

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.

Thoughts?

Update 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.

  • 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.

  • 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.

  • 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.

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.

7
задан Henry Merriam 21 April 2011 в 17:45
поделиться