Dynamically loading an IBindableTemplate

In ASP.NET 1.x, when using a templated control, we have a choice between specifying these templates inline, or loading them dynamically using the LoadTemplate method. This would allow us to load an .ascx file and get an implementation of the ITemplate interface which we could then assign to the desired template on the server control.

In ASP.NET 2.0 this obviously still applies, but there's a caveat - if you want to take advantage of the new two-way binding features in controls such as the new FormView control, you can only specify the templates inline. The reason for this is that in order to provide the two-way binding, ASP.NET needs to generate some behind the scene code in order to implement a new IBindableTemplate interface - just as the ITemplate was used previously. Unfortunately, the ASP.NET team didn't get time to implement an equivalent LoadBindableTemplate method to fetch an implementation of IBindableTemplate - and so the only situation in which ASP.NET generates the required code is for the inline scenario.

The Workaround

So, what can we do? Well, the best workaround that I've been able to come up with goes as follows. In the .ascx file we're wanting to load our template from, instead of just providing the contents of the template, we wrap it within an <ItemTemplate> tag of a FormView or any other two-way bindable control, such as this:

<%@ Control %>
<asp:FormView runat="server">
  <ItemTemplate>
   <asp:TextBox runat="server" Text=<%# Bind("UserName") %> />
 </ItemTemplate>
</asp:FormView>

This means that when we load this control (using the standard LoadControl function), ASP.NET will have generated the required code and implemented IBindableTemplate for us. We can then use the following code:

public IBindableTemplate LoadBindableTemplate(string path) {
Control c = Page.LoadControl(path);
FormView fv = c.Controls[0] as FormView;
if (fv==null)
throw new Exception("Required FormView control not found as the first child of specified template");
return (IBindableTemplate)fv.ItemTemplate;
}

Then we can simply use this as we'd have used LoadTemplate (note you need to do this no later than the OnInit event), on the FormView object (in this case, called myFormView) that we wanted to dynamically load the template for.

myFormView.EditItemTemplate = LoadBindableTemplate("/myBindableTemplate.ascx");

Combining Templates

We can also now consider doing things such as combining two templates into one - for example, loading a common set of input fields from one template, and dynamically loading a second from elsewhere. To do this, we create a class that implements IBindableTemplate, and takes two IBindableTemplate implementations in the constructor.

public class JointBindableTemplate : IBindableTemplate
{
    IBindableTemplate _firstTemplate;
    IBindableTemplate _secondTemplate;

    public JointBindableTemplate(IBindableTemplate firstTemplate, IBindableTemplate secondTemplate)
    {
        this._firstTemplate = firstTemplate;
        this._secondTemplate = secondTemplate;
    }
    public IOrderedDictionary ExtractValues(Control container)
    {
        // extract the values for each of the templates
        IOrderedDictionary d1 = _firstTemplate.ExtractValues(container);
        IOrderedDictionary d2 = _secondTemplate.ExtractValues(container);
        // copy over to the second collection
        foreach (object key in d1.Keys)
            d2.Add(key, d1[key]);
        // return the combined collection
        return d2;
    }

    public void InstantiateIn(Control container)
    {

        // create a container control
        Control c = new Control();
        // instantiate the two templates into this control
        _firstTemplate.InstantiateIn(c);
        _secondTemplate.InstantiateIn(c);
        // add our control to the container we were passed
        container.Controls.Add(c);
    }
}

We can then use the following:

IBindableTemplate t1 = LoadBindableTemplate("/myCommonTemplate.ascx");
IBindableTemplate t2 = LoadBindableTemplate("/myExtendedTemplate.ascx"); 
JointBindableTemplate c = new JointBindableTemplate(t1,t2);
myFormView.EditItemTemplate = c;

Voila! If anyone has any comments on this - or knows a better workaround, then please let me know.

You might also like...

Comments

James Crowley James first started this website when learning Visual Basic back in 1999 whilst studying his GCSEs. The site grew steadily over the years while being run as a hobby - to a regular monthly audience ...

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Every language has an optimization operator. In C++ that operator is ‘//’”