Monday, April 27, 2009

AJAX ReorderList Example for Adding and Editing Items using the SqlDataSource

The AJAX Control Toolkit has some very powerful controls in it. The ReorderList is no exception. It basically allows users to drag and drop rows of the list around into any order the user desired. It also, allows the user to edit and add new rows as well.

It does support the SqlDataSource quite well. This blog entry will show how to use the SqlDataSource. However, it seems that does not support the EntityDataSource that well. In a later blog entry I will show how to use the EntityDataSource.

The ReorderList has some undesired behavior such as being able to reorder items when one of the rows is in Edit mode. This “feature” allows the following problem that shows when the items are reordered, after the postback, the same position (EditItemIndex) is the same which means if the item you were editing has a different position, another row (now in that same EditItemIndex) will be edited, not the row you were editing. I show you how to change (fix) this behavior.

 Most examples you find out there, except one that I can find don’t show how to do Edit on the list of items. This blog shows a pretty good implementation of using the SqlDataSource, so I would recommend looking here also. This is where I started and what my examples are based on. The only issue I found with his example is that after an reorder, edit will edit the wrong row. To fix this be sure to do the key extra steps.

There are some key extra steps you need to do to get Editing of the Reorder list to work, that you don’t necessarily have to do for the read only mode.

  1. PostBackOnReorder=”True”
  2. Don’t use the Update panel around the ReorderList (you can, but it won’t do any good).

This example assumes you have a table in your database. Here is the SQL you can use to create one.

CREATE TABLE [dbo].[TestTable1](
    [intID] [int] IDENTITY(1,1) NOT NULL,
    [strName] [varchar](50) NOT NULL,
    [strLink] [varchar](50) NOT NULL,
    [intOrder] [int] NOT NULL,
CONSTRAINT [PK_TestTable1] PRIMARY KEY CLUSTERED
(
    [intID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

This is an enhanced copy of the blog I noted earlier.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>OrderedList AJAX</title>
    
    <style type="text/css">
        .ajaxOrderedList li
        {
            list-style:none;
        }
    </style>
    
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" ScriptMode="Release" />        
       
        <div class="ajaxOrderedList">
          <ajaxToolkit:ReorderList ID="ReorderList1" runat="server"
            AllowReorder="True"
            PostBackOnReorder="True"
            SortOrderField="intOrder"
            DataKeyField="intID"
            DataSourceID="sqlDSItems"
            ItemInsertLocation="End" 
            onitemreorder="ReorderList1_ItemReorder" 
            onitemcommand="ReorderList1_ItemCommand">
            
                <ItemTemplate>
                    &nbsp;
                    <asp:HyperLink ID="HyperLink1" runat="server" Text='<% #Eval("strName") %>' NavigateUrl='<%# Eval("strLink") %>' />
                    <asp:LinkButton ID="LinkButton1" runat="server" CommandName="Edit" Text="Edit" />
                    <asp:LinkButton ID="LinkButton3" runat="server" CommandName="Delete" Text="Delete" />
                </ItemTemplate>
                
                <DragHandleTemplate>
                    <asp:Panel ID="dragHandle" runat="server" 
                        style="height: 20px; width: 20px; border: solid 1px black; background-color: Red; cursor: pointer;" 
                        Visible="<%# ShowDragHandle %>">
                        &nbsp;
                    </asp:Panel>
                </DragHandleTemplate>
                
                <ReorderTemplate>
                    <div style="width: 300px; height: 20px; border: dotted 2px black;">
                        &nbsp;
                    </div>
                </ReorderTemplate>
                
                <InsertItemTemplate>
                    <asp:Label ID="Label1" runat="server" Text="Name"></asp:Label>
                    <asp:TextBox ID="txtName" runat="server" Text='<%# Bind("strName") %>'></asp:TextBox><br />
                    
                    <asp:Label ID="Label2" runat="server" Text="Link"></asp:Label>
                    <asp:TextBox ID="txtLink" runat="server" Text='<%# Bind("strLink") %>'></asp:TextBox><br />
                    <asp:Button ID="btnInsert" runat="server" Text="Add Link" CommandName="Insert" />
                </InsertItemTemplate>
                
                <EditItemTemplate>
                    <asp:TextBox ID="txtName" runat="server" Text='<%# Bind("strName") %>'/>
                    <asp:TextBox ID="txtLink" runat="server" Text='<%# Bind("strLink") %>' />
                    <asp:TextBox ID="txtOrder" runat="server" Text='<%# Bind("intOrder") %>' />
                    <asp:LinkButton ID="LinkButton1" runat="server" CommandName="Update" Text="Update" />
                    <asp:LinkButton ID="LinkButton2" runat="server" CommandName="Cancel" Text="Cancel" />                      
                </EditItemTemplate>
                
            </ajaxToolkit:ReorderList>
            
            <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label>
            <asp:Button ID="Button1" runat="server" Text="Button" />
        </div>
                
        <asp:SqlDataSource ID="sqlDSItems" runat="server" ConnectionString="<%$ ConnectionStrings:MyConnectionString %>"
                SelectCommand="SELECT [intID], [strName], [strLink], [intOrder] FROM [TestTable1] ORDER BY [intOrder]"
                DeleteCommand="DELETE FROM [TestTable1] WHERE [intID] = @intID"
                InsertCommand="INSERT INTO [TestTable1] ([strName], [strLink], [intOrder]) VALUES (@strName, @strLink, @intOrder)"
                UpdateCommand="UPDATE [TestTable1] SET [strName] = @strName, [strLink] = @strLink, [intOrder] = @intOrder WHERE [intID] = @intID">
            <DeleteParameters>
                <asp:Parameter Name="intID" Type="Int32" />
            </DeleteParameters>
            <UpdateParameters>
                <asp:Parameter Name="strName" Type="String" />
                <asp:Parameter Name="strLink" Type="String" />
                <asp:Parameter Name="intOrder" Type="Int32" />
                <asp:Parameter Name="intID" Type="Int32" />
            </UpdateParameters>
            <InsertParameters>
                <asp:Parameter Name="strName" Type="String" />
                <asp:Parameter Name="strLink" Type="String" />
                <asp:Parameter Name="intOrder" Type="Int32" />
            </InsertParameters>
        </asp:SqlDataSource>
        
    </form>
</body>
</html>

I do however need some code-behind because I am using events to fix some of the issues I described above. Here is the code-behind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;
using AjaxControlToolkit;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Label3.Text = DateTime.Now.ToLongTimeString();
        if (!IsPostBack)
        {
            ShowDragHandle = true;
        }
    }

    protected void ReorderList1_ItemReorder(object sender, ReorderListItemReorderEventArgs e)
    {
        ShowDragHandle = true;
    }

    protected Boolean ShowDragHandle { get; set; }

    protected void ReorderList1_ItemCommand(object sender, ReorderListCommandEventArgs e)
    {
        switch (e.CommandName)
        {
            case "Cancel":
            case "Insert":
            case "Delete":
            case "Update":
                ShowDragHandle = true;
                break;

            case "Edit":
                ShowDragHandle = false;
                break;
            
            default:
                break;
        }
    }
}

The code is pretty straight forward I think, but here is a bit of explanation to help you understand. The ShowDragHandle boolean is bound to the Panel that makes up the drag handle. It shows and hides based on this boolean. When the page if first loaded (non-postback), it is shown. All events except Edit set the ShowDragHandle to true.

I do have a Label called Label3 that is set to the current data-time on page load. This is there so that you can see when a postback occurs. No real reason to include this in your solution, it is really just to help see when a postback occurs.

Tips

  • Be sure that your select statement has an order by statement and is ordering by (ASCENDING) the same column you set the SortOrderField to.
  • If you need more information on installing the AJAX Control Toolkit in Visual Studio 2008 SP1, check of my blog entry.

7 comments:

rfaisal said...

Hai I get an error message for statement :
protected bool ShowDragHandle { get;set;}

The message :
"must declare a body because it is not marked abstract or extern"

Brent V said...

Hi rfaisal,

It sounds like you are using .NET 2. If you use .NET version 3.5 (and I think 3.0 also) the C# language supports this syntax. It is basically short hand for what you have to use in .NET 2.0.

Assuming you want to continue using .NET 2.0, I recommend you just change that one like to the following lines.

protected bool showDragHandle;
protected bool ShowDragHandle
{
get { return showDragHandle; }
set { showDragHandle = value; }
}

That should do it.
Let me know if you still have issues.

Thanks for letting me know.

Thanks,

Brent

Mike L. said...

Thanks for the post this was helpful. One thing I noticed is that the reorder was still allowed even though you set the dragHandle panel visibility to false. The DragHandleTemple still seems to have a container control there with functionality. I worked around this issue by adding ReorderList1.AllowReorder = value; to the set of the ShowDragHandle property.

john said...

Having trouble getting the dragHandle (with a background image) to show up on initial page load. All is well after I have used the edit/update or cancel linkbuttons. I'm using Visual Studio 2008 and usually browsing with IE8. Other than that, everything works perfectly.

Pradip said...

Thank You Very Much it Help Me Very Much for Creating a New Web Aplication
From NSarkaR

Anonymous said...

Thank u so much..u explained the code very well......It was more helpful all the things associated with that application was available...DB,CS,ASPx.....thanks a lot

SunKathiravan said...

fentastic