Skip to main content Skip to footer

How to Improve WPF Datagrid Performance with 2D Cell Rendering

New in the ComponentOne 2023 v1 release, FlexGrid for WPF and MAUI supports custom cell creation using the .NET 2D drawing API. By using the graphics drawing API (such as DrawString or DrawLine), you can gain performance improvements of up to 37% for loading and scrolling, compared to using Framework Elements for custom cells.

In this blog, we will explain the different techniques for custom cell creation in the XAML FlexGrid, show an example using the new 2D drawing approach, and share the new performance gains.

Ready to Test it out? Download ComponentOne Today!

FlexGrid Custom Cell Rendering Techniques

The standard approaches to customizing cells in FlexGrid for XAML platforms are either through the GridCellFactory (C#) or the CellTemplate (XAML). The CellTemplate property allows you to define a DataTemplate in XAML to represent a cell’s content. This approach is still useful for some cases when you need to customize the cell’s content with specific UI controls (like a gauge component). But there are common cases, like displaying plain text and shapes, where drawing the cell content with the 2D rendering API will improve performance.

Basically, calling the DrawString method 100 times is more efficient than rendering 100 TextBlocks.

To access the drawing API (System.Drawing), we need to use the GridCellFactory approach to access the C# code. The GridCellFactory allows the customization of cell content by overriding the methods GetCellContentType, CreateCellContent, BindCellContent, and UnbindCellContent. While this approach also allows you to render cells with UI Framework Elements (like a TextBlock), it now also allows you to use rendering APIs.

To explain how to do it, we will implement this tic-tac-toe demo using FlexGrid.

2D

Example Rendering Cells with 2D Rendering API

The first step is creating a custom Cell Factory and enabling custom cells. Note that custom cells are disabled by default to avoid breaking changes in the code of previously developed cell factories.

public class MyCellFactory : GridCellFactory
{
    public MyCellFactory()
    {
        AllowCustomCells = true;
    }
}

Then we override the methods GetCellKind, CreateCell, BindCell, and UnbindCell, to implement the desired cell content. Notice that these methods are similar to those used above to create the cell content; because the cells are reused and recycled, it is necessary to specify what kind of cells we’re customizing so the recycling algorithm works correctly.

For simplicity reasons, we will assume all the cells are of the same kind in the code below:

public override object GetCellKind(GridCellType cellType, GridCellRange range)
{
    return typeof(MyCustomCell);
}

public override GridCellView CreateCell(GridCellType cellType, GridCellRange range, object cellKind)
{
    return new MyCustomCell();
}

public override void BindCell(GridCellType cellType, GridCellRange range, GridCellView cell)
{
    (cell as MyCustomCell).IsCross = isCross;//Sets the value to determine whether the cell shows a cross of a circle
}

public override void UnbindCell(GridCellType cellType, GridCellRange range, GridCellView cell)
{
}

The class MyCustomCell will implement the custom drawing. In WPF, we can override the OnRender method to perform the drawing. Still, we will override OnRenderBackground provided by GridCellView so the drawing of the border is performed by the base class, and we don’t need to deal with it. Within this override, we draw the desired tic-tac-toe symbol using the DrawLine or DrawEllipse methods.

public class MyCustomCell : GridCellView
{
    private bool _isCross;
    public bool IsCross
    {
        get => _isCross;
        set
        {
            _isCross = value;
            InvalidateVisual();
        }
    }

    protected override void OnRenderBackground(DrawingContext drawingContext, Rect backgroundArea)
    {
        base.OnRenderBackground(drawingContext, backgroundArea);
        backgroundArea.Inflate(-8, -8);//Adds some padding
        if (IsCross)
        {
            drawingContext.DrawLine(new Pen(Foreground, 4), backgroundArea.TopLeft, backgroundArea.BottomRight);
            drawingContext.DrawLine(new Pen(Foreground, 4), backgroundArea.BottomLeft, backgroundArea.TopRight);
        }
        else
        {
            drawingContext.DrawEllipse(null, new Pen(Foreground, 4), new Point(backgroundArea.Left + backgroundArea.Width / 2, backgroundArea.Top + backgroundArea.Height / 2), backgroundArea.Width / 2, backgroundArea.Height / 2);
        }
    }
}

Next, let us also show how to define the Cell Factory in XAML. Here we’ve defined a FlexGrid with three rows and three columns to simulate the tic-tac-toe board.

<c1:FlexGrid AutoGenerateColumns="False" DefaultColumnWidth="100" DefaultRowHeight="100" 
   GridLinesVisibility="All" HeadersVisibility="None" SelectionMode="None" IsReadOnly="True">
    <c1:FlexGrid.Rows>
        <c1:GridRow />
        <c1:GridRow />
        <c1:GridRow />
    </c1:FlexGrid.Rows>
    <c1:FlexGrid.Columns>
        <c1:GridColumn />
        <c1:GridColumn />
        <c1:GridColumn />
    </c1:FlexGrid.Columns>
    <c1:FlexGrid.CellFactory>
        <local:MyCustomCellFactory />
    </c1:FlexGrid.CellFactory>
</c1:FlexGrid>

The full sample can be downloaded here.

Observing Performance Improvements

In our tests, we’ve observed up to 37% performance improvements using the DrawLine method compared to alternatives like images or Framework Elements for the cells. Specifically, the reduced loading time and faster scrolling speed will observe performance gains no matter how many rows of data you have.

 

Load-time

Scrolling

WPF

34% faster

37% faster

Android

34% faster

36% faster

iOS

32% faster

36% faster

Download ComponentOne WPF Edition or update to the 2023 v1 version (or later) today to try it yourself!

 

comments powered by Disqus