Introduction
This article shows you how to set the height of your DataGrid
headers and show multi-line header text. Easily!
Using the code
So, you want to adjust the size of your DataGrid
column headers, or want to support multi-line column header text? Well, here you go…
- Add a project reference to SizableColumnHeaderDataGrid.dll.
- Drop an instance of the
SizableColumnHeaderDataGrid
onto your form or control.
- Set the
ColumnHeaderHeight
property in the property editor (under the Layout group) or in code.
- If you want your column headers to wrap, be sure to use “\n” somewhere in the column header text.
It will look and act just like a regular DataGrid
, but look closely in the properties editor, and you will see an additional property named ColumnHeaderHeight
. Set its value in pixels, and away you go.
You can also set the property at runtime, but if you do, be sure to then call Invalidate()
on your DataGrid
to force a repaint.
col1.HeaderText = "Row Index\nmulitplied\nby 1";
sizableColumnHeaderDataGrid1.ColumnHeaderHeight = 39;
sizableColumnHeaderDataGrid1.Invalidate();
You can download the DLL now and live happily ever after with your new toy, or continue reading for the How did they do that? stuff.
Points of Interest
I fooled around with the DataGrid
and found that, if I changed the font, the header would resize itself. Okay, so I know the header is capable of resizing but cannot find any exposed way to manipulate it. I also found that adding carriage returns to my header text was acceptable, with the *slight* drawback that you can only see the first line of text...
I then used Lutz Roeder’s Reflector for .NET to poke around a bit in the System.Windows.Forms
namespace, and found my way to the DataGrid.ComputeLayout
method which uses the headerFontHeight
to set the height of the ColumnHeaders
rectangle as shown in the following code:
private void ComputeLayout()
{
…
int num4 = this.headerFontHeight + 6;
if (this.layout.ColumnHeadersVisible)
{
Rectangle rectangle6 = data1.ColumnHeaders;
rectangle6 = rectangle3;
rectangle6.Height = num4;
rectangle3.Y += num4;
rectangle3.Height -= num4;
data1.ColumnHeaders = rectangle6;
}
…
}
Woohoo, I’m done!
Or so I thought. Unfortunately, both the ComputeLayout
member and the headerFontHeight
property are private
.
So, how did I make it work? Enter System.Reflection
.
I created a derived DataGrid
class and overrode the OnPaint
and OnLayout
events – these are the two places that call ComputeLayout
in the System.Windows.Forms.DataGrid
class. (Reflector tip: if you right click on a method in Reflector, you can get a Callee Graph to find out who is using the selected member.)
I then used reflection at runtime to get the headerFontHeight
private property, and reset its value before allowing the DataGrid
to paint itself!
Here's the fun reflection stuff:
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;
...
private void setColumnHeaderSize()
{
try
{
if (null != this)
{
DataGrid myClass = new DataGrid();
FieldInfo headerFontHeightFieldInfo =
myClass.GetType().GetField("headerFontHeight",
BindingFlags.NonPublic|BindingFlags.Instance);
Int32 currentHeight =
(Int32)headerFontHeightFieldInfo.GetValue(this);
if (ColumnHeaderHeight >= 0)
{
headerFontHeightFieldInfo.SetValue(this,
ColumnHeaderHeight);
}
}
}
catch (Exception e)
{
System.Diagnostics.Debug.Assert(false, e.ToString());
throw e;
}
}
And here are the slightly less exciting calls to make it work:
private Int32 columnHeaderHeight = 13;
[Browsable(true),
Description("Sets the column header height in pixels"),
Category("Layout"), DefaultValue(13)]
public Int32 ColumnHeaderHeight
{
get
{
return columnHeaderHeight;
}
set
{
columnHeaderHeight = value;
OnLayout(null);
}
}
protected override void OnLayout(LayoutEventArgs levent)
{
setColumnHeaderSize();
base.OnLayout (levent);
}
protected override void OnPaint(PaintEventArgs e)
{
setColumnHeaderSize();
base.OnPaint (e);
}
Caveat Emptor
This code works using reflection to monkey with private members of the System.Windows.Forms.DataGrid
class. I cannot guarantee that it will continue to work if Microsoft changes code in that class. I have tested this only against the .NET Framework 1.1.
Yeah that's cool, but what if it could…
Some fun improvements available for someone with more free time:
- Make the header text auto-wrap based on the width of the column.
- Make the column headers auto update their height to fit the entire header text, given the width of the column.
- Make the headers sizable with mouse drags (in design-time and run-time).
History
- Version 1 - released 10/26/2005.