Finding controls inside nested master pages
Asked Answered
G

6

8

I have a master page which is nested 2 levels. It has a master page, and that master page has a master page.

When I stick controls in a ContentPlaceHolder with the name "bcr" - I have to find the controls like so:

 Label lblName =(Label)Master.Master.FindControl("bcr").FindControl("bcr").FindControl("Conditional1").FindControl("ctl03").FindControl("lblName");

Am I totally lost? Or is this how it needs to be done?

I am about to work with a MultiView, which is inside of a conditional content control. So if I want to change the view I have to get a reference to that control right? Getting that reference is going to be even nastier! Is there a better way?

Thanks

Grosberg answered 8/4, 2009 at 1:15 Comment(0)
A
4

Firstly, you should know that MasterPages actually sit inside Pages. So much so that a MasterPage's Load event is actually called after your ASPX's Load event.

This means, the Page object is actually the highest control in the control hierarchy.

So, knowing this, the best way to find any control in such a nested environment, is to write a recursive function that loops through every control and child controls until it finds the one you're looking for. in this case, your MasterPages are actually child controls of the main Page control.

You get to the main Page object from inside any control like this:

C#:

this.Page;

VB.NET

Me.Page

I find that usually, the Control's class FindControl() method is pretty useless, as the enviroment is always nested.

Because if this, I've decided to use .NET's 3.5 new Extension features to extend the Control class.

By using the code below (VB.NET), in say, your AppCode folder, all your controls will now peform a recursive find by calling FindByControlID()

    Public Module ControlExtensions
    <System.Runtime.CompilerServices.Extension()> _
    Public Function FindControlByID(ByRef SourceControl As Control, ByRef ControlID As String) As Control
        If Not String.IsNullOrEmpty(ControlID) Then
            Return FindControlHelper(Of Control)(SourceControl.Controls, ControlID)
        Else
            Return Nothing
        End If
    End Function

    Private Function FindControlHelper(Of GenericControlType)(ByVal ConCol As ControlCollection, ByRef ControlID As String) As Control
        Dim RetControl As Control

        For Each Con As Control In ConCol
            If ControlID IsNot Nothing Then
                If Con.ID = ControlID Then
                    Return Con
                End If
            Else
                If TypeOf Con Is GenericControlType Then
                    Return Con
                End If
            End If

            If Con.HasControls Then
                If ControlID IsNot Nothing Then
                    RetControl = FindControlByID(Con, ControlID)
                Else
                    RetControl = FindControlByType(Of GenericControlType)(Con)
                End If

                If RetControl IsNot Nothing Then
                    Return RetControl
                End If
            End If
        Next

        Return Nothing
    End Function

End Module
Amused answered 8/4, 2009 at 1:33 Comment(0)
P
23

Finding controls is a pain, and I've been using this method which I got from the CodingHorror blog quite a while ago, with a single modification that returns null if an empty id is passed in.

/// <summary>
/// Recursive FindControl method, to search a control and all child
/// controls for a control with the specified ID.
/// </summary>
/// <returns>Control if found or null</returns>
public static Control FindControlRecursive(Control root, string id)
{
    if (id == string.Empty)
        return null;

    if (root.ID == id)
        return root;

    foreach (Control c in root.Controls)
    {
        Control t = FindControlRecursive(c, id);
        if (t != null)
        {
            return t;
        }
    }
    return null;
}

In your case, I think you'd need the following:

Label lblName = (Label) FindControlRecursive(Page, "lblName");

Using this method is generally much more convenient, as you don't need to know exactly where the control resides to find it (assuming you know the ID, of course), though if you have nested controls with the same name, you'll probably get some strange behavior, so that might be something to watch out for.

Physostomous answered 8/4, 2009 at 1:35 Comment(1)
+1 I know this is like 5 years old, but this method saved me some headache, thanks!Bantam
A
4

Firstly, you should know that MasterPages actually sit inside Pages. So much so that a MasterPage's Load event is actually called after your ASPX's Load event.

This means, the Page object is actually the highest control in the control hierarchy.

So, knowing this, the best way to find any control in such a nested environment, is to write a recursive function that loops through every control and child controls until it finds the one you're looking for. in this case, your MasterPages are actually child controls of the main Page control.

You get to the main Page object from inside any control like this:

C#:

this.Page;

VB.NET

Me.Page

I find that usually, the Control's class FindControl() method is pretty useless, as the enviroment is always nested.

Because if this, I've decided to use .NET's 3.5 new Extension features to extend the Control class.

By using the code below (VB.NET), in say, your AppCode folder, all your controls will now peform a recursive find by calling FindByControlID()

    Public Module ControlExtensions
    <System.Runtime.CompilerServices.Extension()> _
    Public Function FindControlByID(ByRef SourceControl As Control, ByRef ControlID As String) As Control
        If Not String.IsNullOrEmpty(ControlID) Then
            Return FindControlHelper(Of Control)(SourceControl.Controls, ControlID)
        Else
            Return Nothing
        End If
    End Function

    Private Function FindControlHelper(Of GenericControlType)(ByVal ConCol As ControlCollection, ByRef ControlID As String) As Control
        Dim RetControl As Control

        For Each Con As Control In ConCol
            If ControlID IsNot Nothing Then
                If Con.ID = ControlID Then
                    Return Con
                End If
            Else
                If TypeOf Con Is GenericControlType Then
                    Return Con
                End If
            End If

            If Con.HasControls Then
                If ControlID IsNot Nothing Then
                    RetControl = FindControlByID(Con, ControlID)
                Else
                    RetControl = FindControlByType(Of GenericControlType)(Con)
                End If

                If RetControl IsNot Nothing Then
                    Return RetControl
                End If
            End If
        Next

        Return Nothing
    End Function

End Module
Amused answered 8/4, 2009 at 1:33 Comment(0)
R
4

Although I love recursion, and agree with andy and Mun, one other approach you may want to consider is to have a strongly typed Master page. All you have to do is add one directive in your aspx page.

Instead of accessing a page's control from your master page, consider accessing a control in your master page from the page itself. This approach makes a lot of sense when you have a header label on your master page, and want to set its value from each page that uses the master.

I'm not 100% sure, but I think this would be simpler technique with nested master pages, as you would just point the VirtualPath to the master containing the control you wish to access. It might prove to be tricky though if you want to access two controls, one in each respective master page.

Raver answered 8/4, 2009 at 1:48 Comment(1)
yep, good point. Sometimes its good just to put that functionality in some kind of method or property in your custom page base. Although of course, it doesn't hurt to have built in recursive control findersAmused
P
2

Here is a code that is more generic and works with a custom condition (that can be a lambda expression!)

Call:

Control founded = parent.FindControl(c => c.ID == "youdId", true);

Control extension

 public static class ControlExtensions
{
    public static Control FindControl(this Control parent, Func<Control, bool> condition, bool recurse)
    {
        Control founded = null;
        Func<Control, bool> search = null;
        search = c => c != parent && condition(c) ? (founded = c) != null :
                                                    recurse ? c.Controls.FirstOrDefault(search) != null :
                                                    (founded = c.Controls.FirstOrDefault(condition)) != null;
        search(parent);
        return founded;
    }
}
Pelagianism answered 11/10, 2012 at 19:13 Comment(0)
B
1

I have used the <%@ MasterType VirtualPath="~/MyMaster.master" %> method. I have a property in the main master page then in the detail master page other property with the same name calling the main master property and it works fine.

I have this in the main master page

 public string MensajeErrorString
    {
        set
        {
            if (value != string.Empty)
            {
                MensajeError.Visible = true;
                MensajeError.InnerHtml = value;
            }
            else
                MensajeError.Visible = false;
        }


    }

this is just a div element that have to show an error message. I would like to use this same property in the pages with the detail master page(this is nested with the main master).

Then in the detail master I have this

  public string MensajeErrorString
    {
        set
        {
                Master.MensajeErrorString = value;
        }

    }

Im calling the main master property from the detail master to create the same behavior.

Brooksbrookshire answered 8/4, 2010 at 5:6 Comment(0)
A
0

I just got it working perfectly.

In contentpage.aspx, I wrote the following:

If Master.Master.connectsession.IsConnected Then my coded comes in here End If

Aport answered 30/7, 2010 at 11:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.