今天幫朋友解決一個問題ListView中怎麼FindControl不到子控件。
直接調用ListView1.FindControl("idLable") 報個錯未將對象實例內個錯抱著好奇的心態我百度了下。大概有4種辦法我就不說了。
其中一種辦法是
[csharp] Response.Write(ListView1.FindControl("ListView1$ctrl0$idLabel"));
Response.Write(ListView1.FindControl("ListView1$ctrl0$idLabel"));
主要問題是id變了。ID變成Control.UniqueID了。為什麼會出現這種情況呢。
抱著好奇的心態我查了下msdn原來ListView實現了INamingContainer接口子控件的Control.UniqueID變成了父容器加子容器的形式了!難怪
Response.Write(ListView1.FindControl("ListView1$ctrl0$idLabel"))可以找到這個控件。現在看似問題解決了。可是還是有個問題
[csharp] 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中被查找的子控件";
}
}
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的類型
[csharp] 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);
}
}
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.
[csharp] [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);
}
}
[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裡使用這個控件
[html] <cc1:INamingContainerControl ID="INamingContainerControl1" runat="server" />
<cc1:INamingContainerControl ID="INamingContainerControl2" runat="server" />
<cc1:INamingContainerControl ID="INamingContainerControl1" runat="server" />
<cc1:INamingContainerControl ID="INamingContainerControl2" runat="server" />後台cs文件在Page_Load事件裡寫如下代碼通過控件本身的ID搜索
[csharp] view plaincopyprint?protected void Page_Load(object sender, EventArgs e)
{
Response.Write(((TextBox)(INamingContainerControl1.FindControl("btn"))).Text);
}
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(((TextBox)(INamingContainerControl1.FindControl("btn"))).Text);
}
運行結果如圖
很明顯可以搜到控件。
再查看頁面源html:
[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>
<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搜到控件上面的推論完全成立。。問題終於解決叻。嘎嘎。