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.
event using the same techniques used in the previous control. Listing 7
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 =
this.lstItemsOnRight.ClientID + ", document.all."
+ this.lstItemsOnLeft.ClientID + ", " +
(this.AllowDuplicatesOnLeft ? "true" : "false") +
string s_RemoveFromRight =
this.lstItemsOnRight.ClientID + "); ";
string s_BuildItemList =
this.lstItemsOnRight.ClientID + ",
document.all.__" + lstItemsOnRight.ClientID + "); " +
this.lstItemsOnLeft.ClientID + ", document.all.__"
+ lstItemsOnLeft.ClientID + "); ";
+ " " + s_RemoveFromRight + " " + s_BuildItemList
+ " return false");
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,
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
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 hereyou 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 goalsthe 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.