CSTrüter HomeArticlesDownloadsAbout meContact me
a quick look at how to create a windows service using C# 2010-02-28 21:48:06
How to call server-side code from client-side code, using PageMethods in ASP.net 2010-02-21 12:31:27
How to pass a set of data to an xml type in SQL 2005/8 2010-02-12 19:04:23
Some funky behaviour regarding overload Resolution of dynamic/object types 2010-02-09 17:16:52
Object orientated programming within JavaScript 2010-01-28 07:25:45
How to sort data using ASP.net (C#) and SQL 2005/8 2010-01-18 15:23:14
Quick look at some of the new features added to C# 4.0 2010-01-12 21:52:13
SQL 2008 introduced a nifty feature called Table-Valued Parameters (TVP) into its codebase 2010-01-06 22:58:25
How to page data using ASP.net (C#) and SQL 2005/8 2009-10-19 15:01:45
a post about sql joins 2009-09-20 15:50:57
Creating a WYSIWYG textbox for your website is actually quite simple. 2007-02-01 12:00:00
Move items between two listboxes in ASP.net(C#, VB.NET) and PHP 2008-06-12 17:07:43
Firefox word wrapping issues 2008-06-09 09:51:21
2007-02-22 12:00:00
Blog about passing parameters by reference to functions using func_get_arg(s) 2008-07-27 12:38:24
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). You can download the full working source code here, contains sources for ASP.net(C# & VB.NET) and PHP source code. 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[' + dropdown.options[i].text + ']]></key><value><![CDATA[' + 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. C# 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(node["key"].InnerText, 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.TemplateSourceDirectory + "/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(); } } } } VB.net 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(node.Item("key").InnerText, 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.TemplateSourceDirectory & "/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 <?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']); } } } } ?>
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[' + dropdown.options[i].text + ']]></key><value><![CDATA[' + dropdown.options[i].value + ']]></value></option>'; } value+='</' + dropdown.id + '>'; return value } function unselect(listbox) { document.getElementById(listbox).selectedIndex=-1; }
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(node["key"].InnerText, 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.TemplateSourceDirectory + "/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(node.Item("key").InnerText, 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.TemplateSourceDirectory & "/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']); } } } } ?>
Hi, Matt- I've downloaded you code and tried it and I can see where it works, but I'm stuck when it comes to the XML output. I'm working in PHP, and although I know it's terribly 20th Century of me, I've never used any XML, and when I look at the dozens of XML functions in the PHP manual I don't know where to start. In my application, I'll have a table of items with a serial number, a human-readable name and a status code (active=1, inactive=0). I can see from your sample how to load the listboxes. I want the operator to be able to move objects from the active to the inactive side and vise versa, and get back a set of arrays containing the serial numbers of the now active and inactive items so I can update the database. I'm sure I can derive this info somehow from the XML string you are returning into $_POST, but I'm having trouble getting this. If you could suggest one or two functions which are going to put me on the right road here, I'd appreciate it.
Hi Klaus, just remove the / from the link to the javascript. It is not loaded. Page.ClientScript.RegisterClientScriptInclude("listboxjs", Page.TemplateSourceDirectory + "js/listbox.js"); Cheers, Chris
Hey Bas Seems like I made a little mistake in my php demo as well, sigh hahha, change $picker->render(); ?> to =$picker->render(); ?>, helps to sometimes test demo code, I made the adjustment in the code in the download quickly as well (else nothing will display) I am guessing that your error reporting is set to display all errors? Including non defined variables? In this case that Request wont be set since its not been posted yet, hence the not being defined. Adding error_reporting(5); should hide that error, but a more more gracious approach will be to actually test if the Request on line 22 is set: if (isset($_REQUEST[$id.'$hdnDropdowns'])) { if ($_REQUEST[$id.'$hdnDropdowns']) { $this->deserializable = true; $this->xml(urldecode($_REQUEST[$id.'$hdnDropdowns'])); } }
Hello Christoff, I stumbled upon your script in my quest for a solution for moving items between 2 list boxes. I am using PHP. When unzipped and opening index.php I get an error "Notice: Undefined index: picker$hdnDropdowns in /usr/local/nagios/share/admin/t/listpicker/php/includes/listpicker.php on line 22". Any ideas as to what this might be? Tia
Hi there Klaus Thank you for pointing that out to me. The issue is due to the fact that the code assumes (oeps my fault), that the javascript include resolves to the root of the site (thanks to the ResolveClientUrl function) So it cant find the javascript responsible for doing the work. A quick solution would be to simply change the path accordingly ~/csharp/js/listbox.js, or adding it the javascript includes to the page calling it. A better solution would be to resolve it automatically via some code, like the following for example: Page.ClientScript.RegisterClientScriptInclude("listboxjs", Page.TemplateSourceDirectory + "/js/listbox.js"); I updated the code to reflect this, thank you for letting me know :)
I downloaded the demo and it did not work. Kept getting an error in IE... Line: 28 Char: 1 Error: Object Expected Code: 0 URL: http://localhost/listpicker/csharp/default.aspx
1 2
Codebooth my semi-community site etc (work in progress)
The company I'am currently working for as software developer.
a Parallel reference of programming languages 2009-09-10 12:48:23
a tutorial explaining how to develop a simple login using PHP and MySQL 2009-09-05 18:26:47
An article looking at adding some kind of event driven model to PHP 5 2008-07-28 12:48:09
It is very simple creating your own rss reader, the following article looks at a few methods of doing this. 2008-06-23 13:18:25
A quick reference about working with dropdown boxes (select element) in javascript. 2007-02-17 16:36:41
Collection of funny programming articles 2006-10-08 14:23:43