使用LoadControl()動態加入UserControl - (1) @ Derek.NET :: 隨意窩 Xuite日誌
  • 關鍵字
    1. 沒有新回應!






  • 如何使用RSS
    Powered by Xuite
  • Google分析
  • 2008-01-19 15:48 使用LoadControl()動態加入UserControl - (1)
    平均分數:0 顆星    投票人數:0
    我要評分:

    使用LoadControl()動態加入UserControl - (1)

    以前在.NET 1.1時代,開發DotNetNuke 3.x時候有去改Source Code,看到裡面讀入模組都是用LoadControl()做為基礎。不過到2.0之後倒是還滿少用的,只是最近想用的時候就遇到一些些小問題

    • 怎麼用LoadControl()傳參數?
    • 為什麼會使用LoadControl()把UserControl讀進PlaceHolder,點了UserControl上的按鈕卻不會觸發Click事件?
    • 如果頁面上有多個UserControl是用LoadControl(),要怎麼溝通?
    如何用LoadControl傳參數給UserControl?
    在這個問題之前,要先貼一下MSDN裡關於LoadControl的簡介:

    名稱 說明
    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的事件處理會比較完整且方用。

    小馬王 / Xuite日誌 / 回應(0) / 引用(0) / 好文轉寄
    回應