Some time ago when I talked about my PreserveProperty control, Bertrand turned me on to the idea of using Extender style controls and ever since I've been rethinking some of the design choices I've made with custom control design that essentially extends the behavior of existing controls. Extender controls basically are controls that live on a form as a separate object and 'attach' to one or more existing controls and extends them.

 

For example, the PreservePropertyControl's job is to 'monitor' a property of a control (or the Page) and persist it automatically, then restore it automatically on a Postback without having to explicitly access ViewState.

 

The control is an extender and looks something like this:

 

<ww:PreservePropertyControl ID="Persister" runat="server">

    <PreservedProperties>

         <ww:PreservedProperty ID="PreservedProperty1" runat="server"

                               ControlId="btnSubmit"  

                               Property="ForeColor" />

         <ww:PreservedProperty ID="PreservedProperty2" runat="server"

                              ControlId="__Page"

                              Property="CustomerPk" />

    </PreservedProperties>

</ww:PreservePropertyControl>   

 

The PreserveProperty Control was one example where you specified a number of control/property combinations that you wanted to preserve and the control would then manage the state for this control.

 

Another example - and this is more pressing right now as I'm working on it <s> - is a DataBinder control. I think that ASP.NET's simple databinding (Controls to single Data items – fields, properties etc.) is severely lacking and ever since 1.0 I've had custom controls that handle this binding for me.

 

This original design relied on inheritance, slapping a special interface on a control and implementing the handful of properties and methods required to make it work. The actual properties that deal with binding (BindingSource, BindingSourceMember, ControlId, ControlMember, DisplayFormat) had to be attached to the target control which had to be subclassed to make this work. It wasn't much code that had to be attached, but without subclassing this whole model won't work, so this means you have to subclass any control that you might want to use this with.

 

While this isn't a big deal – I have subclasses of the common classes anyway because I have a fair amount of custom behaviors on my controls anyhow, but it is a problem for a control you might want to give out to others to use. A good example is if you wanted to use a third party control like a DevExpress TextBox control – your only choice would be to subclass and add the appropriate interfaces members. In short, it works, but it's not very adaptable since you can't bind to an arbitrary control. Your only way with this scheme to get the DataBinding behavior is to subclass and implement the interface. It works but it's a bit of work and if the interface changes or requires different logic a big maintenance nightmare.

 

Using an Extender style control is a much cleaner design as it can bind any property/field to any property field on the target control and it can do so independently of the control that you are binding. In fact, the control is a standalone control on the page and looks like this:

 

<ww:wwDataBinder runat='server' ID="Binder">

    <DataBindingItems>

        <ww:wwDataBindingItem ID="wwDataBindingItem1" runat="server" BindingSource="Customer.Entity"

            BindingSourceMember="CompanyName" ControlId="txtCompany">

        </ww:wwDataBindingItem>

        <ww:wwDataBindingItem ID="wwDataBindingItem2" runat="server" BindingSource="Customer.DataRow"

            BindingSourceMember="ContactName" ControlId="txtName">

        </ww:wwDataBindingItem>

        <ww:wwDataBindingItem ID="wwDataBindingItem3" runat="server"

            BindingSource="this"

            BindingSourceMember="SomeValue"

            ControlId="txtSomeValue"

            DisplayFormat="{0:c}" >

        </ww:wwDataBindingItem>

    </DataBindingItems>

</ww:wwDataBinder>

 

You're not limited to subclassed controls per se, and even if you decide to subclass the controls it's a lot easier to do because all the logic is wrapped up in the extender object.

 

From a abstract design perspective this is a much cleaner approach. Basically this control (or rather the wwDataBindingItem class) contains the same properties that the control used to expose. So items are added and associated with a particular control. Now the binding is completely externalized and if I use the control on the page like this (or using code like this.Binder.AddBindingItem()) I can now bind ANY control whether it supports a special interface or not. I can also bind multiple properties on a control – for example on a check box you might bind both the Checked state as well as the text that displays on the label.

 

So far so good. As I said – abstract design is much cleaner.

 

Unfortunately, controls like this are not as usable as say a TextBox object that somehow hosts a 'DataBinding' Item. If I want to add a property to the textbox I'm back to requiring a 'special' interface on the control, but this is still a big improvement over the previous version because the implementation of all logic pertaining to the databinding is now exterenal. I can get away with adding a DataBindingItem property and a little code that attaches the ID/Instance of the control automatically. It looks 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;

    }

}

 

The DataBinder control then has a method that can pick up any control on the form hat includes an IwwDataBinder interface and automatically add these controls to the DataBinder and bind them.

 

 

If you've never done DataBinding this way, this may seem pedantic but I can assure you that doing control level databinding on a control by specifying what to bind explicitly is one of the biggest timesavers when dealing with input forms. Having this logic attached to the control is much nicer than say using the collection editor to add the controls to the binder one at a time, or even using markup code in the editor.

Why are there no true Extenders in ASP.NET?

So, this brings me to a tirade (once again <g>). I really wish that ASP.NET would implement a Extender properties similar or the same as they are available in Windows Forms. In WinForms you can have a control or Component implement a specific (fairly complex) interface that effectively 'adds' properties to the object and lets you reference those properties as if they were part of the target control.

 

ASP.NET doesn't support this highly useful feature unfortunately. This would solve the above problem of attaching a BindingItem to the control nicely without having to extend the control via subclassing and still get access to it.

Extending an existing object

Scott Guthrie left a note earlier on my post regarding designer integration of the scheme above. He mentioned that the ATLAS Control Kit there's an Extender Control pattern that allows you to do the above WITHOUT having to add the property to a control.

 

I haven't time to look at the ATLAS Control Toolkit before, so I downloaded and installed it today and took a look. And sure enough if extender controls subclass from ExtenderControlBase<> you can 'extend'. First impression - very cool!

 

I've been trying to figure out exactly how this is accomplished, but I can't quite figure it out. It looks like the designers are managing this somehow behind the scenes, so it looks like this is a designer only feature. This behavior is only skin deep – it only affects the designers, it doesn't actually add a real property to the object at runtime in any way.

 

So although the object shows up in the designer as BindingItem, in code you can't access it as:

 

this.txtName.BindingItem

 

So I'm not sure, if this is really a good solution. Seeing designer and runtime behavior of an object layout differently is not a good idea I think. I do like the idea and for one I'd like to see more said about what the plans are for the future of this particular technology.

 

I think a more formal approach that allows extending similar to the way WinForms works (maybe not quite as complex and a little more lightweight) might be a better approach for the future.