Search code examples
c#asp.netvb.netmaster-pages

ASP.NET - FindControl in nested master page


How to conveniently access a control within a nested master page?


Accessing master page controls is usually straight forward:

Dim ddl As DropDownList = Master.FindControl("ddl")

However when my setup is as follows the control cannot be found, presumably becuase the control is within a content block:

1 Root Master

<asp:ContentPlaceHolder ID="cphMainContent" runat="server" />

2 Nested Master

<%@ Master Language="VB" MasterPageFile="~/Root.master" AutoEventWireup="false" CodeFile="Nested.master.vb" Inherits="Nested" %>

<asp:Content ID="MainContent" ContentPlaceHolderID="cphMainContent" runat="server">
  <asp:DropDownList ID="ddl" runat="server" DataTextField="Text" DataValueField="ID"/>
</asp:Content>

3 Content Page VB.NET

Dim ddl As DropDownList = Master.FindControl("ddl")

Workaround

I have found a solution by traversing up the tree finding the root content placeholder cphMainContent and then looking for the control within.

cphMainContent = CType(Master.Master.FindControl("cphMainContent"), ContentPlaceHolder)
Dim ddl As DropDownList = cphMainContent .FindControl("ddl")

However this solution seems very roundabout and inefficient.

Can the control be accessed directly from within the content block of the master page?


Solution

  • Here is an extension method that can handle an arbitrary number of nesting levels:

    public static class PageExtensions
    {
        /// <summary>
        /// Recursively searches this MasterPage and its parents until it either finds a control with the given ID or
        /// runs out of parent masters to search.
        /// </summary>
        /// <param name="master">The first master to search.</param>
        /// <param name="id">The ID of the control to find.</param>
        /// <returns>The first control discovered with the given ID in a MasterPage or null if it's not found.</returns>
        public static Control FindInMasters(this MasterPage master, string id)
        {
            if (master == null)
            {
                // We've reached the end of the nested MasterPages.
                return null;
            }
            else
            {
                Control control = master.FindControl(id);
    
                if (control != null)
                {
                    // Found it!
                    return control;
                }
                else
                {
                    // Search further.
                    return master.Master.FindInMasters(id);
                }
            }
        }
    }
    

    Use the extension method from any class inheriting from System.Web.UI.Page like so:

    DropDownList ddl = (DropDownList)Page.Master.FindInMasters("ddl");
    if (ddl != null)
    {
        // do things
    }