细说FindControl

今天帮朋友解决一个问题 ListView 中怎么 FindControl 不到子控件。

直接调用ListView1.FindControl("idLable") 报个错未将对象实例内个错抱着好奇的心态我百度了下。大概有4种办法我就不说了。

 其中一种办法是

Response.Write(ListView1.FindControl("ListView1$ctrl0$idLabel"));

主要问题是id变了。ID变成Control.UniqueID了。为什么会出现这种情况呢。 

抱着好奇的心态我查了下msdn原来ListView实现了INamingContainer接口子控件的Control.UniqueID变成了父容器加子容器的形式了!难怪

Response.Write(ListView1.FindControl("ListView1$ctrl0$idLabel"))可以找到这个控件。现在看似问题解决了。可是还是有个问题

protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
    {
        //这里和Repeater稍有不同

        if (e.Item.ItemType == ListViewItemType.DataItem)
        {

            Label txtName = (Label)e.Item.FindControl("idLable");

            txtName.Text = "我是ListView中被查找的子控件";

        }
    }

这个方法也能找到Lable不是说FindControl传的是Control.UniqueID么?这里传idLable怎么可以?经过我和狮虎仔细讨论之后终于得出了结论。

我们可以看e.Item的类型

namespace System.Web.UI.WebControls
{
    // 摘要:
    //     表示 System.Web.UI.WebControls.ListView 控件中的单个项。
    [ToolboxItem(false)]
    public class ListViewItem : Control, IDataItemContainer, INamingContainer
    {
        // 摘要:
        //     初始化 System.Web.UI.WebControls.ListViewItem 类的新实例。
        //
        // 参数:
        //   itemType:
        //     System.Web.UI.WebControls.ListViewItemType 枚举值之一。
        [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
        public ListViewItem(ListViewItemType itemType);

        // 摘要:
        //     获取或设置 System.Web.UI.WebControls.ListViewItem 对象绑定到的基础数据对象。
        //
        // 返回结果:
        //     System.Web.UI.WebControls.ListViewItem 对象绑定到的基础数据对象。
        public virtual object DataItem { get; set; }
        //
        // 摘要:
        //     获取绑定到 System.Web.UI.WebControls.ListViewItem 控件的数据项的索引。
        //
        // 返回结果:
        //     绑定到 System.Web.UI.WebControls.ListViewItem 控件的数据项的索引。
        public virtual int DataItemIndex { get; }
        //
        // 摘要:
        //     获取数据项在 System.Web.UI.WebControls.ListView 控件中显示的位置。
        //
        // 返回结果:
        //     数据项在 System.Web.UI.WebControls.ListView 控件中显示的位置。
        public virtual int DisplayIndex { get; }
        //
        // 摘要:
        //     获取 System.Web.UI.WebControls.ListViewItem 对象的项类型。
        //
        // 返回结果:
        //     System.Web.UI.WebControls.ListViewItemType 值之一。
        public ListViewItemType ItemType { get; }

        // 摘要:
        //     确定是否将事件沿页面的 ASP.NET 服务器控件层次结构向上传递。
        //
        // 参数:
        //   source:
        //     事件源。
        //
        //   e:
        //     事件数据。
        //
        // 返回结果:
        //     如果事件已被取消,则为 true;否则为 false。
        protected override bool OnBubbleEvent(object source, EventArgs e);
    }
}


ListViewItem继承了INamingContainer接口。好吧现在让我们理解为什么ListView1.FindControl("ldLable")不行而Label txtName = (Label)e.Item.FindControl("idLabel")可以。INamingContainer接口是让子控件的Control.UniqueID变成类似父容器加子控件的形式。而在Label txtName = (Label)e.Item.FindControl("idLabel")注意这个事件是绑定行的时候发生的也就是说e.Item就代表一行。他是idLable的上一级控件。所以Label txtName = (Label)e.Item.FindControl("idLabel")FindControl的时候必定会先转化为Control.UniqueID!这样就不违反FindControl(Control.UniqueID)的规则了。而ListView1.FindControl("ldLable")很明显虽然ListView继承了INamingContainer但是他不是ldLable的上一级控件不是他的父容器。无法将ldLable转化成父容器加子控件的Control.UniqueID,违反了FindControl(Control.UniqueID)的规则!自然找不到。

由此我推断出了FindControl工作的大致的流程。先判断调用他的对象是否继承INamingContainer如果继承就转化为INamingContainer,将控件转化为父容器加子控件的形式.ListView1这么调用的时候 没发现他的子控件有lblable ,然后再看他本身是不是服务器ID 结果发现又不是 ,就抛出异常了.Label txtName = (Label)e.Item.FindControl("idLabel") 的时候本身就发现idLabel是他的子控件,ListViewItem又继承了INamingContainer接口,直接转化成"ListView1$ctrl0$idLabel"这样当然找的到叻。哈哈。为了验证我的想法。我自定义了一个控件让他继承INamingContainer.


[ToolboxData("<{0}:INamingContainerControl runat=server></{0}:INamingContainerControl>")]
    public class INamingContainerControl : WebControl, INamingContainer //可以将 INamingContainer 去掉, 运行并查看源代码
    {
        protected override void CreateChildControls()
        {
            TextBox textbox = new TextBox();
            textbox.ID = "btn";
            textbox.Text = "我是淡淡蜀黍";
            this.Controls.Add(textbox);


            Button button = new Button();
            button.ID = "btnOK";
            button.Text = "确定";
            this.Controls.Add(button);

        }

    }

然后在html里使用这个控件

<cc1:INamingContainerControl ID="INamingContainerControl1" runat="server" />
<cc1:INamingContainerControl ID="INamingContainerControl2" runat="server" />

后台cs文件在Page_Load事件里写如下代码通过控件本身的ID搜索

 

protected void Page_Load(object sender, EventArgs e)
    {
        Response.Write(((TextBox)(INamingContainerControl1.FindControl("btn"))).Text);
    }


运行结果如图

很明显可以搜到控件。

再查看页面源html:

 

<div>
        <span id="INamingContainerControl1"><input name="INamingContainerControl1$btn" type="text" value="我是淡淡蜀黍" id="INamingContainerControl1_btn" /><input type="submit" name="INamingContainerControl1$btnOK" value="确定" id="INamingContainerControl1_btnOK" /></span>
        <span id="INamingContainerControl2"><input name="INamingContainerControl2$btn" type="text" value="我是淡淡蜀黍" id="INamingContainerControl2_btn" /><input type="submit" name="INamingContainerControl2$btnOK" value="确定" id="INamingContainerControl2_btnOK" /></span>
</div>

很明显ID是父容器加子控件的形式的。这说明如果搜索的调用者也就是control.FindControl(string id)的这个control是子控件的父容器且继承了INamingContainer接口完全可以通过编程是定义的ID通过FindControl搜到控件上面的推论完全成立。。问题终于解决叻。嘎嘎。



 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值