PropertyGrid, SelectedObjects and the property cloning syndrome
Time permitting, I sometimes haunt the newsgroups and developer forums to help when the PropertyGrid is involved. Last week, a poster noticed a very subtle issue concerning .Net 1.1 and .Net 2.0 and I decided to know more about it. It appears that the MS PropertyGrid behaves differently in these two versions of .Net. Here is an excerpt of the original request for help:
... When there are multiple objects selected, and an object property value is changed in the grid, it is assigning the same object instance to each of the selected objects in .Net 1.0, but in .Net 2.0 it is assigning different instances...
The poster also noticed that the culprit was the method SetValue in the MergePropertyDescriptor class. In .Net 1.x it was simply calling PropertyDescriptor.SetValue() with the same new instance for the target properties but in .Net 2.0 it tries first to make a copy (except if we have a value type) by trying different techniques in a method called CopyValue:
- Clones the value if its type implements ICloneable.
- Creates a new copy of the value by calling the constructor of its type if the attached TypeConverter can convert to InstanceDescriptor.
- Creates a new copy of the value by first converting it to a string and then back to an instance of the value type.
- Creates a new copy of the value by serializing it into memory and deserializing it to an instance of the value type if, of course, the type implements ISerializable.
If all tries fail, then the original value is returned.
My thoughts about this issue is that the behavior should be chosen by the developer. In some cases we will want to assign to the target properties distinct instances of the same value and in other cases it makes more sense to assign the same value as if it was a kind of singleton. I tend to prefer what .Net 2.0 does but hey, having the choice is better. I will check if I can offer this choice in SPG, which so far behaves like .Net 1.x.
With the MS PropertyGrid however, I first thought that there was no solution since CopyValue can't be bypassed. But there is one. Implementing this solution for every property would be tedious but for just some of them, when you want to avoid a multiplicity of instances for cost reasons, this can be useful. Attach a TypeConverter to the type of your property (this won't work if it is attached to the property itself) and override the ConvertTo method so that it will convert to an InstanceDescriptor class. In the class corresponding to your property type, add a static method that will be responsible for supplying a unique instance per possible string representation (it could be done elsewhere like in the TypeConverter itself). In the converter, the InstanceDescriptor will point to this class method.
An example is better than a long explanation so here is a short sample.
Note: this solution won't solve the problem of the original poster unfortunately because for him this happens at designtime and the TypeConverter, quite complex, can't be modified. However, at runtime, it can help in a variety of cases.