Asp.net FindControl不适用于动态创建的用户控件

Asp.net FindControl不适用于动态创建的用户控件,asp.net,recursion,lifecycle,findcontrol,Asp.net,Recursion,Lifecycle,Findcontrol,我正在动态创建一个databound DropDownList控件,并将其添加到databound Repeater控件中的占位符中。databound Repeater控件是放置在带有选项卡的页面上的自定义用户控件 DropDownList的Id是动态设置的,对于下面生成的HTML,它是Comp1A 控件的创建和使用很好,但是当我尝试递归地查找控件时,我总是得到null 以下是生成的HTML: <select class="formDropDownRating" id="MainCont

我正在动态创建一个databound DropDownList控件,并将其添加到databound Repeater控件中的占位符中。databound Repeater控件是放置在带有选项卡的页面上的自定义用户控件

DropDownList的Id是动态设置的,对于下面生成的HTML,它是Comp1A

控件的创建和使用很好,但是当我尝试递归地查找控件时,我总是得到null

以下是生成的HTML:

<select class="formDropDownRating" id="MainContent_ContentPlaceHolder1_TabContainer1_tab1_CE1_Repeater1_Comp1A_0" name="ctl00$ctl00$MainContent$ContentPlaceHolder1$TabContainer1$tab1$CE1$Repeater1$ctl00$Comp1A">
    <option value="5">5 - Strongly Agree</option>
    <option value="4">4 - Agree</option>
    <option value="3">3 - Somewhat Agree</option>
    <option value="2">2 - Disagree</option>
    <option value="1">1 - Strongly Disagree</option>

</select>

看杰夫·阿特伍德的


有人能认出罪犯是什么吗?如何获取对Comp1A的引用?

我不是100%确定,因为您的代码示例不包括控件的创建方式,但如果我的怀疑是正确的,那么问题在于控件是在页面生命周期中的某个点创建的,在回发时会被删除

控件在页面生命周期中创建的确切位置是?如果它没有在正确的位置创建,或者在循环中创建得太晚,那么它将不会在回发时保存在Viewstate中

如果可能,请确保它是在页面_Init中创建的,或者手动将其添加到页面_Init中


更全面地解释它

您应该始终尝试使用离控件最近的容器作为根。如果可以访问中继器或中继器项,则将其用作根目录

如果无法访问中继器,请尝试使用页面或表单作为根目录:

protected DropDownList FindDropDownListControl(string controlReference) 
{ 
    var ddl = (DropDownList)MyApp.Utility.ExtensionMethods.FindControlRecursive(Page, controlReference) as DropDownList;  
    return ddl; 
} 

我想是这样的

public static T FindControl<T>(this Control control) {
    T ctrl = default(T);
    if(control == null) return null;
    foreach(Control c in control.Controls) {
        if(ctrl == null) {
           ctrl = FindControl<T>(c);            
        }
        else return ctrl;
    }
}
公共静态T FindControl(此控件){
T ctrl=默认值(T);
if(control==null)返回null;
foreach(Control.Controls中的控件c){
如果(ctrl==null){
ctrl=FindControl(c);
}
否则返回ctrl;
}
}
我还没有测试过,但您可以根据自己的需要来匹配示例。

顺便说一句:如果我没记错,您需要在PageLoad事件之后使用它,它是OnPreRender
这样你就能看到结果了。
致以最诚挚的问候。

编辑:我猜您的页面中还有一些ID为“Comp1A”的其他控件,您的
FindControlRecursive
方法首先查找该控件。它可能会给你空值,因为那个控件,不管它是什么,都不是DropDownList。当您将
作为DropDownList执行时,在这种情况下,结果将为null

以下是我对
FindControl
的了解,以防对您有所帮助

FindControl仅限于同一命名容器中的控件(即实现InAdminContainer接口的父/祖先控件)。如果试图查找位于另一个控件内部的控件,该控件是相对于调用
FindControl
方法的控件的命名容器,则该控件将找不到该控件

页面是命名容器,用户控件和内容占位符也是。我认为TabContainer也是一个命名容器,以及TabContainer中的每个选项卡控件

EDIT2:Repeater和RepeaterItem(Repeater的每一行都是RepeaterItem)也是命名容器。这意味着,如果从顶部(即页面)开始查看,您确实无法可靠地找到嵌套在中继器中的控件。您需要从相同的RepeaterItem中设置起点(本质上这是James Johnson建议的)。如果您在这方面需要更多帮助,那么您需要提供更多关于您正在执行
target=FindDropDownListControl(“Comp1A”)的上下文的信息

您的代码从页面开始,试图向下挖掘以找到“Comp1A”下拉列表。如果此控件只是“CE1”用户控件中的一个普通控件,那么您可以通过以下内容找到它:

this.Master.Master.FindControl("MainContent").FindControl("ContentPlaceHolder1").FindControl("TabContainer1").FindControl("tab1").FindControl("CE1").FindControl("Comp1A")
(哎呀!太长了。请参阅下面的简短语法。)

母版页还充当命名容器,因此我开始使用
this.master
而不是
this.page

看起来您正在使用另一个母版页中的母版页,因此我更新了示例以使用
this.Master.Master

根据Jeff的帖子,您可以使用以下语法完成相同的任务:

this.Master.Master.FindControl("MainContent:ContentPlaceHolder1:TabContainer1:tab1:CE1:Comp1A")
但是,如上所述,您试图找到的控件位于中继器内部。您可以做的一件事是迭代repeater中的所有项,如下所示:

Control repeater = this.Master.Master.FindControl("MainContent:ContentPlaceHolder1:TabContainer1:tab1:CE1:Repeater1");

foreach (Control control in repeater.Controls)
{
    var button = control.FindControl("Comp1A");
}
但是,如果您正在从中继器的特定行中寻找一个特定的“Comp1A”DropDownList控件,那么您将需要利用您的上下文以便使用正确的根控件进行搜索

this.Master.Master.FindControl("MainContent:ContentPlaceHolder1:TabContainer1:tab1:CE1:Comp1A")
Control repeater = this.Master.Master.FindControl("MainContent:ContentPlaceHolder1:TabContainer1:tab1:CE1:Repeater1");

foreach (Control control in repeater.Controls)
{
    var button = control.FindControl("Comp1A");
}