Moving items between listboxes in ASP.net/PHP example

June 12, 2008 by JavaScript   PHP   ASP.NET   Visual Basic  

Ever wanted to move items between two listboxes in ASP.net (or PHP for that matter)?



Someone wanted the exact same thing on codeproject.com a while ago but I didn't give much thought to it, until someone at work asked me how to do it - since he wasn't able to find a viable solution on the internet.

The solution I scripted wasn't as simple as I thought, nor would have liked it to be, my feeling was that one would be able to alter the two listboxes easily using javascript and simply "harvest" the changes (from the Request variables) when its submitted to the server.

The reality however is that only items selected(or highlighted) in the listboxes, will be returned to the server, not the state of the listboxes - so we need to somehow send the state of the listboxes along with the page request.

What I did was, I wrote a javascript script, that serialized the contents of the listboxes as xml to a hiddenfield on the page - whenever you submit the form to the server, a xml string containing the state of the listboxes get sent along - which one can easily process server side (which I will show you just in a while).

function move(fromID, toID, containerID)
{
    var from = document.getElementById(fromID);
    var to = document.getElementById(toID);

    for (var i = 0; i < from.options.length; i++)
    {
        if (from.options[i].selected)
        {					
            to.options.add(new Option(from.options[i].text,from.options[i].value))
            from.remove(i--);
        }
    }

    var container = document.getElementById(containerID);	
    container.value = escape("<listboxes>" + serialize(from) + serialize(to) + "</listboxes>");
}

function serialize(dropdown)
{	
    var value = '<' + dropdown.id + '>';	
    for (var i = 0; i < dropdown.options.length; i++)
    {
        value+= '<option><key><![CDATA[' + encodeURIComponent(dropdown.options[i].text) + ']]></key><value><![CDATA[' + encodeURIComponent(dropdown.options[i].value) + ']]></value></option>';
    }
    value+='</' + dropdown.id + '>';
    return value
}

function unselect(listbox)
{
    document.getElementById(listbox).selectedIndex=-1;
}


Tying it all together (javascript with some .net code), you can easily write yourself an usercontrol, like I did in my example, or create a composite control.

The .net code below is pretty straighforward, you'll noticed two publicly exposed ListBox properties (lstFrom, lstTo), giving us easy access to the listboxes.

If you really need to, you can easily add third, forth listboxes (with slight changes to the javascript code)

The PHP code is quite a bit more involved, I had to create a few classes that give similiar control of what you would expect from an ASP.NET server side control. There is a lot easier ways to do this, but I believe this is a fairly manageable approach.

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;

public partial class ListPicker : System.Web.UI.UserControl
{
    private XmlDocument _xmlDocument = new XmlDocument();

    public ListBox fromListBox
    {
        get
        {
            return lstFrom;
        }
    }

    public ListBox toListBox
    {
        get
        {
            return lstTo;
        }
    }

    private void PopulateListBox(ListBox listBox)
    {
        listBox.Items.Clear();
        XmlNodeList nodes = _xmlDocument.SelectNodes("listboxes/" + listBox.ClientID + "/option");
        foreach (XmlNode node in nodes)
        {
            listBox.Items.Add(new ListItem(HttpUtility.UrlDecode(node["key"].InnerText), HttpUtility.UrlDecode(node["value"].InnerText)));
        }
    }

    private void PopulateListBoxes()
    {        
        _xmlDocument.LoadXml(HttpUtility.UrlDecode(hdnDropdowns.Value));
        PopulateListBox(lstFrom);
        PopulateListBox(lstTo);
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        Page.ClientScript.RegisterClientScriptInclude("listboxjs", Page.ResolveClientUrl("~/js/listbox.js"));
        if (!IsPostBack)
        {
            String movejs = "move('{0}','{1}','{2}')";
            String unselectjs = "unselect('{0}')";
            lstFrom.Attributes["onclick"] = String.Format(unselectjs, lstTo.ClientID);
            lstTo.Attributes["onclick"] = String.Format(unselectjs, lstFrom.ClientID);
            btnTo.Attributes["onclick"] = String.Format(movejs, lstFrom.ClientID, lstTo.ClientID, hdnDropdowns.ClientID);
            btnFrom.Attributes["onclick"] = String.Format(movejs, lstTo.ClientID, lstFrom.ClientID, hdnDropdowns.ClientID);
        }
        else
        {
            if (!(String.IsNullOrEmpty(hdnDropdowns.Value)))
            {
                PopulateListBoxes();
            }
        }
    }
}
Imports System.Xml

Partial Class ListPicker
    Inherits System.Web.UI.UserControl

    Private _xmlDocument As New XmlDocument()

    Public ReadOnly Property fromListBox() As ListBox
        Get
            Return lstFrom
        End Get
    End Property

    Public ReadOnly Property toListBox() As ListBox
        Get
            Return lstTo
        End Get
    End Property

    Private Sub PopulateListBox(ByVal lstBox As ListBox)
        lstBox.Items.Clear()
        Dim nodes As XmlNodeList = _xmlDocument.SelectNodes("listboxes/" + lstBox.ClientID + "/option")
        For Each node As XmlNode In nodes
            lstBox.Items.Add(New ListItem(HttpUtility.UrlDecode(node.Item("key").InnerText), HttpUtility.UrlDecode(node.Item("value").InnerText)))
        Next
    End Sub

    Private Sub PopulateListBoxes()
        _xmlDocument.LoadXml(HttpUtility.UrlDecode(hdnDropdowns.Value))
        PopulateListBox(lstFrom)
        PopulateListBox(lstTo)
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

Page.ClientScript.RegisterClientScriptInclude("listboxjs", Page.ResolveClientUrl("~/js/listbox.js"))

        If Not IsPostBack Then
            Dim movejs As String = "move('{0}','{1}','{2}')"
            Dim unselectjs As String = "unselect('{0}')"

            lstFrom.Attributes("onclick") = String.Format(unselectjs, lstTo.ClientID)
            lstTo.Attributes("onclick") = String.Format(unselectjs, lstFrom.ClientID)
            btnTo.Attributes("onclick") = String.Format(movejs, lstFrom.ClientID, lstTo.ClientID, hdnDropdowns.ClientID)
            btnFrom.Attributes("onclick") = String.Format(movejs, lstTo.ClientID, lstFrom.ClientID, hdnDropdowns.ClientID)
        Else
            If Not String.IsNullOrEmpty(hdnDropdowns.Value) Then
                PopulateListBoxes()
            End If
        End If
    End Sub
End Class
<?php

require_once("controls.php");
require_once("listbox.php");

class listpicker extends controls
{
    var $lstFrom;
    var $lstTo;
    var $deserializable;

    function listpicker($id)
    {
        $this->attributes['id'] = $id;
        $this->lstFrom = new listbox($id.'_lstFrom');
        $this->lstFrom->attributes['style'] = 'width:200px';
        $this->lstFrom->attributes['onclick'] = "unselect('".$id."_lstTo')";	
        $this->lstTo = new listbox($id.'_lstTo');
        $this->lstTo->attributes['style'] = 'width:200px';
        $this->lstTo->attributes['onclick'] = "unselect('".$id."_lstFrom')";

        if ($_REQUEST[$id.'$hdnDropdowns'])
        {
            $this->deserializable = true;
            $this->xml(urldecode($_REQUEST[$id.'$hdnDropdowns']));			
        }	
    }

    function render()
    {	
    $id = $this->attributes['id'];
    $html='<table>
                <tr>
                    <td>'
                        .$this->lstFrom->render().
                    '</td>
                    <td>
                        <input id="btnTo" type="button" value=">>" onclick="move(\''.$id.'_lstFrom\',\''.$id.'_lstTo\',\''.$id.'_hdnDropdowns\')" />
                            <br />
                        <input id="btnFrom" type="button" value="<<" onclick="move(\''.$id.'_lstTo\',\''.$id.'_lstFrom\',\''.$id.'_hdnDropdowns\')" />
                    </td>
                    <td>'		
                        .$this->lstTo->render().
                    '</td>
                </tr>
            </table>
	        <input type="hidden" ID="'.$id.'_hdnDropdowns" name="'.$id.'$hdnDropdowns" />';		
    return $html;
    }

    function xml($text) 
    {		
        $id = $this->attributes['id'];
        $parser = xml_parser_create();
        xml_parse_into_struct($parser, $text, $vals);
        xml_parser_free($parser);
        $tag = "";

        for($i = 0; $i < count($vals); $i++)
        {	
            switch($vals[$i]['tag'])
            {
                case strtoupper($id.'_lstFrom') : $tag = "lstFrom";	break;
                case strtoupper($id.'_lstTo') : $tag = "lstTo";	break;
            }

            if (($tag) && ($vals[$i]['tag'] == "KEY"))
            {
                $this->{$tag}->addItem($vals[$i]['value'], $vals[$i+1]['value']);					
            }
        }
    }
}

?>


Leave a Comment


March 8, 2012 by azrin

hi, i embedded the listpicker into a tab. but for the second tab the button to and from is not function. hope u can help to solve this problem. thanx in advance.

TQ February 13, 2012 by azrin

Hi, thanx very much. it works. hope can learn more from u next time. thanx again.

February 10, 2012 by Christoff Truter

Hi Azrin I assume you're posting to a different page right? You will need to include the following on your target page: include "includes/listpicker.php"; $picker = new listpicker("picker"); // Contains Values print_r($picker->lstFrom); print_r($picker->lstTo); Note, if the item arrays are empty, it means that nothing in list A or B changed, therefore it doesnt make sense to pass it on.

February 10, 2012 by azrin

hi, thanx for the sharing. i've download the coding and it's work. sory. i modified the code, get the data from database to the listbox. but, the problem now, i want to display the data from the lstTo using _POST[] when user click the button. but i didnt know the field name? can u understand what i mean? sory. thanx for advance

Thanks January 5, 2012 by Christoff Truter

Cool thanks for sharing, I actually need to update this post/control a bit as well, its about 4 years old now. Perhaps instead of using XML use JSON and perhaps instead of an usercontrol a server control, will see...

Control January 5, 2012 by Anonymous

I found this (asp.net) control: http://jasear.wordpress.com/2012/01/05/asp-net-linkedlistboxes-usercontrol/ That does something similar but uses a different approach.

thank you September 23, 2011 by Anonymous

Thanks very much for sharing the code. I have tried building with more than one listboxes and it works perfectly. Thank you.

Third/Fourth box February 19, 2011 by Christoff Truter

Hi Michelle Should be very easy to add more listboxes, will write you some examples some time this weekend and email it to you

moving items to multiple list boxes February 18, 2011 by Michelle Flanagan

Does anyone have the code to easily add third, forth listboxes (with slight changes to the javascript code). I need to be able to move the items from one listbox to 5 listboxes to assign members for 5 teams. Any help would truly be appreciated as I am new to this. I need to use Javascript with ASP.Net. Thanks! Michelle

Re: Object Expected Error December 22, 2010 by Christoff Truter

Hi there Thank you for the screenshot, I found the problem. You will find that the registration method to register the location of the javascript is a bit flimsy (wont work for certain scenarios). In order to fix it, find Page.ClientScript.RegisterClientScriptInclude("listboxjs", Page.TemplateSourceDirectory + "/js/listbox.js"); and replace it with more logical: Page.ClientScript.RegisterClientScriptInclude("listboxjs", Page.ResolveClientUrl("~/js/listbox.js")); I will be sure to update the post as well, this will probably fix the issue Jaganath had as well



Related Downloads

Moving items between listboxes