Accessing Controls inside of Templated Controls

One question that seems to come up often in the asp.net forums is from people who are trying to access controls within controls using templates. LoginViews and CreateUserWizard controls are two commonly used templated controls. These templated controls don't actually have their contents known until run-time because it is dependant on something else; data from a database, user permissions, etc.

Since this information is not known you can't just access the inner controls as you would normal controls, because it is not known until run-time what the controls are. You will get compiler errors if you try to access them. The easiest way to get the controls you need is to use a recursive find control function. Steve Smith has blogged about Using a Recursive Find Control as well as about a very important Code Optimization for using a recursive find control.

The cool thing about the recursive find control is that it will dig down into the controls collection of a control you specify looking for the control you are looking for. It is great for templated controls, because it is a lot neater than trying to statically go after it like this.

Bad Code Do Not Use This

Label Label1 = LoginView1.Controls[2].Controls[2].Controls[1].Controls[3].FindControl("Label1") as Label;

The problem with the above code is that if you move anything you'll break the code. It is not very stable, and is even hard to tell what is being done. Do not worry there is a better way of handling this. A recursive find control is an expensive operation, and this is why Steve's code optimization is important. You don't want to execute the find control more times than needed.

Good Code Use This

public static System.Web.UI.Control FindControl(System.Web.UI.Control start, string id)
{
    System.Web.UI.Control foundControl;
    if (start != null)
    {
        foundControl = start.FindControl(id);
        if (foundControl != null)
            return foundControl;
        foreach (Control c in start.Controls)
        {
            foundControl = FindControl(c, id);
            if (foundControl != null)
            return foundControl;
        }
    }
    return null;
}

The above code will find controls nested within templated controls for you. This code will probably go in one of your class libraries where you keep utility functions. When you're calling this function, I would recommend you call it within a property as in Steve's optimization. The following code shows how you would do the previous example in a better way.

Good Code Use This

private Label _label1 = null;
private Label Label1
{
    get
    {
        if (_label1 == null)
        {
            _label1 = Utilities.FindControl(LoginView1, "Label1") as Label;
        }
        return _label1 ;
    }
}

This allows you to access the control as you normally would try. You can just type in the name of the property and it will let you use it as if it were the control and there were no templated control there at all. Makes working with LoginViews and CreateUserWizard controls.

Repeating templated controls such as the GridView can also benefit from this functionality, but you need to be more careful with them because there is a control with the name you're looking for in each row of the repeating control. Make sure when using this there that you get the correct row first, and only search within that row.

Have fun finding your controls.

Comments