public class Blazor Data Grid

{

In my day job, I am required to create and maintain applications with many data grids, such as search results. Since this company sells windows applications, the WPF DataGrid is our go-to tool to display these results in a rich, interactive context.

 

In my spare time, I enjoy continuing my work with Asp.NET Core web applications, and now Blazor as the new component-based framework for C# web development. However, Blazor is still relatively new, and I didn’t find an example of a DataGrid like in WPF, except from professional component-making companies like SyncFusion and Telerik. (NOTE: Since starting this, I have found Blazorise, and I’ll have to explore their data grid to see if I want to use/contribute to that rather than my own).

 

So I decided it would be a fun side project to design my own data grid in Blazor.

 

I’m still learning the best way to build and share on GitHub, but I fired up a new repository, TimPurdum/blazor-apps (github.com), and created a solution with two projects: a demo Blazor server-side application, and a razor component library to hold all the actual controls (so I can later package this into a nuget package).

 

After having a project structure, I needed to decide on the component structure. DataGrids are tricky because they aren’t simply a single hierarchy. Rows in a data grid are representations of items in a list. Each cell in the row maps to a property on the item. So far, so good. However, Columns in data grids can have their own values, such as size, data type, formatting, etc. So when you are generating a cell in a data grid, you really have to pay attention to both the column and the row. You also need to create a way to let the developer specify column attributes. And then, if they don’t specify, you should auto-generate columns from the item properties. For example, a property of type bool should map to an <input type="checkbox">.

 

In my DataGrid library, I created a base BdGridComponent, inheriting Blazor’s ComponentBase, from which all my other components could inherit. This class included exposed Parameter properties for many styling values, such as Font, Alignment and Color, so that these could be mapped from code rather than added as css by the consumer. It also defined IsEditable, and a base BuildStyle() method that would then convert the style parameters into css.

BdGridComponent
Lots of styling parameters

 

With this base created, I next tackled the BdColumnDefinition, which is again mostly about styling, but also requires tracking a few extra fields. BindingField will be the property name to bind to this column, and FieldType is a separate type enum to determine how to display the input elements in that column.

BdColumnDefinition.cs

 

I did decide to make each Cell in the grid an input, even when editing is disabled. This just makes it easier to code, rather than having to change the element type when going from editable to non-editable. BdInputCell is my base class, and then I have a variety of child types that inherit this.

 

BdInputCell.cs

 

Speaking of the cells, I am using a mix of C# and Razor files, with the “Code-Behind” pattern, so there is a BdInputCell.razor file, and a BdInputCell.razor.cs code behind file. The IDE does a nice job organizing these, and I much prefer this pattern to the @code {} block in the razor file, especially when the C# starts getting long and complex. If no layout was needed, such as the base BdInputCell, I could stick with a C# class. If no code was needed (or a very small amount), just a razor file would do. This also works well with the Blazor CSS Isolation pattern of using BdInputCell.razor.css for component styling.

 

Using generics, and allowing for multiple mixed patterns of C# Type vs. my FieldType and input types, the Input Cells had to keep track of both an object? Value and a T TypedValue to support proper data binding. This is a pretty simple swap, since the values are actually identical, but it allows the blazor @bind to work correctly.

 

Rolling back up from the input cells, I had to create a BdGridRow. This is where a single Item in the source list is read based on the ColumnDefinitions, and each Cell is created. It’s basically a big switch statement, which also handles event bubbling between the cells and the parent grid.

 

BdGridRow.razor

 

The BdGrid object defines the whole grid as a scrollable div, a header row, and the grid rows. If no column definitions are provided by the consumer, it must iterate through the generic item type and use reflection to create logical columns to display the data. It also needs to handle all data changes, and keeping the bound ItemsSource in sync.

 

BdGrid.razor

 

Under the hood, BdGrid is using the Blazor Virtualize component, which allows the grid to only load the rows necessary for viewing, rather than a potentially huge set of rows in a large collection. This works well, however, I discovered that it does not respond well to binding values changing on it’s Items property. I’m sure there is a more elegant solution to the issue, but after struggling for awhile, I finally settled on a pattern of giving it an empty items list, waiting for a refresh, then giving it the updated list.

 

While developing the grid, I also created a simple blazor test page to tweak all the settings and verify the binding. I plan to keep adding features here as well as to the grid itself. You can see the whole application below or at https://demo.tocode.software/datagrid. I will write another post shortly about how I managed to embed a blazor component into a wordpress blog. Please feel free to check out the code, use it, and make a pull request or report a bug if you have feedback!

 

Loading Blazor Data Grid …

 

 

}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.