Reputation: 652
I am trying to create a custom DataGridViewColumn
to create a modern looking checkbox style column using images.
This is my code so far:
public class CustomDataGridColumn : DataGridViewColumn
{
public CustomDataGridColumn()
{
this.CellTemplate = new CustomDataGridCell();
}
}
public class CustomDataGridCell : DataGridViewCell
{
private static Image img = Resources.UncheckedBox;
public bool Checked { get; set; }
public CustomDataGridCell()
{
Checked = false;
}
protected override object GetFormattedValue(object value,
int rowIndex, ref DataGridViewCellStyle cellStyle,
TypeConverter valueTypeConverter,
TypeConverter formattedValueTypeConverter,
DataGridViewDataErrorContexts context)
{
return img;
}
protected override void OnClick(DataGridViewCellEventArgs e)
{
if (Checked)
{
Checked = false;
//Change the image to Resource.UncheckedBox;
}
else
{
Checked = true;
//Change the image to Resource.CheckedBox;
}
base.OnClick(e);
}
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex,
DataGridViewElementStates cellState, object value, object formattedValue, string errorText,
DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value,
formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
graphics.DrawImage(Resources.CheckedBox, new Point(cellBounds.Width/2, cellBounds.Y));
}
}
There are two PNG images that represent the state of the checkbox (i.e. checked or unchecked). These are stored as Resources and have are accessible by Resources.CheckedBox
and Resources.UncheckedBox
. I only want the image to be in the column, there is no text required. The text in the bugged UI in the example below comes from other columns that are in Data Grid
I have two questions:
When I change the width of the columns by clicking and dragging, the cells do not re-draw properly and the UI becomes bugged. How do I fix this?
When the user clicks the cell, I want the image to change to the correct image depending on the state. How can I do this?
Example screenshot of the bugged UI:
Upvotes: 0
Views: 272
Reputation: 4660
Derive from DataGridViewCheckBoxColumn
and DataGridViewCheckBoxCell
to create a custom check box column and cell instead of deriving from their base classes. The derived classes then inherits all the relevant properties and functionalities of these types and you just need to override the relevant members.
The Column
Derive a new class from DataGridViewCheckBoxColumn
and add a couple of properties for the check/uncheck images.
public class CustomDataGridViewCheckBoxColumn : DataGridViewCheckBoxColumn
{
public CustomDataGridViewCheckBoxColumn() : base()
{
CellTemplate = new CustomDataGridViewCheckBoxCell();
DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
public override DataGridViewCell CellTemplate
{
get => base.CellTemplate;
set
{
if (value != null && !value.GetType().IsAssignableFrom(
typeof(CustomDataGridViewCheckBoxCell)))
throw new InvalidCastException("CustomDataGridViewCheckBoxCell.");
base.CellTemplate = value;
}
}
[DefaultValue(null)]
[Category("Appearance")]
[Description("The check image.")]
public Image TrueImage { get; set; }
[DefaultValue(null)]
[Category("Appearance")]
[Description("The uncheck image.")]
public Image FalseImage { get; set; }
public override object Clone()
{
var c = base.Clone() as CustomDataGridViewCheckBoxColumn;
c.TrueImage = TrueImage;
c.FalseImage = FalseImage;
return c;
}
}
Note, we should override the Clone
method to copy the new properties. Please read Notes to Inheritors.
The Cell
Derive another class from DataGridViewCheckBoxCell
and override the Paint
method to draw the state image if provided in the column class, otherwise let the base class paints the default.
public class CustomDataGridViewCheckBoxCell : DataGridViewCheckBoxCell
{
public CustomDataGridViewCheckBoxCell() : base() { }
protected override void Paint(
Graphics g,
Rectangle clipBounds,
Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates elementState,
object value,
object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
var col = OwningColumn as CustomDataGridViewCheckBoxColumn;
var parts = paintParts;
if (col.TrueImage != null && col.FalseImage != null)
parts &= ~(
DataGridViewPaintParts.ContentBackground |
DataGridViewPaintParts.ContentForeground);
base.Paint(g,
clipBounds,
cellBounds,
rowIndex,
elementState,
value,
formattedValue,
errorText,
cellStyle,
advancedBorderStyle,
parts);
if (parts == paintParts) return;
var img = (bool)formattedValue
? col.TrueImage
: col.FalseImage;
var r = new Rectangle(
(cellBounds.Width - img.Width) / 2 + cellBounds.X,
(cellBounds.Height - img.Height) / 2 + cellBounds.Y,
img.Width, img.Height);
g.DrawImage(img, r, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
}
}
Rebuild and by the designer or code, add the new column type and set the TrueImage
& FalseImage
properties from your resources.
Run...
You might need here to enable the grid's DoubleBuffered
feature to get rid of the flicker or at least to reduce it. Either derive a new class from DataGridView
and set DoubleBuffered = true;
in the constructor, or use Reflection to enable this property if you don't want to create a new type.
In the grid's Form ctor:
// +
using System.Reflection;
// ...
public SomeFormConstructor()
{
InitializeComponent();
yourDGV.GetType()
.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(yourDGV, true);
}
Related
Change CheckBox size of DataGridViewCheckBoxCell and increase the clickable area
Upvotes: 2