Multi-ListBox ASP.NET控件

開發一個優秀的數據綁定不是一件很容易的事情。剛開始的時候走了一些彎路,一直緊緊咬着 DataBoundControl類不放。最終失望之後冷靜下來想到關於DataSource不就是一個數據集合嗎?明白之後,有關數據源的問題基本上也解決了。在整個Multi-ListBox控件開發中,我認爲最重要的實際上就是頁面的生命週期的理解,如果您基本上理解了它的話,那麼,基本上,你以後開發一款ASP.NET控件也不是一件很難的事情。我們還是簡單瞭解開發的思路吧。下面是類的設計圖(跟本文無關的方法和屬性已被我隱藏)

Multi-ListBox控件分成三部分組成,一個是左邊的ListBox(FirstListBox),右邊的ListBox(SecondListBox)和一箇中間的控制面板(Control Panel)。FirstListBox,SecondListBox類似於Asp.net下面的ListBox。它也有DataSource,Itemes屬性,但是它本身沒有繼承ListBox。如下圖。

單擊顯示全圖,Ctrl+滾輪縮放圖片

單擊顯示全圖,Ctrl+滾輪縮放圖片

在控件的生命週期中,我們主要需要解決用戶回發頁面的時候保留ListBox的數據源(因爲我沒有采用複合控件的方式來開發)。因些,我們需要重寫控件的SaveViewState, LoadViewState二個方法。
複製  保存
protected override void LoadViewState(object savedState)
{
    if (savedState != null)
    {
        Triplet triplet = (Triplet) savedState;
        base.LoadViewState(triplet.First);
        Reflector.InvokeMethod(this.FirstListBox.Items, "LoadViewState", new object[] { triplet.Second });
        Reflector.InvokeMethod(this.SecondListBox.Items, "LoadViewState", new object[] { triplet.Third });
    }
    else
    {
        base.LoadViewState(null);
    }
    this._stateLoaded = true;
}

protected override object SaveViewState()
{
    if (EnableViewState == false)
        return null;
    //啓用控件視圖狀態
    object x = base.SaveViewState();
    object y = Reflector.InvokeMethod(FirstListBox.Items, "SaveViewState", null);
    object z = Reflector.InvokeMethod(SecondListBox.Items, "SaveViewState", null);
    if ((x == null) && (y == null) && (z == null))
    {
        return null;
    }
    return new Triplet(x, y, z);
}

爲了省事,我沒有自定義ListItem類,改爲直接使用ListItemCollection來存儲數據。因爲MS沒有提供ListItemCollection. SaveViewState和LoadViewState,我們必須採用反射的方式來調用這二個方法來保存數據。很讓人鬱悶。每當到緊要關頭,就會發現MS寫的類,方法不是internal,就是sealed。無可奈何~當然,你也可以自己寫一個類來代替ListItem類.
我們在頁面上進行ListBox進行左移,右移的數據全部需要按一定的格式臨時存儲在HiddenField控件中,這樣我們可以通過繼承IPostBackDataHandler 接口中的LoadPostData方法獲取我們臨時存儲的數據,對ListBox的數據源進行添加,移除等操作。
複製  保存
public bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
    bool resultValueFlag = false;
    //移除指定ListItem,並需要添加了Left ListBox列表框中
    string itemsRemoved = postCollection[this.ClientID + "_REMOVED"];
    string[] itemsRemovedCol = itemsRemoved.Split(',');
    if (itemsRemovedCol != null)
    {
        if (itemsRemovedCol.Length > 0 && itemsRemovedCol[0] != "")
        {
            for (int i = 0; i < itemsRemovedCol.Length; i++)
            {
                string[] itemsRemoveItems = itemsRemovedCol[i].Split('|');
                ListItem item = this.SecondListBox.Items.FindByValue(itemsRemoveItems[1]);
                if (item != null)
                {
                    this.SecondListBox.Items.Remove(item);
                }
                item = this.FirstListBox.Items.FindByValue(itemsRemoveItems[1]);
                if (item == null)
                {
                    this.FirstListBox.Items.Add(new ListItem(itemsRemoveItems[0], itemsRemoveItems[1]));
                }
                resultValueFlag = true;
            }
        }
    }
    //從客戶端添加指定的ListItem
    string itemsAdded = postCollection[this.ClientID + "_ADDED"];
    string[] itemsAddedCol = itemsAdded.Split(',');
    if (itemsAddedCol != null)
    {
        if (itemsAddedCol.Length > 0 && itemsAddedCol[0] != "")
        {
            int counter = -1;
            for (int i = 0; i < itemsAddedCol.Length; i++)
            {
                string[] itemsAddItems = itemsAddedCol[i].Split('|');
                ListItem item = this.SecondListBox.Items.FindByValue(itemsAddItems[1]);
                if (item == null)
                {
                    this.SecondListBox.Items.Add(new ListItem(itemsAddItems[0], itemsAddItems[1]));
                    counter += 1;
                }
                item = this.FirstListBox.Items.FindByValue(itemsAddItems[1]);
                if (item != null)
                {
                    this.FirstListBox.Items.Remove(item);
                }
            }
            resultValueFlag = counter > -1 ? true : false;
        }
    }

    //從客戶端中移除指定的ListItem
    return resultValueFlag;
}

public void RaisePostDataChangedEvent()
{
    //TODO::
}

一切就是這麼簡單,就是SaveViewaState,LoadViewState,LoadPostData順序。後面二個是頁面回發的時候纔會觸發。只要解決這裏,最後不過就是呈現控件而已。


如果在頁面中使用?
複製  保存
<asp:MultiListBox ID="ListBox1" runat="server" Rows="10" Width="250px" Height="200px" DataTextField="UserName" DataValueField="UserID" SelectionMode="Multiple">
    <FirstListBox><StyleSheet Width="100px" /></FirstListBox>
    <SecondListBox><StyleSheet Width="100px" /></SecondListBox>
    </asp:MultiListBox>

複製  保存
protected void Page_Load(object sender, EventArgs e)
{
    if (Page.IsPostBack)
        return;
    ListBox1.FirstListBox.DataSource = LoadData(1, 5);
    ListBox1.SecondListBox.DataSource = LoadData(6, 10);
    ListBox1.DataBind();
}

protected void Button1_Click(object sender, EventArgs e)
{
    Response.Write("您SecondList選擇的值爲:<br/>");
    foreach (ListItem item in this.ListBox1.SecondListBox.Items)
    {
        Response.Write(item.Text + ":" + item.Value + "<br/>");
    }
    Response.Write("您FirstList選擇的值爲:<br/>");
    foreach (ListItem item in this.ListBox1.FirstListBox.Items)
    {
        Response.Write(item.Text + ":" + item.Value + "<br/>");
    }
}

就像前面所說那樣,目前只完成的基本的功能,像如果頁面放了多個控件之後的問題,讓開發人員自定義修改Control Panel的圖標,自定義JS路徑等都還沒有考慮完全(時間有限,只有等以後慢慢完善)。如何跟SqlDataSource控件結合?如何直接可編輯ListBox的Items屬性就能呈現?呵呵。需要挑戰的還有許多地方。我會抽時間慢慢完善它的功能。


最後,就是源碼奉上(ASP.NET2.0)

演示地址:http://www.zfans.net/multilistboxdemo.aspx
下載地址:http://www.cnblogs.com/Files/cnzc/AspNet2.WebControls.rar  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章