Wednesday, January 19, 2005
« CAPTCHA Update | Main | Application Over-Engineering »

One of the many things of late that has kept me from being as active on my blog as I would like to be is the looming pressure of a deadline.  We're building momentum towards releasing our application and there are quite a few loose ends that need addressing.  One of these is just about knocked out of the park - and it's one of the more difficult utility applications.

Prior to starting to work on it, I had never developed and IDE and as such, have had to delve into many obscure areas of .NET and application design that most developers shun and usually don't go.

Our application that we're writing is web-based.  It utilizes templated html in the form of controls (.ascx as well as server controls).  These User Controls (the .ascx variety) are designed to be edited and tweaked by end users (non-programmers/developers) and without the assistance of a tool such as Visual Studio.  We have, therefore, decided to create an IDE tailored towards targetting these users.

Without going into the details of the design of the application, we concluded that the best environment for the non-technical users to work would be a tool of our own creation that masked much of the complexity of .ascx controls (that is, directive tags and control tags) while providing full control fidelity and power.  A control within the .ascx might resemble <asp:literal id=litHeaderCaption ... /> but that would be too ugly (and perhaps intimidating) to display to an end user.  Instead we wanted to display [HEADERCAPTION] that acted as a cohesive unit within the editor.  That is, when a user positioned the insertion point within the tag (via keyboard or mouse), the entire tag would become selected and a set of properties would appear allowing it to be customized similar to how Visual Studio might work.  These controls would also be draggable from a toolbox (a la Visual Studio) onto the editor.

This discussion isn't focused on the richtext color syntax highlighting, the parsing of text to yield the [...] tags, nor the dragging of tools, though each of those is cool and might warrant a complete discussion (let me know if you're interested).  Instead, herein I'm going to talk about another aspect of the application that I thought was pretty incredible.

When dealing with a text string that contains a tag [HEADERCAPTION] how do you assign values to properties for an object that doesn't exist?  When the user selects the 'control' I need to render in a PropertyGrid control the public properties and their values.  Well initially, the control instance doesn't exist at all, that is, nothing is really hosting a control instance - you simply have a string.  Therefore, I had to, upon parsing out the controls, resolve which tags would be represented in the text, parse out the properties present in the <asp:... /> tag and somehow get these values into an instance of the control.  Well, this is a completely separate application from the actual product that performs the rendering of the controls in the web-world.  I need to define a class for each child constituent of my parent user control.

I decided pretty early on that I did not want to create a suite of classes representing each and every control type.  Sure this would have been a snap, and the object hierarchy is extremely straightforward, but I didn't want to have to deal with the maintenance.  After quite a bit of thought on this I decided it would not be the right approach.  If so, the code would have resulted in being a lot less maintainable and much more difficult to work with.  Instead, I decided to have every control type dynamically defined.

The first step in this was that to create a set of properties and their definitions so I could reuse them later in other class instances.  I decided to use the GoF's flyweight pattern so that I would have one set of base objects that were reusable with the mutable data in each property instance.  My code resembled the following:

[Flags()]
internal enum PropertyMode : int {
   Read       = 0x0001,
   Write      = 0x0002,
   ReadWrite  = 0x0003,
}

internal interface IToolPropertyDefn {
   Type            DataType { get; set; }
   PropertyMode    Mode { get; set; }
   string          Name { get; set; }
   string          PrivateName { get; set; }
   string          Description { get; set; }
   object          DefaultValue { get; set; }
   bool            IsMetaProperty { get; set; }
}

Then I had another class called ToolPropertyDefns that wrapped a Hashtable containing instances of ToolPropertyDefnImpl which represents instances of each of the properties in question.  The ToolPropertyDefns class can be indexed into by control type to retrieve the set of properties that pertains to the control type in question.  A snippet resembles:

internal sealed class ToolPropertyDefns {
   private static Hashtable _defns = new Hashtable();

   private ToolPropertyDefns() { }

   static ToolPropertyDefns() {
      // initialize the controls here...
      ToolPropertyDefnImpl[] defn = new ToolPropertyDefnImpl[] {
         new ToolPropertyDefnImpl(typeof(string), PropertyMode.ReadWrite, “Visible“, “_visible“,
            “Determines whether the control is visible or hidden.“, true, false);
      }
      _defns.Add(Tools.HeaderCaption, defn);

   }

   internal static IToolPropertyDefn[] GetDefn(Tools id) {
      return _defns[id] as IToolPropertyDefn[];
   }

   private sealed class ToolPropertyDefnImpl : IToolPropertyDefn {
      // define implementation here
   }
}

Once I had this class that managed the properties associated with each control type it was time to dynamically generate instances of the controls at runtime...which will be the topc for Part II of the discussion.

Wednesday, January 19, 2005 11:35:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
Comments are closed.