I'm in the middle of creating an ASP.NET control that has a sub-object that is a complex object. I've been redesigning some existing controls that I've been using for some time to use Extender style controls and adding the extenders to custom controls. In order to add an extender object you have to be able to have a complex object property on a control class.

 

For things like this designer integration is crucial and I'm always amazed how easy it is to do this sort of thing with ASP.NET. What I'm looking for is to show the control as an object that is expandable. The end result is something like this (in this case attached to a custom TextBox control):

 

Easily done with simple code like this:

public class WebTextBox : TextBox, IwwDataBinder

{

    [Browsable(true)]

    [NotifyParentProperty(true),

     DesignerSerializationVisibility(DesignerSerializationVisibility.Content),

     PersistenceMode(PersistenceMode.InnerProperty)]

    public wwDataBindingItem BindingItem

    {

        get { return _BindingItem; }

    }

    private wwDataBindingItem _BindingItem = new wwDataBindingItem();

 

 

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

 

        this.BindingItem.ControlId = this.ID;

        this.BindingItem.ControlInstance = this;

    }

 

}

 

This results in a Textbox control that has a nested control inside of it that looks like this:

 

<ww:WebTextBox ID="txtNewValue" runat="server">

<BindingItem BindingProperty="Text"

     IsRequired="False"

                 BindingSource="Customer.Entity"

                 ControlId="txtEntered"

                 UserFieldName=""

                 BindingSourceMember="Entered"

                 ErrorMessageLocation="WarningIconRightFromResource"

                 DisplayFormat="{0:d}" >

    </BindingItem>

</ww:WebTextBox>

 

It works, but not quite what I had in mind.

 

Designer Serialization Mode

The designer takes the values of the control and automatically serializes them based on the designer serialization mode:

 

DesignerSerializationVisibility(DesignerSerializationVisibility.Content)

 

which serializes using the element style you see above. As it turns out that works fine, but it's a little verbose and it breaks any control that might have the ability to have literal content in the control content. For example, the TextBox above loses its ability to set the text property through the content when you do this sort of content assignment for sub-properties.

 

There's another approach that is also more commonly used in the stock ASP.NET controls which uses:

 

DesignerSerializationVisibility(DesignerSerializationVisibility.Attribute)

 

This turns the control interface into something looking like this:

 

<ww:WebTextBox ID="txtNewValue" runat="server" Width="305px"

               BindingItem-BindingSource="Customer.Entity"

               BindingItem-BindingSourceMember="Entered"

               BindingItem-ControlId="txtEntered"

               BindingItem-DisplayFormat="{0:d}">

</ww:WebTextBox>

 

This format basically serializes into attributes that use property name as a prefix. This is consistent with things you see in ASP.NET controls like Font-Size, ItemStyle-ForeColor etc. which are doing the same sort of thing.

 

This format is more compact and it leaves the ability to have literal content inside of the control intact. It works great.

 

A few snags

I ran into a few snags at first with this in regards to properties that are defined on the DataBindingItem control definition that shouldn't be serialized in the designer.

 

Now this works just fine, EXCEPT that I have a property on the child control that is a ControlInstance reference that is set once a string ID is assigned. The ControlInstance is available for quicker access to the underlying control when needed.

 

This property is a reference to the control that is bound, which understandably can't be serialized by the ASP.NET designer and caused the designer to show that nice indescript gray box for my control. After looking in all the wrong places for an hour (Browsable, Serialization and Xml Serialization flags) I finally realized that the designer has its own set of serialization flags (Duh!).  The key is the DesingerSerializationVisibility attribute that can be set to keep controls from serializing:

 

[NotifyParentProperty(false)]

[Description("An instance value for the controls")]

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

[Browsable(false)]       

public Control ControlInstance

{

    get

    {

        return _ControlInstance;

    }

    set

    {

        _ControlInstance = value;

    }

}

private Control _ControlInstance = null;

 

In addition you should make sure to set Browsable to false so that property doesn't show up in the designer in the first place (although that alone will not keep the property from trying to serialize).

 

Browsing Control IDs

The control also has a ControlId property – you can select the Control to bind to by its control ID. One trick here is that if you ever need to select a ControlId you can use a special TypeConverter that allows you to convert a control instance to a Control ID and lets the designer display a control selection drop down with all the controls on your form. To do this you can declare the ControlId property like this:

 

[NotifyParentProperty(true)]

[Description("The ID of the control to that is bound."),DefaultValue("")]

[TypeConverter(typeof(ControlIDConverter))]

[Browsable(true)]

public string ControlId

{

    get

    {

        return _ControlId;

    }

    set

    {

        _ControlId = value;

    }

}

private string _ControlId = "";

 

Once you do this the designer shows a drop down with all the controls on the page.

 

For this particular control which is an extender, it's crucial that you can select the control unless you have photographic memory and can remember every control name. I actually was working with the control for a while without the type converter and I was constantly finding myself in the modal collection editor cursing that I couldn't remember the name of a control and couldn't just switch back to the designer to figure out the control name. With the TypeConverter I can at least jog my memory even if it means looking through a long list of controls.

 

 

The designer integration really is nice. This is nothing new to commercial control developers but if you do this stuff once in a while it's easy to forget all the cool stuff that's available. One big beef I have with this is that it's very difficult to discover the huge number of designer attributes that are available and the documentation on most of them is pretty sparse. It'd be really nice if there was one spot in the documentation that covers all or most of the designer attributes in some format that can be followed without going in complete circles on MSDN <s>.