[ASP.net] GridView、ListView資料列的上移下移(底層sort資料異動的演算法)

[ASP.net] GridView、ListView資料列的上移下移(底層sort資料異動的演算法)

要實現這個功能,我見過有使用Container.DataItemIndex、抓GridView該列的列索引或使用Row_Number()處理等等。

這邊紀錄一下我見過程式碼最少的演算法(看來看去還是靠SQL做最快)

 

需求:因為客戶想要在後台維護資料「上移下移」時候,前台網站的資料排序跟著後台資料排序異動

所以

1. 先把資料表加一個sort欄位

2. 若該資料表為階層式資料(如員工資料表),則同一層的sort數字不能重覆

    以上面那張圖來看,因為北風資料庫的Categories資料表只有一層

    所以該sort欄位數字全部不重覆,否則在上移下移過程中,會發現資料沒有移動。

3. C# Server端只要得知 點選該列的sort數字,就可以進行資料排序的移動。

 

請先看SQL語法撈出全部的資料:

現在假設ListView點選第三筆資料要下移,為了要和第4筆交換sort數字,所以Select 語法:

若ListView點選第四筆資料要上移,為了要和第3筆交換sort數字,Select語法:

SQL語法準備好後,開工寫程式

 

.aspx

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
</head>
<body>
    <form id="form1" runat="server">

    <asp:SqlDataSource runat="server" ID="sds_Categories" ConnectionString="<%$ ConnectionStrings:Conn_E %>"
        SelectCommand="SELECT [CategoryID], [CategoryName], [Description], [sort] FROM [Categories]
                       Order by sort ASC" />

    <asp:ListView ID="lv_Categories" runat="server" DataKeyNames="CategoryID" 
        DataSourceID="sds_Categories" onitemcommand="lv_Categories_ItemCommand">
        <ItemTemplate>
            <tr >
                <td>
                    <asp:Label ID="CategoryIDLabel" runat="server" Text='<%# Eval("CategoryID") %>' />
                </td>
                <td>
                    <asp:Label ID="CategoryNameLabel" runat="server" Text='<%# Eval("CategoryName") %>' />
                </td>
                <td>
                    <asp:Label ID="DescriptionLabel" runat="server" Text='<%# Eval("Description") %>' />
                </td>
                <td>
                    <asp:Label ID="sortLabel" runat="server" Text='<%# Eval("sort") %>' />
                </td>
                <td>
    <asp:Button ID="cmd_Down" Text="下移" runat="server" CommandName="ChangeSort" CommandArgument='<%# Eval("sort") %>' ToolTip="Down" />
    <asp:Button ID="cmd_Up" Text="上移" runat="server" CommandName="ChangeSort" CommandArgument='<%# Eval("sort") %>' ToolTip="Up" />
                </td>
            </tr>
        </ItemTemplate>
        <LayoutTemplate>
            <table id="itemPlaceholderContainer" runat="server" border="1" >
                <tr >
                    <th>
                        CategoryID
                    </th>
                    <th>
                        CategoryName
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                        sort
                    </th>
                    <th>
                        異動
                    </th> 
                </tr>
                <tr id="itemPlaceholder" runat="server">
                </tr>
            </table>
        </LayoutTemplate>
    </asp:ListView>
    </form>
</body>
</html>

.cs 檔

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Configuration;
using System.Data;
using System.Data.SqlClient;
public partial class _Default : System.Web.UI.Page
{
    protected void lv_Categories_ItemCommand(object sender, ListViewCommandEventArgs e)
    {
        if (e.CommandName=="ChangeSort")
        {
            string sort = e.CommandArgument.ToString();//點選該列的sort數字
            string DownUp = ((Button)e.CommandSource).ToolTip;//下移或上移的Command

            #region 取得要交換sort的兩筆資料
            string sql =   " SELECT Top 2 [CategoryID],[sort] "+
                           " FROM [Categories]"+
                           " Where sort" + ((DownUp=="Down")? ">=":"<=") + sort +
                           " Order by sort " + ((DownUp == "Down") ? "ASC" : "DESC");

           //以上是呈現端Order by sort ASC的語法
           //若呈現端Order by sort DESC的話,條件要顛倒
     


            DataTable dt = this.queryDataTable(sql);
            #endregion

            if (dt.Rows.Count >= 2)//防呆第一列上移動作和最後一列下移動作
            {
                string firstID = dt.Rows[0]["CategoryID"].ToString();
                string firstSort = dt.Rows[0]["sort"].ToString();

                string secondID = dt.Rows[1]["CategoryID"].ToString();
                string secondSort = dt.Rows[1]["sort"].ToString();

                this.ExecuteNonQuery(@"SET XACT_ABORT ON
                Begin Transaction
                Update Categories Set sort='" + secondSort + "' Where CategoryID = '" + firstID + @"' /*將第一筆sort換成第二筆*/
                Update Categories Set sort='" + firstSort + "' Where CategoryID = '" + secondID + @"' /*將第二筆sort換成第一筆*/
                Commit Transaction");
            }
            

        }

        lv_Categories.DataBind();//重新資料繫結
    }



    protected string Conn_E = WebConfigurationManager.ConnectionStrings["Conn_E"].ConnectionString;
    protected DataTable queryDataTable(string sql)
    {
        using (SqlConnection conn = new SqlConnection(this.Conn_E))
        {
            SqlDataAdapter da = new SqlDataAdapter(sql, conn);
            DataSet ds = new DataSet();
            da.Fill(ds);
            return (ds.Tables.Count > 0) ? ds.Tables[0] : new DataTable();
        }
    }

    protected int ExecuteNonQuery(string sql)
    {
        using (SqlConnection conn = new SqlConnection(this.Conn_E))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            int rows = 0;
            conn.Open();
            rows = cmd.ExecuteNonQuery();
            return rows;
        }

    }
}

範例程式檔下載

 

2011.7.2 追記

如果此Table有做使用者維護畫面,而且有新增語法的話,該SQL語法最好用Begin Transaction - Commit Transaction包住

並在之前插入語法


SET TRANSACTION ISOLATION LEVEL
    SERIALIZABLE;

參考MSDN論壇