Search code examples
c#asp.netweb-controlstemplatefield

When does a control in a programmatically added TemplateField have its' ID property set?


I have a TemplateField that is dynamically added to a custom GridView.

void ITemplate.InstantiateIn(System.Web.UI.Control container)
    {
        switch (_templateType)
        {
            case ListItemType.Header:
                if (this.ParentGridView.ShowDeleteHeaderImage)
                {
                    Image hImg = new Image();
                    hImg.ImageUrl = this.ParentGridView.DeleteHeaderImageUrl;
                    hImg.AlternateText = "Mark for Deletion";
                    container.Controls.Add(hImg);
                }
                else
                {
                    Label l = new Label();
                    l.Text = "Del";
                    container.Controls.Add(l);
                }
                break;
            case ListItemType.Item:
                container.Controls.Add(new CheckBox());
                break;
            case ListItemType.EditItem:
                break;
            case ListItemType.Footer:
                QLImageButton deleteButton = new QLImageButton();
                deleteButton.Settings.ImageId = "cmdQLGVDelete";
                deleteButton.Settings.ImageUrl = this.ParentGridView.DeleteImageUrl;
                deleteButton.CommandName = "Delete";
                container.Controls.Add(deleteButton);
                break;
        }
    }

In response to a grid Command (insert/update/delete), a method called GetRowControls is called which iterates through the columns in the particular gridrow, and adds each of its controls to a Dictionary.

Dictionary<string, WebControl> GetRowControls(GridViewRow row)
...

rowControls.Add(ctrl.ID, (WebControl)ctrl);

...

So this works fine for both template field and bound controls added declaratively, as well as dynamic-non template fields added programatically.

However when the control is a TemplateField control added dynamically ctrl.ID is always null and therefore the statement above throws an exception.

I've looked into this with Reflector because I found that when I examined the variable in the immediate window in VS 2005 i.e. ?ctrl, ctrl.ID WOULD list a value. I've since established that this is because in listing ?ctrl in the immediate window, the proprty ClientID is called and ClientID calls EnsureId(), which in turn sets ID.

public virtual string ClientID
{
    get
    {
        this.EnsureID();
        string uniqueID = this.UniqueID;
        if ((uniqueID != null) && (uniqueID.IndexOf(this.IdSeparator) >= 0))
        {
            return uniqueID.Replace(this.IdSeparator, '_');
        }
        return uniqueID;
    }
}

So I'm assuming that ClientID, UniqueId and ID are all null - although as above just reading the first two will trigger all to be set. Also note that NamingContainer is not null. It has been set.

So the work around for this is quite simple i.e. check for ctrl.ID==null and if so simply read ctrl.ClientID. And thats what I've done because time wise I've really got to get a wriggle on. But I'm still interested in the answer if anyone knows it off the top of their heads.

Why is the ID value of a child control, of a dynamically added TemplateField, set at a different time from that of other controls?


Solution

  • It is not that they behave differently, but that almost always when you add a control declaratively you set the ID right away. Try adding a label with no ID to a page and browse the control collection and check its ID, it will be null (make sure not to show its clientID since it would get the ID filled):

    <asp:Label runat="server">something</asp:Label>
    

    Also note that if you run it like that you get an span with no ID.