HOME, PRODUCTS, ARTICLESCONTACTGLOSS CONTOUR SUITE™ FOR .NET

DOWNLOAD OR BUY Gloss Contour Suite™GCSuite™ FEEDBACKGCSuite™ SUPPORTC# OR .NET TECHNICAL ARTICLE FEEDBACK

GlossContourButton™

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

Copy code to clipboard (JavaScript/IE).

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

Copy code to clipboard (JavaScript/IE).

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*

As Pani also shows, we must declare the DesignerSerializationVisibility and Browsable attributes on the property definitions we may now make in outer classes.

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

Copy code to clipboard (JavaScript/IE).

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

Copy code to clipboard (JavaScript/IE).

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

Copy code to clipboard (JavaScript/IE).

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

Copy code to clipboard (JavaScript/IE).

When the outer class creates an instance of the property class, of course it passes this to the specialized property class constructor:

C# EXAMPLE

Copy code to clipboard (JavaScript/IE).

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

Copy code to clipboard (JavaScript/IE).

Alternatively, we could assign a delegated handler in such a constructor, calling the delegate method from our accessor:

C# EXAMPLE

Copy code to clipboard (JavaScript/IE).

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.

RELATED TOPICS

© Copyright 1995-2007, by ADVANCE Information Systems, Inc. ALL RIGHTS RESERVED.Copyright 1995-2007, by ADVANCE Information Systems, Inc. ALL RIGHTS RESERVED.

Firefox™.Best viewed in Mozilla Firefox™.