Usually, when you create a user interface that allows users to view the information stored in their database, you deal with what can be called one dimensional views. By that I understand a well known grid with fixed number of columns and properties and varying number of records.
Recently I have taken part in a project that required somehow different approach. The client requested a significant number of pivotal views. Because they deal with market analysis, they wished to see most of the data presented in a way they could see how it changes over time.
One example of such a need can be the product sales analysis. Let’s see how our database looks like.
As you can see we start with orders that occur at some specificity point in time. Next for each of them we have a collection of details. Every detail points to a product, also it stores the information about the quantity and price which were true at the time of transaction.
This simple data model is represented in our application using the following classes.
public class Product
{ public int Id
{ get;
set;
}
public string Name
{ get;
set;
}
public ICollection<OrderDetail> Details
{ get;
set;
}
}
public class Order
{ public int Id
{ get;
set;
}
public DateTime Date
{ get;
set;
}
public ICollection<OrderDetail> Details
{ get;
set;
}
}
public class OrderDetail
{ public int Id
{ get;
set;
}
public Order Order
{ get;
set;
}
public Product Product
{ get;
set;
}
public int Quantity
{ get;
set;
}
public decimal Price
{ get;
set;
}
}
Our goal is to create user interface that would present the total sales value for every product and every year from our database. We would like all of our products to appear as row headers and all of the years from our database to appear as column headers. On every intersection of those two we would like to have a cell representing that aggregated sales value.
When we think row and columns, we usually turn to some kind of grid control. What every grid control needs is a data source or more simply a collection of objects it can bind to.
Such an a object would have to declare a Product property so we know which product should be displayed as a row header. Then for each year analyzed we need one property that holds the aggregated sales value for that year.
Unfortunately we don’t have such an objects. We simply cannot declare a class if we don’t know exactly what properties it has, and that’s the case because the set of the years being analyzed is unknown during the development phase.
There is a solution however. It may become clearer to us if we first understand how most of the grid controls work. And they usually follow the same pattern regardless of the technology you use, Windows Forms, WPF, ASP.NET.
When you tell your grid to bind a column to a property you specify the name of the property in some dedicated setting and expect the control to get that property value from every object you supply to it.
How the grid knows if the objects you give it even declare such a property?
The first method that comes in mind is reflection.
Reflection is a process of inspecting the existing classes structure during the runtime. By using it you can browse the class definition and find out anything you would like to know about it. It allows you to check the base class of a given class, list all of its methods together with their visibility level and their parameters. It also lets you see what kind of properties your class declares.
There is one downside however. Again the reflection mechanism knows only about the members you declared in a development stage and that is unfortunately not our case.
What we would like to do is to skip the development part, no matter how it sounds. Our goal is to move both the properties declaration and their discovery to a runtime phase.
We need another method.
The System.ComponentModel.ICustomTypeDescriptor interface comes to rescue.
By making your class implement the ICustomTypeDescriptor interface you simply say “Hey, forget the reflection. My class knows better what kind of properties it has.”
The ICustomTypeDescriptor interface declares a method GetProperties that returns a System.ComponentModel.PropertyDescriptorCollection object which in turn holds the information about every property the class exposes packed inside the System.ComponentModel.PropertyDescriptor object.
Under the hood every data bound grid uses the System.ComponentModel.TypeDescriptor class also offering the GetProperties method. This method checks if the object implements the ICustomTypeDescriptor interface. If it does it allows the object to describe itself be calling its GetProperties method. Otherwise it builds it own instance of the PropertyDescriptorCollection class using the reflection mechanism.
By giving us the TypeDescriptor class and ICustomTypeDescriptor interface the .NET team has in fact killed two birds with one stone. It allowed us to override the actual structure of the type and provided the unified way for controls developers to browse the class definition.
Now when we understand how it all works we can use this technique to create the records for our grid. What we are going to do is to create a class declaring the Product property and allow it to extend itself by adding a unique property for every year.
The next part of this article, containing the actual implementation, is coming soon.
Please stay tuned.