At run-time, I am adding a DataGridView
to a windows form. The final column is a DataGridViewImageColumn
:
Dim InfoIconColumn As New
I needed a bit more complex thing - adding image in front of text in some column headers with respect to column alignment.
You need to implement your own System.Windows.Forms.DataGridViewColumnHeaderCell and replace ColumnHeaderCell
:
// Create header and set up image
YourDataGridViewColumnHeaderCell headerCell = new YourDataGridViewColumnHeaderCell();
headerCell.Image = something;
// Create column
DataGridViewColumn yourColumn = new DataGridViewTextBoxColumn();
// ...
yourColumn.ColumnHeaderCell = new headerCell;
Now the funny part (implementation of your column header):
class YourDataGridViewColumnHeaderCell : System.Windows.Forms.DataGridViewColumnHeaderCell
{
// Set up image as you want
System.Drawing.Image Image { get; set; }
}
Now we want to add Paint() method. The only tricky part is working with System.Windows.Forms.DataGridViewPaintParts.
protected override void Paint( Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates dataGridViewElementState,
object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts )
{
// Outside header or without an image, use default painting
if ((rowIndex != -1) || (Image == null)) {
base.Paint( graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts );
return;
}
// Borders, background, focus selection can remain the same
// But Foreground will have different image
base.Paint( graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle,
advancedBorderStyle, paintParts & ~DataGridViewPaintParts.ContentForeground );
// Repainting of content background (that's where we want to place our image)
if ((paintParts & DataGridViewPaintParts.ContentBackground) != DataGridViewPaintParts.None) {
// +4 is hardcoded margin
Point bounds = new Point( cellBounds.X + 4, cellBounds.Y );
// Handle vertical alignment correctly
switch (cellStyle.Alignment) {
// Top
case DataGridViewContentAlignment.TopLeft:
case DataGridViewContentAlignment.TopCenter:
case DataGridViewContentAlignment.TopRight:
// Already set
break;
// Middle
case DataGridViewContentAlignment.MiddleLeft:
case DataGridViewContentAlignment.MiddleCenter:
case DataGridViewContentAlignment.MiddleRight:
bounds.Y = cellBounds.Y + (cellBounds.Height - Image.Height) / 2;
break;
// Bottom
case DataGridViewContentAlignment.BottomLeft:
case DataGridViewContentAlignment.BottomCenter:
case DataGridViewContentAlignment.BottomRight:
bounds.Y = cellBounds.Y + (cellBounds.Height - Image.Height);
break;
}
graphics.DrawImage( Image, bounds );
}
// Foreground should be shifted by left image margin + image.width + right
// image margin and of course target spot should be a bit smaller
if ((paintParts & DataGridViewPaintParts.ContentForeground) != DataGridViewPaintParts.None) {
Rectangle newCellBounds = new Rectangle( cellBounds.X + 4 + Image.Width + 4, cellBounds.Y, cellBounds.Width - Image.Width - 8, cellBounds.Height );
base.Paint( graphics, clipBounds, newCellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle,
advancedBorderStyle, DataGridViewPaintParts.ContentForeground );
}
}
If you want to use AutoSizeColumnsMode set to DataGridViewAutoSizeColumnsMode.ColumnHeaders (so you would autofit image and text) you need to override DataGridViewColumnHeaderCell.GetPreferredSize. I did this by using base implementation and adding Image.Width + Padding
to it.
protected override Size GetPreferredSize( Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex,Size constraintSize )
{
// Load up original image
Size original = base.GetPreferredSize( graphics, cellStyle, rowIndex, constraintSize );
// Ensure the image is set and that we are working on header
if ((rowIndex != -1) || (Image == null)) {
return original;
}
// -1 is reserved value
if (original.Width < 0) {
return original;
}
return new Size( original.Width + Image.Width + 4, original.Height );
}
NOTE: I've spent several hours digging in .NET sources until I figured this out. Hopefully you won't have to.