The AGT (Argentum Tela) series of articles is an effort to do two things. Usually an idea is presented only in its finished form. The first goal is to do some Reality Blogging, to show an idea evolve over time without pulling any punches. The second goal, and the example vehicle for the evolution aspect, is an extensible Design Surface for Silverlight similar to what we have in Visual Studio 2008. This type of application has all sorts of interesting uses. My example is a Home Theater layout tool. Read the entire saga: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx
Choosing an Approach to Editing Properties
One of the last top level pieces to begin fleshing out is Property Editing. I don’t want to see “Red Couch” in the toolbox anymore: I want to see “Couch” and to change its color at runtime by editing his properties. Having had a lot of experience writing things like this so as to fit neatly within the framework Microsoft gives us, I felt very familiar with the benefits and drawbacks of these approaches. I had to ask myself some questions:
1. Is the PropertyGrid really the best way to go about changing attributes of objects on a design surface?
2. Is a Visual Studio style grid the best route? Maybe a smaller gird that appears right next to a Component (ala Visio) is better? Maybe there should be more interaction with the Component itself?
3. How important is it to support the editing of a Property for many Components at once?
4. How important is Undo? Redo?
5. What is the best contract between IDesignableControl and “the design time environment” for determining which properties are editable and how they should be edited?
When writing custom controls for the Visual Studio designer, it always bothered me that the Contract between my Controls and the Designer Time Environment was simply some attributes on public properties. Now that I’m in a position to change it for my own world, I’m having trouble thinking of something better. My ideas on what is the most “correct” in terms of Object- and Component- oriented semantics is not the only thing at play here. As a component creator, what do I want my software development experience to like? Is placing eight attributes on a property the easiest and most sensible way? If I diverge from what’s present in the Visual Studio design time environment, how much extra work am I creating?
What design-time support is present in Silverlight anyway?
Designer Support native to Silverlight
What namespaces, classes, interfaces, and attributes we are accustomed to from “full framework” design time support have made it into Silverlight?
This namespace only contains things related to a Silverlight plug-in control on ASPX pages. There’s nothing useful for us here.
Many of the familiar friends are here:
· CategoryAttribute – Place this on a property indicating what category the property belongs to in a categorized PropertyGrid.
· DefaultValueAttribute – specify the default value for a property
· DescriptionAttribute- a Description for a property or event
· DesignerProperties class. This one is interesting. At the time I started this article, RTW was not quite out and the documentation on MSDN didn’t really say anything about what this actually did. The description now says “The DesignerProperties class provides attached properties that can be used to query the state of a control when it is running in a designer. Designer tools will set values for properties on objects that are running in the designer.” Ultimately, though, this is an attached property for “IsInDesignMode”, which I don’t know if I’ll need or not.
· EditorBrowsableAttribute – In what situations should this property be shown by designers or intellisense
· TypeConverter – TypeConverter is missing some things from the full framework. The MSDN documentation still says it supports “standard values” but those methods (to return a standard values collection) are missing in Silverlight.
· TypeConverterAttribute –present and accounted for!
Missing in action:
· System.ComponentModel.TypeDescriptor – In the full framework, TypeDescriptor is extremely important to the design-time environment. TypeDescriptor is used to augment Reflection to specify what attributes a Component has.
o TypeDescriptor tells us what kind of Editor is used to modify an instance of a Component
o TypeDescriptor tells us what kind of Designer a particular component uses
o TypeDescriptor is used to create design-time instances of a type
· TypeDescriptor is extensible via IExtenderProvider, ITypeDescriptorFilterService, ICustomTypeDescriptor, all missing.
In some cases I’ve already made design decisions that negate the need for some of these missing things. Other’s I’ll have to introduce or replace.
Property editing is complex. Now that I see the lay of the land, I can state some design goals and some approaches to achieving them.
1. The items on the design surface (IDesignableControl) need to be able to be inspected for a list of Properties that are displayable and editable. It should be painless for Control authors to do this.
2. When a property is found to be displayable, we must be able to determine what type of visual Control should be used to display the property value.
3. When a property is found to be editable, we must be able to determine:
a. The type of the property
b. The category of the property
c. What type of visual Control should be used to edit the property value
4. We should be able to display all of this in a PropertyGrid-esque fashion.
In order to reach these goals, I decide on the following:
1. I’m going to try replacing the attribute based Component-Designer contract mechanism with new and modified interfaces.
2. I’m going to make the Component inspection mechanism pluggable, in case someone really wants to fall back on Reflection.
3. I’m going to come up with some better named interfaces and types to use to modify property values.
4. I’m going to build some helpers/extension methods to make this painless for IDesignableControl authors.
5. I’m going to build a PropertyGrid from scratch.
Working on these ideas, I have come up with this:
Since no code was written as part of this article, the most updated code is still AGT.
In the next article we’ll look into implementing this approach.