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

时间:2011-11-15 21:04:23

标签: asp.net recursion lifecycle findcontrol

我正在动态创建数据绑定DropDownList控件并将其添加到数据绑定器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>

找到我正在调用的控件

target = FindDropDownListControl("Comp1A");

见Jeff Atwood的function

protected DropDownList FindDropDownListControl(string controlReference)
{
    Control root = this.Page.FindControl("ctl00"); //the Master page (the root control)
    var ddl = (DropDownList)MyApp.Utility.ExtensionMethods.FindControlRecursive(root, controlReference) as DropDownList; 
    return ddl;
}

有人能发现可能是罪魁祸首吗?我怎样才能获得对Comp1A的引用?

4 个答案:

答案 0 :(得分:1)

我不是百分百肯定,因为您的代码示例不包含控件的创建方式,但如果我的怀疑是正确的,问题是控件是在页面生命周期中的某个位置创建的在回发中消失了。

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

如果可能,请确保在Page_Init中创建它,或者手动将其添加到Page_Init。

This article更全面地解释了这一点。

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

我觉得它看起来像那样,

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

我没有测试过,但您可以根据需要进行测试。

btw :如果我没记错的话,你需要在PageLoad事件后使用它吗OnPreRender
      比你能看到结果。 最诚挚的问候。

答案 3 :(得分:0)

编辑:我的猜测是你的页面中有一些其他控件也有ID“Comp1A”,并且你的FindControlRecursive方法首先找到该控件。它可能会给你null,因为无论它是什么控件都不是DropDownList。执行as DropDownList时,结果将为null。

以下是我对FindControl的了解,如果这对您有任何帮助。

FindControl仅限于同一命名容器中的控件(即实现INamingContainer接口的Parent / Ancestor控件)。如果您正在尝试查找位于另一个控件内部的控件,该控件是相对于您调用FindControl方法的控件的命名容器,则它将无法找到它。

Page是一个命名容器,UserControl和ContentPlaceHolder也是如此。我认为TabContainer也是一个命名容器,以及TabContainer中的每个选项卡控件。

EDIT2:Repeater和RepeaterItem(转发器的每个“行”都是RepeaterItem)也都是命名容器。这意味着如果从顶部(即页面)开始查看,您实际上无法可靠地找到嵌套在转发器中的控件。你需要在同一个RepeaterItem中设置你的起点(基本上这就是詹姆斯约翰逊所建议的)。如果您需要更多帮助,那么您需要提供有关执行target = FindDropDownListControl("Comp1A");的上下文的更多信息。

您的代码从页面开始,并尝试深入查找“Comp1A”DropDownList。如果此控件只是“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")

但是,如上所述,您尝试查找的控件位于转发器内部。您可以做的一件事是迭代转发器中的所有项目,如下所示:

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控件,那么您需要利用您的上下文来为您的搜索使用正确的根控件。