C# – Thoughts on having a generic, reusable grid viewmodel

cdesigndesign-patternsnetwpf

I'm developing a -based application and I'm currently struggling to find the best way of supporting a recurring scenario in it.

This app uses grids a lot. There are a bunch of features I want in it that are meant to be used in grids, such as drag & drop, multiple row edition, conditional row coloring, etc. But at the same time, I want my data objects to derive from a specific, data-oriented class that is unaware of any UI context and could be used by a single form, with standalone editors.

Therefore, I'd like to have a DataGridViewModel class that will in turn expose two collections: Rows as DataGridRowViewModel and Columns as DataGridColumnViewModel. The row view model would expose a RowData property which will be the actual data object, while the column view model would have a PropertyDescriptor (from System.ComponentModel namespace) that will indicate which data object field the column is meant for.

You can think of the DataGridRowViewModel as a container for the "real" row object.

The goal is to allow separation of concerns between grid-specific functionalities (controlling row height, color, selection, etc) and data-specific functionalities, so that one doesn't need to know about the other. Ideally, a third component will be aware of both and ensure proper communication between the two layers (observing a data field and change the row color under condition x or y, for example).

Does this approach makes any sense? What kind of complications should I expect out of this (binding issues, performance and the likes)?

Best Answer

I've been working with grids for a while and had simmilar idea and scenarios. There is a flaw in this approach: you want many features on your grid, but this means you'll have many very different kinds of data in it. So your grid will have to visualize and edit all this data.

This isn't a problem by itself, but, this means that grid will become "God Object" itself. There is a different approach to solve this: make grid as a "thin" container. I mean don't think of a grid as a cells with text.

Think of it as a layouts.

Then:

  • Grid is really a list of rows. Each row can have cells, so you'll have to create some mechanism to sync columns' width, but some rows may be special (grouping, totals, details, seprators, spans ...)
  • Each cell is really a panel for other components: it can be quite complex, so it won't be just text in it: there can be an edit with 2 buttons and an image.
  • This way grid and row doesn't know about what the cell is, except that it's size and location.
  • This all means that binding data to cells isn't binding data to two dimensional array of values, but rather binding an array of rows to an array of objects.
  • Rows should handle binding cells to object's fields or to some computed values over some fields.
  • Cells should pass bindings to their child components
  • And the components inside cells (texboxes in simplest and most common cases) will finally handle the binding.

The approach I proposed is quite complex to implement though. Maybe you don't need such complex cases. Also be warned that having this many components requires optimizations (specifically in simple and most common cases). But in the result you can have a swiss-knife solution for all scenarios with grids.

Comparing to your proposial it have some benefits (and your proposial have limitations in this areas): it can take data from deep fields inside your objects, it can have computed values, you don't have to convert your objects to "RowData@, it can handle several hungry components in one cell.

For your question "Is this really ok?" - yes it is. There are several big frameworks do grids your way, from what comes to my mind right now: Qt have this way of binding. So your approach is a well known good practice anyway too whith some known problems and limits though.

Related Topic