|
HOME, PRODUCTS, ARTICLESCONTACTGLOSS CONTOUR SUITE™ FOR .NET |
![]() OBJECTS AS .NET PROPERTIES |
|
DOWNLOAD OR BUY Gloss Contour Suite™GCSuite™ FEEDBACKGCSuite™ SUPPORTC# OR .NET TECHNICAL ARTICLE FEEDBACK |
|
GlossContourButton™. |
|
VITAL TECHNIQUES FOR USING OBJECTS AS .NET PROPERTIES |
|
Our experiences with Visual Studio 2005™ demonstrate that function calls are not fired in outer set accessors of properties subject to TypeConverters. This article first revisits how to deploy TypeConverters so that your class properties will be displayed from a nested node in Properties View. It then demonstrates the necessary pattern for declaring DefaultValueAttributes, and for writing set accessors that will successfully fire vital accessor functions. |
|
BACKGROUND |
|
A rudimentary and very common chore of writing class libraries is defining properties comprised of custom classes or structs. As with Font, Size, Location, or Anchors, a developer will practically always want to adhere to conventions which display the class/struct property in an expandable property node at design time. This is a job we should be able to get done in just a few minutes. Product documentation and nomenclature however make it difficult to find reference material on nested node behavior, and fail altogether to explain what constraints are imposed on DefaultValueAttributes or what hurdles obstruct functions we expect to perform in property accessors. You can spend days on these regular design issues instead of minutes without the simple instructions which follow. |
|
EXAMPLE BACKGROUND |
Colors.Offset property of this example, showing the nested node behavior we are trying to accomplish. |
|
In this explanation, our outer GlossContourButton™ class needs to expose a Colors.Offset property of the class GCColorOffset. Colors.Offset provides R, G, and B offset values which allow developers to tint or offset the luminosity of base colors. Thus if the R, G, or B subproperties of Colors.Offset are changed, we need to fire respective drawing methods of GlossContourButton™ from property set accessors. Let's first get GlossContourButton™ to display Colors.Offset as an expandable node in Property View so that we can address the non-firing accessor function and DefaultValueAttributes issues. |
|
"TypeConverters" PROVIDE NESTED NODE BEHAVIOR |
|
Thanks to very unhelpful documentation, many developer's nested node behavior efforts might remain dead in the water until they discover material such as a Code Project article, "Creating Custom Controls-Providing Design Time Support 1" by Kodanda Pani. Pani explains that we have to build a TypeConverter for the class of our property, overriding GetPropertiesSupported( ) to return true; and overriding GetProperties( ) to pass the class of our property to the base implementation. The latter reflects our subproperties to the nested node. This is all there is to the necessary TypeConverter: |
|
C# EXAMPLE TypeConverter FOR A GCColorOffset CLASS |
|
Browsable and Serializable are required on the property class definition; and our TypeConverter is associated with this GCColorOffset class by a TypeConverterAttribute: C# EXAMPLE — TypeConverterAttribute ASSOCIATES GCColorOffsetTypeConverter WITH GCColorOffset |
|
Now, whenever we declare a GCColorOffset property in an outer class, the IDE will provide our GCColorOffset subproperties in a nested node of the outer class. |
|
IN THE OUTER CLASS, DECLARE THE PROPERTY *WITHOUT A DefaultValueAttribute* |
|
To our great displeasure however, and at incredible wastes of work, we found that acceptable IDE behavior was impossible if we declared DefaultValueAttributes on property declarations in outer classes. DefaultValueAttributes would compile, and they might even work once, but thereafter reset behavior would crash; property accessors would fail to read or write, and so forth. Exhaustive experimentation found that DefaultValueAttributes could only be declared directly on the bottom/inner-most property class members (GCColorOffset.BaseProperty). Almost certainly however, good reasons will compel you to declare DefaultValueAttributes on the outer class property, because it is the outer class to which the default is associated, and because usual conditions require a given DefaultValue in one outer class while yet other outer classes require different DefaultValues. As these are standard patterns which class designs must contend with, forcing us to declare DefaultValueAttributes on the inner property class denies us the DefaultValue flexibility that class library designs require. |
|
COSTS OF DISCOVERING YOU CANNOT DECLARE DefaultValueAttributes IN THE OUTER CLASS |
|
It is practically impossible therefore that class design efforts will never encounter this issue. Because composite classes are inevitable design paths, this flaw or weakness of the IDE will cost all class engineers dearly, as we first struggle for great whiles to make DefaultValueAttributes work in outer classes, only to find after exhaustive wasted efforts that the IDE blows itself out of the water if we do so. That this weakness is not documented — even by a recommended pattern for declaring DefaultValueAttributes — only compounds our injuries, because we can only solve this obstructive behavior by conceding to a pattern which is adverse to inevitable purposes of class design. Needing to associate DefaultValues with the outer class, every temptation to place them in inner classes will be strenuously resisted. Why? Because you will want to avoid an obvious further problem (and bad design) this imposes: Your further class design efforts will suffer the further cost of having to subclass inner property classes merely to declare different defaults. |
|
COSTS OF DISCOVERING THAT VITAL FUNCTIONS ARE NEVER FIRED IN OUTER CLASS ACCESSORS |
|
In the very next moments of this inevitable process however, we discover a further amazing thing — that vital function calls in outer class property accessors are not fired by the IDE. Indeed, design time execution of set accessors simply jumps across vital instructions as if they are meant to be ignored. Ignored calls are preserved below: |
|
C# EXAMPLE OUTER CLASS PROPERTY DEFINITION |
|
In other words, at design time, outer class accessors of properties subject to TypeConverters can perform no validation, no function calls... you get the picture. Thus, you will have to perform vital accessor functions too from the same improbable and unreasonable place — the inner property class. *Further* exhaustive (wasted) experimentation found that outer class design time handling supports no more than the following (most simple possible) accessor form: |
|
C# EXAMPLE OUTER CLASS PROPERTY DEFINITION PATTERN |
|
Given these costly issues, a basic (albeit otherwise undesirable and illogical) pattern paves the way for your composite class design to succeed. |
|
PATTERN FOR DECLARING DEFAULTATTRIBUTES |
|
Firstly, even if this obstructs good and usual design intentions, DefaultValueAttributes can only be declared on the properties of the inner property class: |
|
C# EXAMPLE |
|
REVISING INNER CLASSES TO FIRE OBLIGATORY OUTER CLASS FUNCTIONS |
|
Secondly, we must revise both property class and outer class designs so that vital accessor functions of the outer class are fired from the property class. This purpose is reasonably accomplished with delegates or references. Because this example requires the reference for other purposes which are not evident from this example, and because we do not have to expose this property class elsewhere, we first show how to use a reference to the outer class. To preempt potential calls to a null reference, we assign the reference as early as possible with a specialized constructor for the property class: |
|
C# EXAMPLE |
|
When the outer class creates an instance of the property class, of course it passes this to the specialized property class constructor: |
|
C# EXAMPLE |
|
Having called such a constructor so, accessors of our property class can make the calls into the outer class functions which the IDE ignored when we made them where they belong. We now call them (absurdly) from the outer class's set accessor: |
|
C# EXAMPLE |
|
Alternatively, we could assign a delegated handler in such a constructor, calling the delegate method from our accessor: |
|
C# EXAMPLE |
Finished nested node behavior shows the resultant non-support for Reset in the outer property declaration. Owing to the required DefaultValueAttribute declarations, only the inner class property declarations (R, G, and B) provide Reset support. |
|
This reference or delegate pattern solves our problems and demonstrates that there's just a bit more to every little composite property than might meet the eye. Of course, when/if the IDE is ultimately repaired, successful designs will be achieved as easily as they should have been from the beginning — eliminating the temporary need for these workarounds, and requiring that you revise your class designs (again) to your original intention. |
|
Gloss Contour Suite™ for .NET DOCUMENTATION Introductory material and complete GCS technical documentation. HOW TO — ARTICLES AND EXAMPLE SOURCE GlossContour Suite™ INSTALLATION HOW TO IMPLEMENT EFFICIENT TOGGLE GROUP BEHAVIOR Writing efficient, type safe code to implement toggle group behavior. WALKTHROUGH 1 — DESIGNING GlossContourSurfaces™, DEPLOYING AS ContourServers™ Basic configuration of GlossContourSurfaces™ in preparation for Walkthrough 2. WALKTHROUGH 2 — DESIGNING GlossContourButtons™, DEPLOYING AS ContourServers™ OR ContourClients™ How to configure color, color offsets, gloss, glare, and 3D effects. ContourServer™/ContourClient™ implementation. NET TECHNICAL VITAL TECHNIQUES FOR USING OBJECTS AS .NET PROPERTIES Our experience with Visual Studio 2005™ demonstrates that function calls are not fired in outer set accessors of properties subject to TypeConverters. This article first revisits how to deploy TypeConverters so that your class properties will be displayed from a nested node in Properties View. It then demonstrates the necessary pattern for declaring DefaultValueAttributes, and for writing set accessors that will successfully fire vital accessor functions. |
|
|
|