dotnet windows forms controls:
Dear DataGridView lovers,
I want a DataGridViewColumn that acts like a button but that shows an
image. Furthermore, when the user mouse-overs the image or clicks on it,
I want the image to change.
I've written some code the works, KINDA. Actually it works well, except
it generates error messages in the designer. I can't figure out why.
So, I'm going to post my code for 2 reasons:
1- To solicit help on perfecting the code. If you are a master at C#
custom controls, I need thee. Most importantly, there is an error that
happens not at run-time but in the designer, and I can't figure it out.
2- To share the code with others who are trying to write similar controls.
Problems with my code:
1- If you use the control in a DataGridView, close the form, then
re-open the form, you get an "Object reference not set to an instance of
an object" error message. You get 3 instances of that error, one for
each of the Images associated with the control. I'll paste a sample call
stack at the bottom of this message. (If you ignore the error and
continue then everything works properly. This error only appears in the
designer, not at run-time.)
2- I wanted a traditional Click event for the button. (The normal way of
testing for clicks within a DataGridView is a pain.) The way I got this
to work may is somewhat inelegant. It works though.
3- In order to get the mouseovers to work properly, I made
MouseEnterUnsharesRow always return true. This is probably bad design,
because it could lead to lots of memory being used by the DataGridView.
Without further ado, I give you the DataGridViewImageButtonColumn!
<cue applause>
******************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace CustomControls
{
public class DataGridViewImageButtonCell : DataGridViewTextBoxCell
{
public DataGridViewImageButtonCell()
: base()
{
}
enum MultiStateImageStates
{
StateNormal,
StateMouseOver,
StateClicked
}
Image imageToDraw = null;
void SetImageFromState(MultiStateImageStates state, int rowIndex)
{
DataGridViewImageButtonColumn parent =
(DataGridViewImageButtonColumn)this.OwningColumn;
if (parent != null) {
switch (state) {
case MultiStateImageStates.StateNormal:
imageToDraw = parent.ImageNormal;
break;
case MultiStateImageStates.StateMouseOver:
imageToDraw = parent.ImageMouseOver;
break;
case MultiStateImageStates.StateClicked:
imageToDraw = parent.ImageClicked;
break;
}
}
if (imageToDraw != null) {
parent.DataGridView.InvalidateCell(parent.Index, rowIndex);
}
}
protected override void Paint(System.Drawing.Graphics graphics,
System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle
cellBounds, int rowIndex, DataGridViewElementStates elementState, object
value, object formattedValue, string errorText, DataGridViewCellStyle
cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
const int padding = 5;
base.Paint(graphics, clipBounds, cellBounds, rowIndex,
elementState, value, String.Empty, errorText, cellStyle,
advancedBorderStyle, paintParts);
if (imageToDraw == null) {
DataGridViewImageButtonColumn parent =
(DataGridViewImageButtonColumn)this.OwningColumn;
if (parent != null) {
imageToDraw = parent.ImageNormal;
}
}
if (imageToDraw != null) {
graphics.DrawImage(imageToDraw, cellBounds.Left +
padding, cellBounds.Top + padding);
}
}
protected override void OnMouseEnter(int rowIndex)
{
SetImageFromState(MultiStateImageStates.StateMouseOver,
rowIndex);
base.OnMouseEnter(rowIndex);
}
protected override void OnMouseLeave(int rowIndex)
{
SetImageFromState(MultiStateImageStates.StateNormal, rowIndex);
base.OnMouseLeave(rowIndex);
}
protected override void
OnMouseDown(DataGridViewCellMouseEventArgs e)
{
SetImageFromState(MultiStateImageStates.StateClicked,
e.RowIndex);
base.OnMouseDown(e);
}
protected override void OnClick(DataGridViewCellEventArgs e)
{
DataGridViewImageButtonColumn parent =
(DataGridViewImageButtonColumn)this.OwningColumn;
if (parent != null) {
parent.CellWasClicked(this);
}
base.OnClick(e);
}
protected override bool MouseEnterUnsharesRow(int rowIndex)
{
return true;
}
}
public class DataGridViewImageButtonColumn : DataGridViewTextBoxColumn
{
public DataGridViewImageButtonColumn()
: base()
{
this.ReadOnly = true;
DataGridViewImageButtonCell template = new
DataGridViewImageButtonCell();
base.CellTemplate = template;
}
void SetRowHeightFromImage(Image image)
{
const int verticalPadding = 10;
if (image != null) {
int minimumHeight = image.Height + verticalPadding;
if (this.DataGridView.RowTemplate.Height < minimumHeight) {
this.DataGridView.RowTemplate.Height = minimumHeight;
}
}
}
Image imageNormal = null;
public Image ImageNormal
{
get { return imageNormal; }
set
{
imageNormal = value;
SetRowHeightFromImage(imageNormal);
}
}
Image imageMouseOver = null;
public Image ImageMouseOver
{
get { return imageMouseOver; }
set
{
imageMouseOver = value;
SetRowHeightFromImage(imageMouseOver);
}
}
Image imageClicked = null;
public Image ImageClicked
{
get { return imageClicked; }
set
{
imageClicked = value;
SetRowHeightFromImage(imageClicked);
}
}