P
Philipp Schumann
Hello,
I'm stuck in a rather difficult situation, and for everyone who has already
dealt with TypeDescriptor and PropertyDescriptor, as well as the
CodeDomSerializer and the DesignerSerializerAttribute, here's a summary of
that situation.
I have a component with a "SupportedTypes" property of type DictionaryBase
(that is, a strongly typed Hashtable). The default behaviour of the
PropertyGrid within a design environment would be for this property to
display the text "(Collection)" together with a tiny "..." button, which
would open a dialog to display the elements within the SupportedTypes
collection. Those elements are components themselves, but the dialog doesn't
provide facilities to change the properties of those components.
Using a custom type editor, derived from UITypeEditor, I got rid of the
nasty dialog including the "..." button.
The design of the aforementioned component dictates that it should be
possible for a developer to change the properties of all the components
contained within the SupportedTypes collection, at design time. At the same
time, however, it shouldn't be possible to change the keys within the
Hashtable, or even to remove elements, add new ones, or assign different
ones to existing keys.
Utilizing TypeDescriptor and a custom PropertyDescriptor I managed to solve
this quite neatly - all components contained within the SupportedTypes
collection are now displayed as child properties in a PropertyGrid, as soon
as the "+" now displayed next to the SupportedTypes property is clicked. The
properties of each of these components are equally displayed properly and
can now be changed when clicking the "+" displayed next to the respective
component (fake "child property" of "SupportedTypes").
So far, everything works beautifully. But, for those still interested, let
me clarify and concretise the situation - if possible, please try to ignore
the greater context and to digest the facts without some background
information otherwise I'd have to write a book about it.
The class "SimpleTypeHandler" (derived from Component) provides a property
"SupportedTypes". This collection is of type "TypeHandlerSettingsCollection"
(which derives from DictionaryBase and implements IComponent) and contains
components of type "TypeHandlerSettings". The only way to access those
components is through one of the two indexers provided by the
TypeHandlerSettingsCollection - the first one is a numeric indexer and the
second one accepts a System.Type instance, as each TypeHandlerSettings
component stores settings for "handling" a particular Type. In other words,
within a SimpleTypeHandler.SupportedTypes collection, each
TypeHandlerSettings component is uniquely associated with one particular
Type.
Utilizing TypeDescriptor / a custom PropertyDescriptor, it was now possible
to display each TypeHandlerSettings component as a child property of the
SupportedTypes property within a PropertyGrid, just as would be the case if
the TypeHandlerSettingsCollection would provide single properties such as
"Boolean", "String" or "Int32", each of type TypeHandlerSettings. This,
however, is not the case, and this way of visually exposing the components
within the SupportedTypes collection exists to provide convenient access to
the settings contained within these components at design time. Otherwise,
one would have to write tedious initialization code to be executed at
runtime, such as "simpleTypeHandler1.SupportedTypes [typeof
(System.Boolean)].SomeSetting = SomeValue".
To make sure that the Designer does persist the changes made on a
PropertyGrid accurately, I have written a
TypeHandlerSettingsCollectionSerializer class, which derives from
CodeDomSerializer and is connected to the TypeHandlerSettingsCollection
class via the DesignerSerializerAttribute.
My custom serializer now eventually managed to successfully and accurately
include the following lines of code into the InitializeComponent method of
the container (which is a plain, empty test Form), after having changed a
property of a TypeHandlerSettings component on the PropertyGrid:
//
// simpleTypeHandler
//
this.simpleTypeHandler.Name = "simpleTypeHandler";
this.simpleTypeHandler.SupportedTypes[typeof (string)].BoolTest = true;
this.simpleTypeHandler.SupportedTypes[typeof (char)].IntTest = 20;
The code is syntactically and semantically (!) correct, it compiles and, at
runtime, executes successfully -- as is the case if the
TypeHandlerSettingsCollectionSerializer uses the other indexer:
//
// simpleTypeHandler
//
this.simpleTypeHandler.Name = "simpleTypeHandler";
this.simpleTypeHandler.SupportedTypes[10].BoolTest = true;
this.simpleTypeHandler.SupportedTypes[7].IntTest = 20;
Now, here's the PROBLEM.
Despite the syntactical and semantical correctness of the code produced by
the TypeHandlerSettingsCollectionSerializer.Serialize method, and although
the code compiles successfully and executes successfully at runtime, the
Form designer fails to execute the code successfully when re-loading the
container (the test Form). Of course, its InitializeComponent method
contains the proper initialization of the component:
this.simpleTypeHandler = new mokka.Persient.SimpleTypeHandler();
Still, when attempting to display the Form designer (thereby, one would
expect, running at least the InitializeComponent method), the following
error keeps coming up:
(Translated from the German Visual Studio .NET 2003 - this may not be the
exact wording in English editions so googling for it might not help. No
error number is provided by the Designer.)
<error>
The Designer cannot execute code in line 68:
this.simpleTypeHandler.SupportedTypes[7].BoolTest = true;
The code in method 'InitializeComponent' is created by the Designer and must
not be changed manually. Remove all changes and re-open the Designer.
</error>
The same happens when using the System.Type indexer instead of the numerical
one.
Intuitively, one would expect that when initializing the container, an
instance of the TypeHandlerSettingsCollectionSerializer class would be
created and its Deserialize method would be called, just as such an instance
gets created and its Serialize method is called when saving changes to
properties. This, however, is not the case - a new
TypeHandlerSettingsCollectionSerializer instance is not even created.
I might actually be able to manually parse the code generated for the
InitializeComponent method, probably using CodeDOM - if only I would be
given the opportunity by the Designer! Now I'm wondering whether anyone has
a clue as to how to get hold of such an opportunity. What's the point of the
Deserialize method if it doesn't get called at all? Does the Designer
actually "pre-check" the InitializeComponent code before deserializing it?
(Apparently it isn't simply compiled/executed - strange enough.)
I'd be very grateful for the tiniest hints,
and thank you very much for taking the time to read this,
Best regards,
Philipp Schumann
I'm stuck in a rather difficult situation, and for everyone who has already
dealt with TypeDescriptor and PropertyDescriptor, as well as the
CodeDomSerializer and the DesignerSerializerAttribute, here's a summary of
that situation.
I have a component with a "SupportedTypes" property of type DictionaryBase
(that is, a strongly typed Hashtable). The default behaviour of the
PropertyGrid within a design environment would be for this property to
display the text "(Collection)" together with a tiny "..." button, which
would open a dialog to display the elements within the SupportedTypes
collection. Those elements are components themselves, but the dialog doesn't
provide facilities to change the properties of those components.
Using a custom type editor, derived from UITypeEditor, I got rid of the
nasty dialog including the "..." button.
The design of the aforementioned component dictates that it should be
possible for a developer to change the properties of all the components
contained within the SupportedTypes collection, at design time. At the same
time, however, it shouldn't be possible to change the keys within the
Hashtable, or even to remove elements, add new ones, or assign different
ones to existing keys.
Utilizing TypeDescriptor and a custom PropertyDescriptor I managed to solve
this quite neatly - all components contained within the SupportedTypes
collection are now displayed as child properties in a PropertyGrid, as soon
as the "+" now displayed next to the SupportedTypes property is clicked. The
properties of each of these components are equally displayed properly and
can now be changed when clicking the "+" displayed next to the respective
component (fake "child property" of "SupportedTypes").
So far, everything works beautifully. But, for those still interested, let
me clarify and concretise the situation - if possible, please try to ignore
the greater context and to digest the facts without some background
information otherwise I'd have to write a book about it.
The class "SimpleTypeHandler" (derived from Component) provides a property
"SupportedTypes". This collection is of type "TypeHandlerSettingsCollection"
(which derives from DictionaryBase and implements IComponent) and contains
components of type "TypeHandlerSettings". The only way to access those
components is through one of the two indexers provided by the
TypeHandlerSettingsCollection - the first one is a numeric indexer and the
second one accepts a System.Type instance, as each TypeHandlerSettings
component stores settings for "handling" a particular Type. In other words,
within a SimpleTypeHandler.SupportedTypes collection, each
TypeHandlerSettings component is uniquely associated with one particular
Type.
Utilizing TypeDescriptor / a custom PropertyDescriptor, it was now possible
to display each TypeHandlerSettings component as a child property of the
SupportedTypes property within a PropertyGrid, just as would be the case if
the TypeHandlerSettingsCollection would provide single properties such as
"Boolean", "String" or "Int32", each of type TypeHandlerSettings. This,
however, is not the case, and this way of visually exposing the components
within the SupportedTypes collection exists to provide convenient access to
the settings contained within these components at design time. Otherwise,
one would have to write tedious initialization code to be executed at
runtime, such as "simpleTypeHandler1.SupportedTypes [typeof
(System.Boolean)].SomeSetting = SomeValue".
To make sure that the Designer does persist the changes made on a
PropertyGrid accurately, I have written a
TypeHandlerSettingsCollectionSerializer class, which derives from
CodeDomSerializer and is connected to the TypeHandlerSettingsCollection
class via the DesignerSerializerAttribute.
My custom serializer now eventually managed to successfully and accurately
include the following lines of code into the InitializeComponent method of
the container (which is a plain, empty test Form), after having changed a
property of a TypeHandlerSettings component on the PropertyGrid:
//
// simpleTypeHandler
//
this.simpleTypeHandler.Name = "simpleTypeHandler";
this.simpleTypeHandler.SupportedTypes[typeof (string)].BoolTest = true;
this.simpleTypeHandler.SupportedTypes[typeof (char)].IntTest = 20;
The code is syntactically and semantically (!) correct, it compiles and, at
runtime, executes successfully -- as is the case if the
TypeHandlerSettingsCollectionSerializer uses the other indexer:
//
// simpleTypeHandler
//
this.simpleTypeHandler.Name = "simpleTypeHandler";
this.simpleTypeHandler.SupportedTypes[10].BoolTest = true;
this.simpleTypeHandler.SupportedTypes[7].IntTest = 20;
Now, here's the PROBLEM.
Despite the syntactical and semantical correctness of the code produced by
the TypeHandlerSettingsCollectionSerializer.Serialize method, and although
the code compiles successfully and executes successfully at runtime, the
Form designer fails to execute the code successfully when re-loading the
container (the test Form). Of course, its InitializeComponent method
contains the proper initialization of the component:
this.simpleTypeHandler = new mokka.Persient.SimpleTypeHandler();
Still, when attempting to display the Form designer (thereby, one would
expect, running at least the InitializeComponent method), the following
error keeps coming up:
(Translated from the German Visual Studio .NET 2003 - this may not be the
exact wording in English editions so googling for it might not help. No
error number is provided by the Designer.)
<error>
The Designer cannot execute code in line 68:
this.simpleTypeHandler.SupportedTypes[7].BoolTest = true;
The code in method 'InitializeComponent' is created by the Designer and must
not be changed manually. Remove all changes and re-open the Designer.
</error>
The same happens when using the System.Type indexer instead of the numerical
one.
Intuitively, one would expect that when initializing the container, an
instance of the TypeHandlerSettingsCollectionSerializer class would be
created and its Deserialize method would be called, just as such an instance
gets created and its Serialize method is called when saving changes to
properties. This, however, is not the case - a new
TypeHandlerSettingsCollectionSerializer instance is not even created.
I might actually be able to manually parse the code generated for the
InitializeComponent method, probably using CodeDOM - if only I would be
given the opportunity by the Designer! Now I'm wondering whether anyone has
a clue as to how to get hold of such an opportunity. What's the point of the
Deserialize method if it doesn't get called at all? Does the Designer
actually "pre-check" the InitializeComponent code before deserializing it?
(Apparently it isn't simply compiled/executed - strange enough.)
I'd be very grateful for the tiniest hints,
and thank you very much for taking the time to read this,
Best regards,
Philipp Schumann