See the bottom of this post if you just want to see a quick solution ;-)
What's this about?
The new .net 2.0 datagridview is a great control and implementing custom columns is very easy, but doing so has a tricky setback: inherited fields and properties are not persisted. I'm sure this little oversight is something that will be mended soon enough, but it in the meantime the only solution seems to be to override the clone functionality. Don't know why precisely, but in the background there seems to be a lot of cloning of the columns and cell templates going on.
Now the easiest way would seem to use a memberwiseclone, but that gives other errors. Probably this is also the reason why the data is not persisted: the mentioned datagridview objects implement their own cloning functionality.
Example:
The quickest way to see how the values are not persisted is to create a testcolumn, eg:
public
class ExampleColumn :
DataGridViewTextBoxColumn{
private
int testprop;
[
DefaultValue(0)]
public int TestProperty
{
get { return testprop; }
set { testprop = value; }
} } If you add this code to your project and build that project, the column will automatically show up in the available columns in a datagridview. Add a datagridview to a form, select its properties, select Columns, open the collection editor (click the ... button) and add a column. Choose the examplecolumn type.
Now change the TestProperty value. Close the dialog window, open it again and check the value. It will be back to 0. This will also happen in runtime after eg a datasource is asigned. Your columns will be cloned and private fields will not be cloned.
public
override object Clone()
{
ExampleColumn o = base.Clone() as ExampleColumn;
o.testprop = testprop;
return o;
}
This will persist the property, but is very maintenance sensitive. Each new property/field would have to be added manually.
Resuable SolutionA more generic solution is to have a function set the child fields. Reflection to the rescue again :)
public class CustomDataGridViewColumn : DataGridViewTextBoxColumn
{
//needs a using System.Reflection;
/// <summary>
/// Extends the basic cloned object by doing a member wise cloning
/// on all the child fields
/// </summary>
/// <param name="Original"></param>
/// <param name="BaseClonedObject"></param>
/// <returns></returns>
public static object CloneFields(
object Original,
object BaseClonedObject)
{
Type t = BaseClonedObject.GetType();
foreach (
FieldInfo fi
in t.GetFields(
BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.SetField | BindingFlags.GetField
| BindingFlags.DeclaredOnly
))
{
fi.SetValue(BaseClonedObject, fi.GetValue(Original));
}
return BaseClonedObject; }
// Example call in an Iclonable object
public override object Clone()
{
return CloneFields(this, base.Clone());
}
}
. . .
What I do myself, is have the custom columns (same can be done for the cell templates) inherit from this one. Normally you'll inherit from the DataGridViewTextBoxColumn anyway, even when implementing custom controls, so you don't have to worry about any of this cloning stuff. If that is not an option, the function can still be called straight out, as shown in the example method call.