InvalidCastException in DataGridView
(Using VS 2010 Beta 2 - .Net 4.0 B2 Rel)
I have a class, MyTable, derived from BindingList where S is a struct. S is made up of several other structs, for example:
public class MyTable<S>:BindingList<S> where S: struct
{
...
}
public struct MyStruct
{
public MyReal r1;
public MyReal r2;
public MyReal R1 {get{...} set{...}}
public MyReal R2 {get{...} set{...}}
...
}
public struct MyReal
{
private Double d;
private void InitFromString(string) {this.d = ...;}
public MyReal(Double d) { this.d = d;}
public MyReal(string val) { this.d = default(Double); InitFromString(val);}
public override string ToString() { return this.real.ToString();}
public static explicit operator MyReal(string s) { return new MyReal(s);}
public static implicit 开发者_运维知识库operator String(MyReal r) { return r.ToString();}
...
}
OK, I use the MyTable as a binding source for a DataGridView. I can load the data grid easily using InitFromString on individual fields in MyStruct.
The problem comes when I try to edit a value in a cell of the DataGridView. Going to the first row, first column, I change the value of the existing number. It gives an exception blizzard, the first line of which says:
System.FormatException: Invalid cast from 'System.String' to 'MyReal'
I've looked at the casting discussions and reference books but don't see any obvious problems.
Any ideas?
I tried your method of handling CellParsing, and it worked. Although I did it a little differently, handling any types:
private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
e.Value = Activator.CreateInstance(e.DesiredType, new object[] { e.Value });
e.ParsingApplied = true;
}
The DataGridView uses a TypeConverter.
You must define a TypeConverter object for your structure and decorate it with the TypeConverterAttribute, so that the DataGridView uses it as soon as it encounters a property that is typed with your structure.
The example I give here of such a TypeConverter is a bit rudimentary, but the Degree structure I use is similar to yours, it will give you a good idea of what to do.
[TypeConverter(typeof(DegreeConverter))]
public struct Degree {
double Value;
public Degree(double v) { Value = v; }
public static implicit operator double(Degree v) => v.Value;
public static implicit operator Degree(double v) => new Degree(v);
public override string ToString() => Value.ToString();
}
class DegreeConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
return destinationType == typeof(string) ? true : base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
return new Degree(Convert.ToDouble(value));
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
return value.ToString();
}
}
I (almost) fixed this problem by handling the CellParsing event, e.g.
private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
... // (checking goes here)
e.Value = new MyReal(e.Value.ToString());
e.ParsingApplied = true;
}
e.Value is being set correctly but the DataGridView still shows the old value. How does the new value get placed in the BindingList?
Do I need an explicit method call to force the new value into the BindingList and, if so, where?
My gridcells are filled with objects of type GridValueGroup
and they have an ObjValue
property displayed by the ToString
override. When modifying the string in the grid cell, the CellParsing
event is handled by:
- filling the cell-value and
- modifying the
e.Value
(which is of typestring
initially while theDesiredType
isGridValueGroup
) to become the desired type.
This way I avoid creating a new object because the cell already has that object.
Also it keeps the entered value in the data source. Not certain yet if it blocks other events (CellValueChanged
has to be handled sometimes after the parsing of the value).
private void grid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) {
string val = e.Value.ToString();
DataGridViewCell cell = this.dgvGroup1[e.ColumnIndex, e.RowIndex];
if (e.DesiredType == typeof(GridValueGroup))
{
((GridValueGroup)cell.Value).ObjValue = val;
e.Value = ((GridValueGroup)cell.Value);
}
e.ParsingApplied = true;
}
精彩评论