Introduction
One of the advantages of working with Windows Forms applications is the ability to include domain objects directly into UI components as it is done with dropdowns and listboxes, for example. Among UI components, tabular grids are very useful since they provide a broad view from an information perspective, and are very much used in CRUD windows.
Let's suppose a system is asked to be built with a grid of categories with some relevant information. A Category
class is already available, and is declared as a POCO class with the following C# code:
using System;
namespace DomainObjects
{
public class Category
{
private long _id;
public virtual long Id
{
get { return this._id; }
set { this._id = value; }
}
private string _categoryName;
public virtual string CategoryName
{
get { return this._categoryName; }
set { this._categoryName = value; }
}
private string _description;
public virtual void SetDescription(string description)
{
this._description = description;
}
public virtual string GetDescription()
{
return this._description;
}
}
}
For that purpose, in the .NET framework, we can find the DataGrid
. In order to display objects data in a DataGrid
, the DataSource
property must receive a collection of these objects.
IList<category> lstCategories = PopulateCategoriesList(lstCategories);
this.dataGridView1.DataSource = lstCategories;
As a result of a list of Categories, the following grid is obtained:
Note that the DataGrid
component can only see the properties Id
and CategoryName
and display their contents as columns.
The DataGrid
presents the following disadvantages when used with a collection of objects:
- It is very restrictive is terms of reflective mappings since it can only see public properties as columns in the grid, making it not possible to be used in a big variety of situations with objects;
- Configuring column features such as headers and widths is not an intuitive task;
- It is not possible to traverse an object association to display data from another object in a higher depth;
In order to overcome these drawbacks, this article proposes a grid for Domain Objects capable of mapping fields and associations between objects to columns in a grid table, referred as ObjectGrid
.
Sample Usage
To use ObjectListGrid
, first, you should drag the component to your window and rename it as gridResults
. After replacing the grid above with an ObjectGrid
named as gridResults
, it must be configured before receiving its object items.
this.gridResults.AddColumn(new string[] {"CategoryName"},"Name",80);
this.gridResults.AddColumn(new string[] {"GetDescription"}, "Description", 200);
this.gridResults.DataSource = PopulateCategoriesList();
Below shown is the resulting grid:
Transforming the Object Graph into a Grid Table
The ObjectGrid
is a .NET User Component that wraps a DataGrid
. It has methods to describe column characteristics, and must receive a collection of objects as a DataSource.
These columns are mapped into values by a chain of member names, which is used by the component to traverse the object graph from the object in the collection, to get a specific data of the final object in the chain through Reflection.
Since it uses reflective information of member names, the same ObjectGrid
can be used to display different types of objects, provided that they contain these members' names.
Another Usage
Take, for example, a grid of Suppliers taken from the UML below:
In this grid, it is asked to display the following data from the suppliers: company name, phone, street, city, state and country.
Four steps are necessary to use this component in a Windows Forms application:
- Drag the component in the desired space in a Windows form.
- Add the columns describing the title, length, and object members.
this.gridResults.AddColumn(new string[]{"CompanyName"},"Name",80);
this.gridResults.AddColumn(new string[]{"ContactName"},"Contact",90);
this.gridResults.AddColumn(new string[]{"Phone"},"Phone",50);
this.gridResults.AddColumn(new string[]{"HomePage"},"Home Page",110);
this.gridResults.AddColumn(new string[]{"Street","Name"},"Street",50);
this.gridResults.AddColumn(new string[]{"Street","City","Name"},"City",50);
this.gridResults.AddColumn(new string[]{"Street","City","State","Name"},"State", 50);
this.gridResults.AddColumn(new string[]{"Street","City", "State","Country",
"Name"},"Country",50);
If you know this grid will be used only for suppliers, the columns can be added to the grid in the form initialization.
The first parameter is an array of member names (be it a method, property, or field). The second is the column title, and the third is the column width. The first parameter is also the key aspect of this grid. The map method is responsible for mapping the columns of the domain grid navigating through the object graph in memory.
For example, in order to get a supplier's city name, it is necessary to execute supplier.Street.City.Name
. Thus, the column Street is mapped as {“Street”,”City”,”Name”}.
Theoretically, the ObjectGrid
is capable of navigating through object associations of N-depth, and the only requirement in order to avoid exceptions from this component is that the object must have the mapped member name implemented, and that the member return value implements the forward member name, and so on.
- Set a collection of objects to the component. Each object must implement the mapped members.
this.olstgResults.DataSource = PopulateSuppliersList();
Adding, Updating, and Removing Objects to the Grid
In order to modify the data displayed by the grid, new objects can be added or removed from the grid with the Item
property.
this.gridResults.Items.Add(category);
this.gridResults.Items.Remove(category);
When an object inside the list is altered, the grid must be notified with the method UpdateView
.
category.SetDescription("New description");
this.gridResults.Refresh();
Recovering Selected Object
Besides easy configuration to view the data, another advantage of working with the ObjectGrid
is that the object that represents a selected line can be recovered to the application.
After selecting a grid line, like this:
the selected object can be recovered with the CurrentObject
property:
object selectedObject = this.gridResults.CurrentObject;