2008-01-19 15:48 使用LoadControl()動態加入UserControl - (1)
使用LoadControl()動態加入UserControl - (1)
以前在.NET 1.1時代,開發DotNetNuke 3.x時候有去改Source Code,看到裡面讀入模組都是用LoadControl()做為基礎。不過到2.0之後倒是還滿少用的,只是最近想用的時候就遇到一些些小問題
- 怎麼用LoadControl()傳參數?
- 為什麼會使用LoadControl()把UserControl讀進PlaceHolder,點了UserControl上的按鈕卻不會觸發Click事件?
- 如果頁面上有多個UserControl是用LoadControl(),要怎麼溝通?
在這個問題之前,要先貼一下MSDN裡關於LoadControl的簡介:
OK,看起來第二個方法是我們要的,可以把參數包進一個Object陣列傳進去就好了。但偏偏事情並沒有這麼簡單。我自己試了一下,想把Page上的一個TextBox當參數傳給UserControl(當然UserControl已經做好對應的Constructor了)。沒錯,程式順利的跑起來了,沒有Compile erro,只是顯示上有點怪。為什麼我放在UserControl上的東西不見了?
上Google搜尋了一下,找到一篇文章也在談這件事:LoadControl(Type, Object[])這個函式在處理Strong Type的確會發生一些問題。所以只好再想別的方法:用Session?最好還是不要,個人傾向在開發Web的時候,Session還是儘量能不用就不用。後來又找到另一個方法,有人寫好了一段函式,就把它紀錄一下以後可以用這招:
| 名稱 | 說明 |
| TemplateControl.LoadControl (String) | 根據指定的虛擬路徑,從檔案載入 Control 物件。 |
| TemplateControl.LoadControl (Type, Object[]) | 根據指定的型別和建構函式參數,載入 Control 物件。 |
OK,看起來第二個方法是我們要的,可以把參數包進一個Object陣列傳進去就好了。但偏偏事情並沒有這麼簡單。我自己試了一下,想把Page上的一個TextBox當參數傳給UserControl(當然UserControl已經做好對應的Constructor了)。沒錯,程式順利的跑起來了,沒有Compile erro,只是顯示上有點怪。為什麼我放在UserControl上的東西不見了?
上Google搜尋了一下,找到一篇文章也在談這件事:LoadControl(Type, Object[])這個函式在處理Strong Type的確會發生一些問題。所以只好再想別的方法:用Session?最好還是不要,個人傾向在開發Web的時候,Session還是儘量能不用就不用。後來又找到另一個方法,有人寫好了一段函式,就把它紀錄一下以後可以用這招:
private UserControl LoadControl(string UserControlPath, params object[] constructorParameters)
{
List constParamTypes = new List();
foreach (object constParam in constructorParameters)
{
constParamTypes.Add(constParam.GetType());
}
UserControl ctl = Page.LoadControl(UserControlPath) as UserControl;
// Find the relevant constructor
ConstructorInfo constructor = ctl.GetType().BaseType.GetConstructor(constParamTypes.ToArray());
//And then call the relevant constructor
if (constructor == null)
{
throw new MemberAccessException("The requested constructor was not found on : " + ctl.GetType().BaseType.ToString());
}
else
{
constructor.Invoke(ctl, constructorParameters);
}
// Finally return the fully initialized UC
return ctl;
}
這真是不錯的方法,利用Reflection機制在runtime才處理要傳入UserControl建構子的參數。
所以傳參數這件事算是解決了。但為什麼會發生加入了UserControl到PlaceHolder後,按下UserControl的按鈕卻沒觸發對應的事件呢?我想這應該是有兩個原因:
- 因為是動態加入的,所以Page沒有保留該UserControl的ViewState
- 事件加入的順序有問題
後來再檢視了一下Code果然沒錯,因為加入這UserControl的地方是這樣寫的:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Control c = LoadControl("MyControl.ascx", TextBox1);
PlaceHolder1.Controls.Add(c);
}
}
問題就是因為沒有ViewState,那當頁面被PostBack回來之後,就會不知道誰是引發PostBack的物件。所以把Page、UserControl的Load、PreRender等都做了一遍又出去,就是不會進入到UserControl的事件裡。因此呢,可以改成以下這樣:
protected void Page_Load(object sender, EventArgs e)
{
string UserControlPath = "MyControl.ascx"
Control c = LoadControl(UserControlPath, TextBox1);
PlaceHolder1.Controls.Add(c);
ViewState["UserControlPath"] = UserControlPath;
}
也就是說,每次PostBack都要再重加一次UserControl。另外也可以把一些資訊另外用ViewState給紀錄下來,當Postback後重新加入UserControl也可以再把這些資訊重新加回UserControl。或者也可以考慮使用一個Hidden Field,把Client和UserControl互動的結果給紀錄下來,PoatBack後要重新加入UserControl再重新指定這些互動的結果給UserControl。今天先寫這樣,下集就是比較進階一點的議題,要怎麼做UserControl的事件處理會比較完整且方用。
回應

