Tuesday, December 7, 2010

DataGridView Custom CheckBox Column

I lately had a need to a checkbox column in a DataGridView, but with custom images (plus, minus and a question mark). DataGridViewCheckBoxColumn images cannot be changed, so I had two options: Customize the OnPaint event, or recreate a new DataGridViewColumn.

I decided to go on the second option. I created a DataGridViewThreeStateCheckBoxColumn class(I prefer long names on abbreviations):

public class DataGridViewThreeStateCheckBoxColumn : DataGridViewImageColumn
{
public DataGridViewThreeStateCheckBoxColumn(ImageList images, bool isThreeState)
{
this.CellTemplate = new DataGridViewThreeStateCheckBoxCell(images, isThreeState, true);
}
}


And a DataGridViewThreeStateCheckBoxCell class:

public enum eState {Unknown = 0, Positive = 1, Negative = 2}
public class DataGridViewThreeStateCheckBoxCell : DataGridViewImageCell
{
private Dictionary StateToImage { get; set; }

private bool IsReadOnly { get; set; }

private eState cellState;
public eState CellState
{
get
{
return cellState;
}
set
{
cellState = value;
this.Value = StateToImage[cellState];
}
}

public DataGridViewThreeStateCheckBoxCell()
{
}

public DataGridViewThreeStateCheckBoxCell(ImageList images, bool isThreeState, bool isReadOnly)
{
StateToImage = new Dictionary { { eState.Unknown, images.Images["question-mark"] }, { eState.Positive, images.Images["plus"] } };
if(isThreeState)
StateToImage[eState.Negative] = images.Images["minus"];
CellState = eState.Unknown;
IsReadOnly = isReadOnly;
}

protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
if (IsReadOnly)
return;

// toggle the cell state
CellState = ((eState)(((int)CellState + 1) % StateToImage.Count));

base.OnMouseClick(e);
}
}


In may case I also need a way to notify the parent on state changes, to update a database. I did this by creating an event that the parent was able to register to.

Thursday, July 22, 2010

Overriding ExpandableObjectConverter::GetProperties

Today I was faced with a BrowsableAttribute problem:

I had a collection that I needed to be able to select from a PropertyGrid, but also be able to edit the items and their properties with a DataGridView. I wanted the PropertyGrid to show only the item name in an expandable control and to be able to edit all the items property in the DataGridView.

When using BrowsableAttribute I had a problem that each time I would have to set the value according to the requesting control. I solved this by overriding ExpandableObjectConverter::GetProperties. I went over the PropertyDescriptorCollection and removed each property I didn't want:

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection collection = base.GetProperties(context, value, attributes);
PropertyDescriptor[] final = new PropertyDescriptor[1];
foreach (PropertyDescriptor descriptor in collection)
{
if (descriptor.Name == "ItemName")
final[0] = descriptor;
}
return new PropertyDescriptorCollection(final);
}

Now when binding the list to DataGridView I would see all the properties, and when expanding the list in PropertyGrid I would see only the Item Name Property.

Saturday, May 15, 2010

Capturing keyboard events in a form

When wishing to capture a keyboard event in winforms, one might encounter a problem that although you can register to capture the keyboard events, the events don't reach the parent form.

The simplest way is to override ProcessCmdKey and processing the message it receives.

I wonder this is so...

Wednesday, May 5, 2010

"Constructor on type 'System.String' not found"

This exception I got yesterday while trying to add an item to a collection in this string collection editor:
















I found no simple solution on the web, but after some thought had this solved by changing the property from a list to an array of strings. This changed the default editor to this simpler string collection editor that worked good enough for me: