RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Achieving Synchronicity: A ListBox Double Feature : Page 4

See how to combine two Rendered controls to create a single control that provides the ability to move items both within and between lists.

All Synched Up, But More Places to Go
Tired of my pathetic attempt at catchphrases yet? Oh good, because I have plenty more.

You can now use the EnhancedListBox control to reorder some items, postback, and be sure that before the page is re-rendered, the control's server storage will be completely synchronized with the client storage that was altered at the client side. So now let's use these same techniques and build a composite control called ListMover.

The ListMover Control
Figure 2. ListMover Control: The ListMover control provides a standard way to let users move items from one list to another.
The ListMover control will contain two EnhancedListBox controls plus a few buttons used to move items back and forth between the two lists. Using the composite control building techniques I taught you in my last article, you'll learn to create child controls and render them using some surrounding HTML so that your final control looks like Figure 2. What's important to understand in this control is where certain things have to take place.

First of all, you have to add the JavaScript you need for this control in the override for the OnInit event using the same techniques used in the previous control. Listing 7 shows the JavaScript code you'll need. Like you did in the last control, you'll use JavaScript to access elements from a ListBox (<select> element) and in this case, move them over. As you can see, I've provided functions for adding items to a list, removing items from a list, as well as adding all and removing all from a list. I've separated the functionality instead of having single "move" methods so that I can make the removal of items from a list optional based on property settings. I think this gives the final control a bit more robustness but I won't walk through the code in this article. Also note I have added a BuildItemList method as in the previous control.

Now you need to wire this client code into the buttons of the composite control. You'll do this at the end of the CreateChildControls method where you initialized the child controls and built the Controls collection. I'll show you the code for one of the buttons but the others work the same way (don't forget to download the code).

   string s_AddToLeft = 
      "AddSelectedItemToList(document.all." + 
      this.lstItemsOnRight.ClientID + ", document.all." 
      + this.lstItemsOnLeft.ClientID + ", " + 
      (this.AllowDuplicatesOnLeft ? "true" : "false") + 
      "); ";
   string s_RemoveFromRight = 
      "RemoveSelectedItemFromList(document.all." + 
      this.lstItemsOnRight.ClientID + "); ";
   string s_BuildItemList = 
      "BuildItemList(document.all." + 
      this.lstItemsOnRight.ClientID + ", 
   document.all.__" + lstItemsOnRight.ClientID + "); " + 
      "BuildItemList(document.all." + 
      this.lstItemsOnLeft.ClientID + ", document.all.__"
      + lstItemsOnLeft.ClientID + "); ";
   this.btnAdd.Attributes.Add("onclick", s_AddToLeft 
      + " " + s_RemoveFromRight + " " + s_BuildItemList 
      + " return false");
Note that I'm doing the same thing I did in the previous control. I'm building the JavaScript function calls into a string and attaching them to a button. The main difference is that since this is a composite control containing other controls, you can use the Attributes.Add method of adding code to the onclick event as opposed to putting it in the stack as in a rendered control. Notice too that I placed more than one function in the onclick attribute. Also note that the end of the function calls return false in order to cancel whatever postback the button would execute.

Finally, the code will make the initial call to the client function BuildItemList in an override to the Render method. This looks just like the one I walked you through in the EnhancedListBox control so I won't repeat it here. In this control I registered two hidden text fields, one for each ListBox.

   protected override void OnPreRender(EventArgs e)
      if(Page != null)
             "__" + this.lstItemsOnRight.ClientID,
             "__" + this.lstItemsOnLeft.ClientID,
So you've built the composite control, provided some client-side JavaScript, and wired it into the buttons. As before, you can drop it on a form and use it; but it will suffer from the problem you had with the first control before you added the synchronization code. You can move items back and forth all you want but as soon as you initiate a postback (with any other control on the form) the control will revert back to its pre-postback state.

To fix this problem you'll pretty much do the same kind of thing you did in the first control. However, since you are programming a composite control, not extending an already existing control, you need to implement the IPostBackDataHandler interface and provide implementation for the LoadPostData and RaisePostDataChangedEvent methods. The implementation here (see Listing 7) is almost identical to that of the previous control except you'll synchronize the Items collections of two EnhancedListBox controls instead of just one. And as before, you need to make sure you save your SelectedIndex locations so you can set them back after you synch up the Items collection. Also note that in the first control you were overriding the LoadPostData method of the base control so at one point you called its base, Since you'll write a composite control from scratch there is no base to call and you simply need to provide the method's implementation.

The final version of this control contains several properties that add functionality including properties that determine whether the items added to one list will be removed from the other, and if a list will allow duplicate items. It also contains extensive styling for maximum reuse. In my "Custom Web Controls Demystified" article I stated that in custom control development, the more properties and styling you add to your controls, the larger your potential market will be and the more sites it can be integrated into.

That's about it. You've used hidden text fields, available from client-side script, to store the state of the list boxes, also available to use from client-side script. During the postbacks you used the contents of the hidden text fields to resynchronize with the server-side Items collection. The end result is a nice composite control that allows you to move list items back and forth with no server postback, yet keeps the changes made when a postback does occur.

Best of Both Worlds
One thing I didn't touch with too much specificity before is why I mixed the controls in this article. I started by enhancing a standard ListBox control then used two instances of this enhanced control in the ListMover control; I did not build ListMover with two standard ListBox controls. One part of the ListMover control I left out of this article is the properties that will map to the properties that were added to the EnhancedListBox control. This way I can control the enhanced functionality of the two EnhancedListBox controls from the ListMover control that contains them. So you can see that you have the best of both worlds here—you have a ListMover control that allows you to move list items between the two lists or you could reorder each list.
Figure 3. Controls Combined: By combining the ListMover and the EnchancedListBox controls, you give users complete control over two lists, letting them move items both between and within lists.
The real beauty here is something I mentioned in previous articles as one key benefit of Web control-oriented ASP.NET development-total encapsulation. The EnhancedListBox control contains all the code it needs to accomplish its goals—the reordering of its items. When I included two of them in the ListMover control, I got all the intelligence that came with them as extra functionality for the new control, including the client-side scripting each control includes and the client-to-server synchronization gave the EnhancedListBox control without having to worry about it in the ListMover control. The ListMover control simply had to worry about its own functionality. Figure 3 shows the ListMover control with the EnhancedListBox control's reorder buttons turned on.

I strongly encourage you to download the complete code for both these controls. Nothing compares to looking at a complete working product to fully understand its intricacies. Since I wrote these controls, I've used the ListMover quite a bit, not to mention a lot of downloads, and the EnhancedListBox control has pretty much replaced the standard ASP.NET ListBox in all my projects.

The techniques I used in this article demonstrate how to keep the server and client synchronized within a Web control and can be applied to many similar situations. While ASP.NET 2.0 introduces an intuitive interface for handling script callbacks, keeping functionality completely on the client remains the fastest solution and is compatible with ASP.NET 1.1 as well. I am, however, a big fan of the callback capabilities built into ASP.NET 2.0 and will cover them in future articles.

Miguel A. Castro is President of InfoTek Consulting Group, Inc., a professional consulting firm that specializes in architecting, designing, and developing solutions using Microsoft .NET technologies. His VB background goes all the way back to 1.0. Miguel's focus for the last couple of years has been the .NET Framework and languages; he is fluent in both VB .NET and C#. His programming experience goes back 20 years when he started on TRS-80s, Apple IIs, and Atari 800 computers. Miguel lives in Lincoln Park, New Jersey with his wife Elena and his daughter Victoria.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date