Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Finding Child Item Controls in ASP.NET


:P
On this page:

I was mucking around with some old control code today where I have a control that has an array of child items. While making some modifications I realized that I really wanted to make some of these controls available as child controls.

But alas it turns that ASP.NET is not readily firing any 'add' control methods (AddParsedSubObject and CreateChildren) when a control is not a direct child of the parent.

So for example, I have a TabControl that looks like this:

/// <summary>
/// Summary description for wwWebTabControl.
/// </summary>
[ToolboxData("<{0}:wwWebTabControl runat=server></{0}:wwWebTabControl>")]
[ToolboxBitmap(typeof(System.Web.UI.WebControls.Image))]
[ParseChildren(true,"TabPages")] 
[PersistChildren(false)]
public class wwWebTabControl : Control, IPostBackDataHandler, INamingContainer
{
    const string HIDDEN_FORMVAR_PREFIX = "__TABSELECTION_";

    /// <summary>
    /// Collection of Tabpages.
    /// </summary>
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]  
[PersistenceMode(PersistenceMode.InnerProperty)] public List<TabPage> TabPages { get { return Tabs; } } private List<TabPage> Tabs = new List<TabPage>();
...
}
The individual TabPages are defined as inheriting from Control primarily so they can be assigned a few common propert
    [ToolboxData("<{0}:TabPage runat=server></{0}:TabPage>")] 
    [ToolboxItem(false)]
    public class TabPage : Control

Now this works all fine and good both for design time and runtime display. In Markup the layout looks like this:

<ww:wwwebtabcontrol id="TabControl" runat="server" selectedtab="UserGuide" tabheight="30px" TabstripSeparatorHeight="" TabWidth="">
    <TabPages>
    <ww:TabPage runat="server" ID="TabPage1"  Caption="User Guide" TabPageClientId="UserGuide" ActionLink="" meta:resourcekey="TabPage1"></ww:TabPage>
    <ww:TabPage runat="server" ID="TabPage2"  Caption="Class Reference" TabPageClientId="ClassRef" ActionLink="" meta:resourcekey="TabPage2"></ww:TabPage>
    <ww:TabPage runat="server" ID="TabPage3"  Caption="Misc." TabPageClientId="Misc" ActionLink="" meta:resourcekey="TabPage3"></ww:TabPage>
    </TabPages>
</ww:wwwebtabcontrol>

However, there's some generic code I'd like to be able to run against this control. Basically I have some code that scans for any controls in the UI and allows them to be localized in a custom fashion. The code basically looks at all controls on a page and iterates through them, finds the localizable properties and stores those values away.

This too works, except it can't generically navigate into the TabPages above. Although each of the TabPages is a Control object, the objects are not added to the controls collection. IOW, this.TabControl.Controls.Count == 0.

Ok I suppose this makes sense in that TabPages is a property rather than 'control content', and that the individual TabPages are simply items in the list maintained.

But here's the big question: Is there no way to be able to detect the creation of these child items?

None of the ASP.NET control events are firing which makes sense as mentioned above. But even if you have a custom collection as opposed to List<TabPages> even the Add methods of the collection aren't firing because .NET is directly deserializing these child controls.

The only way that I can see this working is by going back to have an explicitly implemented collection class. So instead of the convenient List<TabPage> I'm back to:

public class TabCollection : CollectionBase
{
    protected Control Parent = null;

    public TabCollection(Control parent)
    {
        this.Parent = parent;
    }
...
}

I can then pass the parent container in the constructor when of the TabControl's constructor:

public wwWebTabControl()
{         
   this.Tabs = new TabCollection(this);   
}

Finally, each of the methods that add an item need to be overridden to explicitly add the control to the controls collection:
public void Add(TabPage Tab)
{
    this.List.Add(Tab);
    this.Parent.Controls.Add(Tab);
}

I can't help but think that there should be an easier way to do this. There's been a number of occasions where I've wished for better notifications in child item content and it's difficult to do this right, requiring several additional method implementations plus a separate object just to get a basic notification that the control was created.

Another place where this same sort of thing has bit me is in my wwDataBinder control which relies on similar semantics. In that control the key is that the child controls need a reference to the parent so that they can properly 'inherit' default values from the parent control. Same schtick there...

ASP.NET and Generate Local Resources

On a related note: ASP.NET manages to somehow figure out - without my child controls being in the Tab Control's Control collection - that there are items that need to be localized. So if I look at my control (see markup above) I can see that VS has indeed generated a meta resource tag and fired it against my resource provider.

So ASP.NET somehow IS walking into the child controls and I wonder how they are doing this. In my code I basically parse through the page control hierarchy - at runtime this is easy and at design time all it takes is getting a reference to the page object and go from there.

So I wonder if VS is actually parsing the page markup to get these values? Or are they actually parsing into each child property and see if the child properties are Control objects? I dunno that seems like it could cause all sorts of headaches, because child collections could be of any of a variety of collection types...

Posted in ASP.NET  Localization  

The Voices of Reason


 

# A Continuous Learner's Weblog: Links (6/26/2007)


Michal Talaga
June 27, 2007

# re: Finding Child Item Controls in ASP.NET

I spent countless hours trying to create a similar control. I have solved the problem you describe by using CreateChildControls of the parent control (in your case the wwWebTabControl).

protected override void CreateChildControls()
{
Controls.Clear();
foreach (FormItem item in Items)
{
Controls.Add(item);
}
}

No need to create a separate collection class.

Rick Strahl
June 27, 2007

# re: Finding Child Item Controls in ASP.NET

Thanks Michal. You know I tried that previously and found it wasn't firing. Tried that one more time though just now and sure enough it works and fires so yes this will solve the issue of getting controls added as chidren.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024