在嵌套的主控页中查找控件

8

我有一个嵌套了2级的主页面。它具有一个主页面,而该主页面又具有一个主页面。

当我将控件放置在名为“bcr”的ContentPlaceHolder中时,我必须像这样查找控件:

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

我是完全迷失了吗?还是这就是该如此完成的方式?

我即将使用一个MultiView,它位于一个条件内容控件内。因此,如果我想要更改视图,我必须获取对该控件的引用,对吧?获取该引用甚至会更加困难!有没有更好的方法?

谢谢

6个回答

23

寻找控件真是一件痛苦的事情,我使用了这个方法,它来自CodingHorror博客,并进行了一个小修改,如果传入空id,则返回null。

/// <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;
}

在您的情况下,我认为您需要以下内容:
Label lblName = (Label) FindControlRecursive(Page, "lblName");

使用这种方法通常更加方便,因为您不需要精确地知道控件位于哪里才能找到它(当然,假设您知道ID),但是如果您有相同名称的嵌套控件,您可能会遇到一些奇怪的行为,所以这可能是需要注意的事情。


+1 我知道这已经是5年前的事了,但这个方法让我省去了一些麻烦,谢谢! - psoshmo

4

首先,您应该知道MasterPages实际上位于Pages内部。实际上,MasterPage的Load事件在ASPX的Load事件之后被调用。

这意味着,页面对象实际上是控件层次结构中最高的控件。

因此,在这样的嵌套环境中查找任何控件的最佳方法是编写一个递归函数,循环遍历每个控件和子控件,直到找到您要查找的控件。在本例中,您的MasterPages实际上是主Page控件的子控件。

您可以通过以下方式从任何控件内部访问主Page对象:

C#:

this.Page;

VB.NET

Me.Page

我发现通常控件的FindControl()方法是无用的,因为环境始终是嵌套的。

因此,我决定使用.NET 3.5新的扩展功能来扩展Control类。

通过使用下面的代码(VB.NET),例如,在您的AppCode文件夹中,所有控件现在都将执行通过调用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

4
虽然我喜欢递归,并同意Andy和Mun的观点,但你可能还想考虑一种强类型主控页面的方法。你只需要在aspx页面中添加一个指令即可。
不要从主控页面访问页面控件,而是考虑从页面本身访问主控页面中的控件。当你的主控页面上有一个标题标签,并且想要从使用该主控页面的每个页面设置它的值时,这种方法非常有意义。
我不是100%确定,但我认为这对于嵌套主控页面来说可能是更简单的技术,因为你只需将VirtualPath指向包含你想要访问的控件的主控页面即可。但如果你想要访问两个控件,一个在每个相应的主控页面中,那么这可能会变得棘手。

好的,说得对。有时候最好把这种功能放在自定义页面基类的某种方法或属性中。当然,内置的递归控件查找器也是很有用的。 - andy

2
这里有一个更通用的代码,可以使用自定义条件(可以是lambda表达式!)来工作。
调用:
Control founded = parent.FindControl(c => c.ID == "youdId", true);

控制扩展

 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;
    }
}

1

我使用了<%@ MasterType VirtualPath="~/MyMaster.master" %>方法。在主母版页中,我有一个属性,然后在详细母版页中有另一个同名属性调用主母版的属性,它运行良好。

我在主母版页中有这个。

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


    }

这只是一个显示错误消息的 div 元素。我想在具有详细主页面的页面中使用相同的属性(此页面嵌套在主主页面中)。

然后在详细主页面中,我有这个:

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

    }

我正在从详细主控件调用主主控件以创建相同的行为。


0

我刚刚完美地让它工作了。

在contentpage.aspx中,我写了以下内容:

If Master.Master.connectsession.IsConnected Then 我的代码在这里 End If


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接