Click here to Skip to main content
15,867,453 members
Articles / Web Development / ASP.NET
Article

PickList

Rate me:
Please Sign up or sign in to vote.
4.13/5 (18 votes)
9 Jun 2004CPOL4 min read 245.5K   3.9K   75   58
Two listboxes to choose from and client-side scripting to move items from left to right and vice versa

Introduction

When working with internet-programs, you should reduce the number of post backs to the server to a minimum. This is an often heard sentence. When I was working on a site where I had a lot of these picklists on screen (see screenshot above) which can move items from the left to the right listbox and vice versa. I was in a hurry and didn't want to listen to the message of this sentence. So I made a user-control and some necessary events to be hooked upon in my code-behind. You can imagine that the client response was not so great. Now that I have more time to think it over, I came up with this custom control with client-side scripting that should have worked perfect for that site. But alas, the site is up-and-running so think of this as some fun to please you and me.

Using the code

Picklist is a composite control, which consists of two ListBoxes and four HtmlInputButtons. I tried to name everything as common as possible. So, there is no 'Selected'-listbox or a 'DeSelectAll'-function but a left- and right listbox. I used HtmlInputButtons for these can easilly have JavaScript connected to it.

The class PickList is inherited from webcontrol. It implements the INamingContainer and IPostBackDataHandler. I choose for the WebControl and not for the Control class because it has some webcontrol properties like Height and Width for the layout of the listboxes. The INamingContainer makes the naming of multiple picklists on a webform unique. The first PickList gets a name like PickList1, the second one gets PickList2. The IPostBackDataHandler is nessecary for getting values back out of some hidden fields which are being filled by JavaScript on the client. Getting values back on the server is done in the LoadPostData method. Through the PostCollection collection, the posted-back-values can be retrieved. It seems to me that this collection contains the same as the Page.Request.Forms collection.

The working of PickList is that you fill the listboxes with whatever you want during design-time or in code. When rendering the control to html, it saves the items from the listboxes separated with <tab>'s to viewstate which is done in the Page_Load method. This way, we know what was originally in the listboxes when we get a postback. Next, in the OnPreRender function, we render two hiddenfields to the client, which are later being filled by JavaScript on the client. In the beginning when making this control, I used two HtmlInputHidden controls for storing values. I had to create these two controls in the CreateChildControls, set there ID's and values and then add these to the controls collection. After searching the internet I coincidentally found the Page.RegisterHiddenField function which works the Microsoft way and is used now, instead of the HtmlInputHidden controls. Since this function, there is no need for setting the values and add it to the controls-collection. Ironically, the work saved, is not significant.

C#
protected override void OnPreRender(EventArgs e)
{ 
    base.OnPreRender(e);
        if (Page != null)
    {
        Page.RegisterHiddenField(HelperLeftID, this.ValuesLeft);
        Page.RegisterHiddenField(HelperRightID, this.ValuesRight);
    };
}

On the client, the JavaScript moves the items from one listbox to the other when the user clicks a HtmlInputButton. After this move, it sorts the collection whenever the IsSorted property is set to true. Hereafter, another JavaScript function will copy the items in the listboxes, separated with <tab>'s, to the two hiddenfields which are being read on the server when a postback takes place.

When the postback take place, the two hidden fields contain the new values for the listboxes. In the IPostBackDataHandler.LoadPostData method, the values are being read from the postCollection and compared to the saved viewstate. When changed, the items are stored in the item-collection of the listboxes. The new viewstate value's can also be saved here, but it better be saved in the Page_Load method. It seems that the collection has to be read again in the Page_Load, and it does. So why do this twice? Because the first time the control is created, the LoadPostData does not fire, but Page_Load will. ViewState has to be set for the control to work properly.

C#
bool IPostBackDataHandler.LoadPostData(string postDataKey,
    System.Collections.Specialized.NameValueCollection postCollection)
{ 
    bool lblnReturn = false; 
    string NewLeftValue = postCollection[HelperLeftID]; 
    string NewRightValue = postCollection[HelperRightID];
    if (NewLeftValue != null) 
    { 
        EnsureChildControls(); 
        // If the user changes Input value, update the text property. 
        if (String.Compare(this.ValuesLeft, NewLeftValue) != 0) 
        { 
            this.ItemsLeft.Clear(); 
            foreach (string str in NewLeftValue.Split(strSep)) 
            { 
                if (str != string.Empty) 
                // Here ViewState could easilly be set 
                // Something like: this.ValuesLeft += str
                    this.ItemsLeft.Add(str); 
            }; 
            // Returning true invokes RaisePostDataChangedEvent. 
            lblnReturn = true; 
        } 
    }
    return lblnReturn;
}

Consider when calling Page.RegisterClientScriptBlock , always first call Page.IsClientSideScriptBlockRegistered , which will avoid the performance taking call to <CODE>Page.RegisterClientScriptBlock like this:

C#
if (!Page.IsClientScriptBlockRegistered("PickListScript"))
        Page.RegisterClientScriptBlock("PickListScript", script);

This way, what is done in code behind for making the script is omitted.

In the source of the control, JavaScript is set in code-behind. I could have chosen for a separate js-file, but I choose for better deployment. Also, by reading the property IsSorted, the sort-function from the JavaScript can be switched off. When using a separate js-file should be some more coding.

Points of Interest

Databinding is not supported in this control because of simplicity. However, you can extent this control with databinding features from this good article http://www.codeproject.com/aspnet/webcontrolsdatabinding.asp. I have done it and it works great.

Also I thought it was difficult to implement child ListItems in the html-code like so:

C#
<PickList id="PickList1">
    <LeftListBox ID="left1">
        <asp:ListItem Value="1">one</asp:ListItem>
            <asp:ListItem Value="1">one</asp:ListItem>
    </LeftListBox>
</PickList> 

The solution was found at http://blogs.aspadvice.com/jlovell/archive/2004/02/29/663.aspx. I should have entered the ParseChildren(true) and PersistChildrenattribute(false).

Also to view the ListBox in design-time properly, set the height and width property to something like 150px. I did not support a designer-class for design-time support. The look and feel in design-time comes directly from the Render method.

Hope you enjoyed it!

History

  • Version 1.0, June 2004.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
3sL
Software Developer
Netherlands Netherlands
Hi my name is Dries de Groot. I have a Masters degree in applied physics and am graduated in solid-state matter. Besides that I am a .NET Professional with scrum certificates

Comments and Discussions

 
GeneralDouble Click Support Pin
i386.com22-Nov-04 8:12
i386.com22-Nov-04 8:12 
GeneralRe: Double Click Support Pin
3sL7-Jan-05 3:04
3sL7-Jan-05 3:04 
QuestionCan this be a bug? Pin
Dorin Asaftei31-Aug-04 20:57
sussDorin Asaftei31-Aug-04 20:57 
GeneralBug Pin
Matkwan8-Aug-04 17:32
Matkwan8-Aug-04 17:32 
GeneralRe: Bug Pin
3sL8-Aug-04 20:24
3sL8-Aug-04 20:24 
GeneralRe: Bug Pin
hawkfry5-Sep-04 3:38
hawkfry5-Sep-04 3:38 
GeneralRe: Bug Pin
hawkfry5-Sep-04 3:40
hawkfry5-Sep-04 3:40 
GeneralRe: Bug Pin
i386.com29-Oct-04 3:24
i386.com29-Oct-04 3:24 
Thanks Hawkfry,

Good fix Smile | :)
GeneralRe: Bug Pin
Member 108570430-May-05 18:45
Member 108570430-May-05 18:45 
GeneralClient Side Strategy Pin
bobgramcko17-Jun-04 7:21
bobgramcko17-Jun-04 7:21 
GeneralRe: Client Side Strategy Pin
3sL18-Jun-04 1:38
3sL18-Jun-04 1:38 
GeneralRe: Client Side Strategy Pin
bobgramcko18-Jun-04 6:47
bobgramcko18-Jun-04 6:47 
GeneralRe: Client Side Strategy Pin
3sL23-Jun-04 2:04
3sL23-Jun-04 2:04 
GeneralRe: Client Side Strategy Pin
bobgramcko28-Jun-04 7:32
bobgramcko28-Jun-04 7:32 
Generalnice but I missed the ability to bind data Pin
meron15-Jun-04 20:32
meron15-Jun-04 20:32 
GeneralRe: nice but I missed the ability to bind data Pin
3sL15-Jun-04 23:22
3sL15-Jun-04 23:22 
GeneralSmall bug in code Pin
3sL8-Jun-04 0:28
3sL8-Jun-04 0:28 
GeneralRe: Small bug in code Pin
3sL10-Jun-04 22:13
3sL10-Jun-04 22:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.