Multi-ListBox ASP.NET控件

本文介绍了一款自定义的ASP.NET Multi-ListBox控件的开发过程,该控件包含两个ListBox和一个控制面板,重点讲解了如何通过重写SaveViewState和LoadViewState方法来维护数据源状态,以及如何实现页面回发时的数据更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发一个优秀的数据绑定不是一件很容易的事情。刚开始的时候走了一些弯路,一直紧紧咬着 DataBoundControl类不放。最终失望之后冷静下来想到关于DataSource不就是一个数据集合吗?明白之后,有关数据源的问题基本上也解决了。在整个 Multi-ListBox控件开发中,我认为最重要的实际上就是页面的生命周期的理解,如果您基本上理解了它的话,那么,基本上,你以后开发一款 ASP.NET控件也不是一件很难的事情。我们还是简单了解开发的思路吧。下面是类的设计图(跟本文无关的方法和属性已被我隐藏)

Multi-ListBox控件分成三部分组成,一个是左边的 ListBox(First ListBox),右边的 ListBox(Second ListBox)和一个中间的控制面板(Control Panel)。First ListBox,Second ListBox类似于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  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值