Reddnet Scribbles

It makes me want to gouge my eyes out with a cheese grater!

AjaxControlToolkit - Using the ReorderList with simple collections

Stephen M. Redd
Saturday, March 29 2008

Download Sample Project

Recently I've been working with the AjaxControlToolkit and Microsoft's Ajax technologies in .net 3.5. While I love the AjaxToolkit, I find the documentation and code-sample is somewhat lacking.

The ReorderList is also one of the lest intuitive controls in the Toolkit.

There are several other samples around the net, but all the ones I found assumed your were binding to a DataSource control of some sort, and most didn't demonstrate persisting changes to an underlying data store.

The few that showed persistence assumed a SqlDataSoruce control that would automate persistence with two-way binding (outside of demo-land, this is a a rare case  in my experience).

So when I first tried out this control, I was surprised at how difficult it was to use it with a simple collection such as an array, or a generic list.

Here is the basic markup you need in your page:

<ajaxToolkit:ReorderList ID="OrderList" runat="server" AllowReorder="true"
            LayoutType="Table" OnItemReorder="OrderList_ItemReorder" 
            PostBackOnReorder="true">
            <ItemTemplate>
                <span style="cursor: pointer;">
                    <asp:Label ID="ItemName" runat="server" 
                               Text='<%# Container.DataItem %>' />
                </span>
    </ItemTemplate>
</ajaxToolkit:ReorderList>

We're setting the text of the label control to the Container.DataItem directly rather than using an Eval or Bind method. That's because simple collections don't have properties that Eval and Bind can use, so you put the content of the data item directly in your output. This is the same with almost all bindable controls with simple collections, but is also one of those things the documentation just sort of "assumes" you already know... which is probably why I coded asp.net for about 4 years before I discovered this little trick.

On a postback or callback, there is no reliable way to get at the data items in their re-arranged state. If you put a button on the page to postback to the server and try to look at the items in the control, you will only be able to see them in the order they had been in when the control was first bound to the collection.

This means you can't just let the user make changes on the client and just submit the page back when they are done. The only reliable way to keep up with the user made reordering is to let the control postback each time the user makes a change, then manually keep track of the changes yourself.

Annoyingly, if you let the control post back to the server as the users makes their changes, the control will re-render itself with the original items in their original order. This will cause the list to reset after each postback effectively undoing the user's changes.

The only way around that seems to be to manually rebind the control yourself on each postback after you've updated the collection that you are using to manually track the changes.

To get all this to work together, the trick is to cache a copy of the items. When a user reorders the list you rearrange your copy of the items, recache it, and re-bind the ReorderList control to your updated copy.

How you choose to cache the items might depend on how much information is in the collection. For small collections you can just use ViewState, but for larger collections you'd probably want to cache them on the server with the Cache API or session variable.

This example will use ViewState since it assumes small and simple collection.

Here is the code that does all this:

private string[] OrderItems
{
    get
    {
        //We assume the array of items will be small, so we use viewstate
        //  If the array were big you may need to use session, the cache 
// API,
or even a database or filesystem to store the items
// between postbacks.
object items = ViewState["OrderItems"]; if(items == null) // items are not in viewstate, read from data store { items = GetOrderItemsFromDb(); //get values from the data store ViewState["OrderItems"] = items;//shove into viewstate } return (string[])items; } set { ViewState["OrderItems"] = value; } } private string[] GetOrderItemsFromDb() { //dummy up items, instead of a real DB
string[] ret = new string[] { "Item 1", "Item 2", "Item 3" };
return ret; } protected void Page_Load(object sender, EventArgs e) { Message.Text = string.Empty; } protected void Page_PreRender(object sender, EventArgs e) { // doing this at PreRender so we don't have to worry about when/if // we should bind based on if it's a postback or callback and what not. OrderList.DataSource = OrderItems; OrderList.DataBind(); } protected void SaveOrder_Click(object sender, EventArgs e) { //normally you'd save to reordered list to the DB or whatever foreach(string s in OrderItems) { // output the re-ordered values to page
Message.Text = Message.Text + s + "<br />";
} } protected void OrderList_ItemReorder(object sender, ReorderListItemReorderEventArgs e) { string[] items = OrderItems; List<string> list = new List<string>(OrderItems); //using a list for the reordering (convienience) string itemToMove = list[e.OldIndex]; list.Remove(itemToMove); list.Insert(e.NewIndex, itemToMove); OrderItems = list.ToArray(); //you could save this to the DB now, but this example uses a
//save button to batch up changes
}

As you can see, this is pretty straight forward. We maintain our own copy of the items collection putting it into ViewState between server trip. When the user makes a change the event handler just syncs our copy of the items collection. On PreRender we rebind the ReorderList to our collection, which should always be in sync with the client's copy. And finally, the save button can just commit the changes using our copy.

Stephen M. Redd
Saturday, March 29 2008
Filed under: Code
Tagged as: , , ,
8 Comments

» Trackbacks & Pingbacks

    No trackbacks yet.
Trackback link for this post:
http://reddnet.net/trackback.ashx?id=205

» Comments

  1. Craig Howard avatar

    Nice !

    Craig Howard — April 19, 2008 9:23 AM
  2. Adam avatar

    Hi - Just wanted to let you know that this was a big help. I am surprised that the control itself doesn't maintain state through viewstate by default.

    I wish MS had better documentation on this stuff. All I could find was the ReorderList sample on the asp.net ajax website, with an incomplete list of properties and methods.

    Thanks again!

    Adam — July 16, 2008 4:17 PM
  3. Jeff avatar

    Outstanding. Very helpful. Hard to believe there wasn't an to make the original reorder list this simple and useful.

    Jeff — July 20, 2008 10:04 AM
  4. Kevin McKinney avatar

    What about edit and inserting, have you found a way to accomplish those bits of functionality while using this technique. I can't seem to figure it out.

    Kevin McKinney — August 1, 2008 11:37 AM
  5. Stephen M. Redd avatar

    If you want a good comprehensive example, you can grab the source for my TicketDesk application. The admin section has a settings editor with three reorder lists that implement delete, update, insert, and reorder operations.

    You can download TicketDesk from:

    http://codeplex.com/TicketDesk

    Stephen M. Redd — August 1, 2008 12:36 PM
  6. John avatar

    Good Article on a very clunky control..

    John — October 23, 2008 12:26 PM
  7. Tom-Erik Thorbeck avatar

    I was having the same issues as you when using this control for the first time. On top of that I just a got a new home with no internet yet.

    Frustrating to say the least.

    Thanks for the tip!

    Tom-Erik Thorbeck — November 26, 2008 10:35 AM
  8. The Master avatar

    Thanks man, after many searches through useless other posts, this one really was what i was looking for.

    Cheers!

    The Master — January 3, 2009 6:27 PM

» Leave a Comment