Introduction
When you use the DateGrid
control to populate tabular data, the DataGrid.AutoGenerateColumns
property provides a handy approach to generate data columns dynamically. However, the DataGrid
actually displays all properties with property name instead of header name. It would be helpful if you have control about the column generation. This article presents a solution to easily specify the header information while getting the benefits from the DataGrid.AutoGenerateColumns
.
Using the Code
The solution introduces a smart Behavior
object plugged into the DateGrid
so that the header information specified in the attributes of the data item will be generated automatically. In the data item:
- You can specify the
string
to be displayed for each column header
- You can specify a blank header; and
- You can decide which columns to show or hide with little effort
It takes three steps to use the Behavior
object.
- Add the reference of
System.Windows.Interactivity
, and add the SmartColumnBehavior.cs to the project.
- Specify the header in the property of the data item with the attribute. If you don’t want to display the property, don’t add the attribute. Here is a self-explanatory example of “attributed” Data Item.
public class DataItem
{
public int Id { get; set; }
[DisplayName(" ")]
public bool AddToBag { get; set; }
[DisplayName("Theme Name")]
public string ThemeName { get; set; }
}
- In the XAML file, plug in the
Behavior
object as follows:
<Window ...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:AttributtedDataColumn" />
<DataGrid ItemsSource="{Binding DataItems}" >
<i:Interaction.Behaviors>
<local:SmartColumnBehavior />
</i:Interaction.Behaviors>
</DataGrid>
And now, you are ready to compile and run!
Under the Hood
The DataGrid.AutoGeneratingColumn
event offers you an opportunity to manipulate the data columns during the process of column generation if you set the DataGrid.AutoGenerateColumns
property to true
. There are many ways to implement the event handler. I elect to wrap it in a Behavior
object to make it pluggable and reusable. When being attached to the DataGrid
control, it gives the DataGrid
an additional behavior. In this implementation, the logic in the event handler detects the attribute of data item’s property, and returns information for generating the column and header information according. Furthermore, if you need more features such as icons, filters, etc., you can add your extension to the Behavior
object. Following is the source code of the Behavior
.
public class ColumnHeaderBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
AssociatedObject.AutoGeneratingColumn +=
new EventHandler<DataGridAutoGeneratingColumnEventArgs>(OnAutoGeneratingColumn);
}
protected override void OnDetaching()
{
AssociatedObject.AutoGeneratingColumn -=
new EventHandler<DataGridAutoGeneratingColumnEventArgs>(OnAutoGeneratingColumn);
}
protected void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
string displayName = GetPropertyDisplayName(e.PropertyDescriptor);
if (!string.IsNullOrEmpty(displayName))
{
e.Column.Header = displayName;
}
else
{
e.Cancel = true;
}
}
protected static string GetPropertyDisplayName(object descriptor)
{
PropertyDescriptor pd = descriptor as PropertyDescriptor;
if (pd != null)
{
DisplayNameAttribute attr = pd.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
if ((attr != null) && (attr != DisplayNameAttribute.Default))
{
return attr.DisplayName;
}
}
else
{
PropertyInfo pi = descriptor as PropertyInfo;
if (pi != null)
{
Object[] attrs = pi.GetCustomAttributes(typeof(DisplayNameAttribute), true);
foreach (var att in attrs)
{
DisplayNameAttribute attribute = att as DisplayNameAttribute;
if ((attribute != null) && (attribute != DisplayNameAttribute.Default))
{
return attribute.DisplayName;
}
}
}
}
return null;
}
}
The Behavior
class is a generic class design to attach additional behaviors to a control. In the example, the control type is the DataGrid
. Therefore I derive the SmartColumnBehavior
class from Behavior
. I then override the OnAttached()
method. The class encapsulates the column generation logic, and will be attached to the DataGrid
in the XAML. When the DataGrid
generates data columns, DataGrid.AutoGeneratingColumn
event will be fired for each data column. This is the place to handle the custom column generation. In the Behavior
object, I subscribed the AutoGeneratingColumn
event, and detected the header display name. For each property in the data item, if I can find the display name in the attribute, I assign it to the column header in e.Column.Header
. If I cannot find the attribute, I cancel the column generation by setting e.Cancel
to true
. The e.Column
property contains a lot of properties for custom column generation. The above is just a simple implementation.