Article Index:
Without further ado, here is my RowNumberDataGridViewDecorator class, which decorates a DataGridView to display row numbers in the row header column:
// This class decorates an instance of a DataGridView to show row
// numbers in the row header cells and to automatically adjust the
// width of the column containing the row header cells so that it can
// accommodate the new row numbers. All of the "heavy lifting" is done
// in the decorated DataGridView's RowPostPaint event (adapted from:
// http://www.danielsoper.com/programming/DataGridViewNumberedRows.aspx)
public class RowNumberDataGridViewDecorator
{
// The decorated DataGridView.
private DataGridView dataGridView;
// Backing field for ShowRowNumber property.
private bool showRowNumbers;
// Constructor
// dataGridView - the DataGridView to be decorated
public RowNumberDataGridViewDecorator(DataGridView dataGridView)
{
if (dataGridView == null)
{
throw new ArgumentNullException("dataGridView");
}
this.dataGridView = dataGridView;
// decorate dataGridView with row number behaviour
this.dataGridView.RowPostPaint += new
DataGridViewRowPostPaintEventHandler(
this.dataGridView_RowPostPaint);
}
// If true, row numbers will be shown in row header.
public bool ShowRowNumbers
{
get
{
return showRowNumbers;
}
set
{
bool changed = false;
if (showRowNumbers != value)
{
changed = true;
}
showRowNumbers = value;
if (changed)
{
// force control to be repainted
dataGridView.Refresh();
}
}
} // end of property ShowRowNumbers
// Paints the row numbers in the row header column.
private void dataGridView_RowPostPaint(
object sender, DataGridViewRowPostPaintEventArgs e)
{
if (showRowNumbers && sender == dataGridView)
{
// Store a string representation of the row number
// in 'strRowNumber'
string strRowNumber = (e.RowIndex + 1).ToString();
// Prepend leading zeros to the string if necessary to improve
// appearance. For example, if there are ten rows in the grid,
// row seven will be numbered as "07" instead of "7". Similarly,
// if there are 100 rows in the grid, row seven will be numbered
// as "007".
while (strRowNumber.Length < dataGridView.RowCount.ToString().Length)
{
strRowNumber = "0" + strRowNumber;
}
// Determine the display size of the row number string using
// the DataGridView's current font.
SizeF size = e.Graphics.MeasureString(
strRowNumber, dataGridView.Font);
// Adjust the width of the column that contains the row header
// cells if necessary.
if (dataGridView.RowHeadersWidth < (int)(size.Width + 20))
{
dataGridView.RowHeadersWidth = (int)(size.Width + 20);
}
// This brush will be used to draw the row number string on the
// row header cell using the system's current ControlText color.
Brush b = SystemBrushes.ControlText;
// Draw the row number string on the current row header cell using
// the brush defined above and the DataGridView's default font.
e.Graphics.DrawString(
strRowNumber,
dataGridView.Font,
b,
e.RowBounds.Location.X + 15,
e.RowBounds.Location.Y + ((e.RowBounds.Height - size.Height) / 2));
}
} // end of dataGridView_RowPostPaint
} // end of class RowNumberDataGridViewDecorator
Note that all of the heavy lifting is done in dataGridView_RowPostPaint(), which the decorator attaches to the wrapped DataGridView’s RowPostPaint event. The key is that client code can happily work with the wrapped DataGridView, blissfully unaware of the row numbering behavior that was added to it via the decorator.
Note that I modified Daniel’s Soper’s original row numbering code by adding a ShowRowNumbers property to optionally enable or disable the row numbering behavior. To use that property, client code would need to keep a reference to the decorator so, in this case, client code would be aware of the decorator. Nevertheless, I needed the ability to enable or disable the behavior on demand so I allowed for this additional departure from the classic Decorator pattern.
To complete my example, here is a code snippet where I create and use the decorator:
public partial class Form1 : Form
{
private RowNumberDataGridViewDecorator decorator;
public Form1()
{
InitializeComponent();
decorator = new RowNumberDataGridViewDecorator(this.dataGridView1);
decorator.ShowRowNumbers = true;
}
// other code...
}
Note that the wrapped object, dataGridView1, was created automatically by the Visual Studio designer in a separate file.
Throughout my application, I have successfully applied the RowNumberDataGridViewDecorator and similar decorators to existing instances of DataGridView and also third-party subclasses of DataGridView.
If this article helped you, please feel free to leave a comment.
Article Index:







Joe, your decorator pattern was exactly what I was looking for. Thanks!