Alex
Alex

Reputation:

How to display an image in a datagridview column header?

At run-time, I am adding a DataGridView to a windows form. The final column is a DataGridViewImageColumn:

Dim InfoIconColumn As New DataGridViewImageColumn
MyDataGridView.Columns.Insert(MyDataGridView.Columns.Count, InfoIconColumn)

Adding the following code will get my Information Icon (bitmap) to display in each of the column cells but NOT the column header:

Dim InfoIcon As New Bitmap("C:\MyPath\InfoIcon.bmp")
InfoIconColumn.Image = InfoIcon

Also, it is worth noting that the image displays 'perfectly' in the cells i.e. it is sized correctly to fit the cell.

However, I cannot find a way to add the same image to the column header cell. After some googling I used the following code which placed the image in the header cell but left me with two problems:

  1. The image did not 'auto-size' to the column headercell in the same way it did when added to the column cells. The image was slightly larger and blurred.
  2. By using the _CellPainting event slowed down performance i.e. when hovering over the DataGridView to highlight the selected row the highlighting lagged behind where my mouse was placed.

Here is the code:

Private Sub MyDataGridView_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles MyDataGridView.CellPainting
   Dim InfoIcon As Image = Image.FromFile("C:\MyPath\InfoIcon.bmp")
   If e.RowIndex = -1 AndAlso e.ColumnIndex = MyDataGridView.Columns.Count - 1 Then
       e.Paint(e.CellBounds, DataGridViewPaintParts.All And Not   DataGridViewPaintParts.ContentForeground)
       e.Graphics.DrawImage(InfoIcon, e.CellBounds)
       e.Handled = True
    End If
End Sub

Does anybody know of a way to solve my problem and get a nicely sized, sharp image into a DataGridViewImageColumn headercell at run-time?

Upvotes: 10

Views: 38463

Answers (4)

Kredns
Kredns

Reputation: 37211

One way you can do this is to use the CellsPainting event to draw the bitmap for a particular header cell. Here is code that does this assuming the bitmap is in an imagelist.

//this.images is an ImageList with your bitmaps
void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    if (e.ColumnIndex == 1 && e.RowIndex == -1)
    {
        e.PaintBackground(e.ClipBounds, false);

        Point pt = e.CellBounds.Location;  // where you want the bitmap in the cell

        int offset = (e.CellBounds.Width - this.images.ImageSize.Width) / 2;
        pt.X += offset;
        pt.Y += 1;
        this.images.Draw(e.Graphics, pt, 0);
        e.Handled = true;
    }
}

Upvotes: 18

Mogahed Nasr
Mogahed Nasr

Reputation: 11

Try This:

Private Sub DataGridView1_CellPainting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
        If e.RowIndex > -1 Then

            If e.ColumnIndex = -1 Then
                e.Paint(e.CellBounds, DataGridViewPaintParts.Focus And Not DataGridViewPaintParts.ContentForeground)
                DataGridView1.RowHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single
                e.Graphics.DrawImage(IconImg, e.CellBounds)
                e.Handled = True
                'DataGridView1.RowHeadersWidth = 100
                'DataGridView1.ColumnHeadersHeight = 25
            End If

        End If

Upvotes: 1

Vyktor
Vyktor

Reputation: 20997

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.

Upvotes: 4

Herry
Herry

Reputation: 11

try this code:

    Private Sub DataGridView1_CellPainting(ByVal sender As System.Object, _
            ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) _
            Handles DataGridView1.CellPainting
        If e.RowIndex = -1 AndAlso e.ColumnIndex = DataGridView1.Columns.Count - 1 Then
            e.Paint(e.CellBounds, DataGridViewPaintParts.All And Not DataGridViewPaintParts.ContentForeground)
            e.Graphics.DrawImage(IconImg, e.CellBounds)
            e.Handled = True
        End If
    End Sub

if you have read full article for doing this check This link:

http://www.authorcode.com/add-image-on-datagridview-column-header-in-vb-net/

Upvotes: 0

Related Questions