ASP.NET Webform 簡易活動報名系統(9) 學員資料管理頁 Member/Manage.aspx Gird分頁 列表SQL分頁

by | 4 月 1, 2025 | 程式 | 0 comments

Views: 19

前面已經做好報名功能,這邊要開始做學員資料管理Member/Manage.aspx

資料管理頁包含哪些功能

  1. 列表
    • 欄位
    • 分頁/一頁幾筆/目前頁數
    • 排序
    • 列表
    • 總筆數
    • 勾選(批次刪除或做特殊處理)
  2. 新增/修改/刪除
  3. 搜尋(含Filter)
  4. 匯出Excel
  5. 匯入Excel
    • 包含匯入Sample下載
  6. 統計報表
  7. 列印

整套資料輸入輸出與查詢做起來大概有這麼多大項,然後雖然項目很多,但程式設計有個原則是沒在需求上的就不用做,每多做一個功能對專案開發上就是成本,成本就會減少公司的收入。

在來GridView是個蠻強大的工具(但效率不好),可以在上面把欄位、分頁、排序、勾選那些都做好,但缺點也是你一但脫離這麼方便的工具,學習門檻就會相當高。

思路

  1. 先建立Member/Manage.aspx
  2. Manage.aspx 加入GridView
  3. Manage.aspx.cs 加入讀取功能
  4. Manage.aspxGridView 加入 編輯 刪除
  5. Manage.aspx 加入編輯/刪除

雖然上面一小節我列了很多,但這些功能是慢慢往上疊上去的,程式上先做個可以動的小雛形,再修成需要的模樣。

建立Member/Manage.aspx

Member加入檔案

Member.aspx

在Member資料夾加入>使用主版頁面的Webform >加入Manage.aspx

選Admin.master

這是後台的頁面,所以選的是Admin/Admin.Master 然後按下確定

Manage.aspx 加入Grid

學員管理

<%@ Page Title="" Language="C#" MasterPageFile="~/Admin/Admin.Master" AutoEventWireup="true" CodeBehind="Manage.aspx.cs" Inherits="PartyRegister.Admin.Member.Manage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>學員管理</h1>
    <asp:GridView runat="server" ID="gvList">
    </asp:GridView>
</asp:Content>

一般來說GridView普遍都會簡稱為gv,我待過大部分公司的老專案都這樣做,所以在這邊把列表稱為gvList

大家可能會想問,為什麼不使用設計工具就好?

  1. 敲鍵盤比較快
  2. 程式可以直接貼給ChatGpt之類的AI直接除錯
  3. 敲程式比較可以知道自己做了些什麼

Manage.aspx.cs 加入讀取功能

這邊要分幾段

  1. 加入連線字串connectionString
  2. 加入加入讀取的程式 BindData()
  3. Page_Load 加入呼叫BindData()

GridView

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace PartyRegister.Admin.Member
{
    public partial class Manage : System.Web.UI.Page
    {
        /// <summary>
        /// 連線字串
        /// </summary>
        private readonly string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

        protected void Page_Load(object sender, EventArgs e)
        {
            //判斷頁面是第一次載入時才更新
            if(!IsPostBack)
            {
                BindData();
            }
        }

        /// <summary>
        /// GridView 讀取資料表
        /// </summary>
        private void BindData()
        {
            string sql = "SELECT * FROM Member";
            // 取得資料
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();

                using (var cmd = new SqlCommand(sql,conn))
                {
                    //寫法1:使用DataAdapter,資料不做後續處理的話用這個最快
                    //很多人用這個都是先在SQL語法上將資料處理成乾淨的資料,在讀入
                    var reader = cmd.ExecuteReader();
                    gvList.DataSource = reader;
                    gvList.DataBind();

                    // 寫法2: 使用 DataTable,通常用在讀取後要處理資料內容
                    DataTable dt = new DataTable();
                    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                    {
                        adapter.Fill(dt);
                    }
                    //通常Fill後資料已經由SQL讀入Server端了
                    //這邊後續會在C#內對DataTable做一些處理,例如統計,資料格式轉換等
                    gvList.DataSource = dt;
                    gvList.DataBind();

                    // 寫法3: 使用 DataSet,通常用在讀取會同時返回很多張資料表
                    DataSet ds = new DataSet();
                    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                    {
                        adapter.Fill(ds);
                    }
                    gvList.DataSource = ds;
                    gvList.DataBind();
                }
            }
        }
    }
}

這邊我提供三種很常見的GridView綁定資料的寫法,也把註解寫上去了,我生涯上最常見的是DataTable > DataReader > DataSet,然後無論你用哪一種,這類讀取資料的方式都稱為弱型別,意思是讀取進來的資料不去定義資料格式,讓GriveView自己判斷。

寫法優點缺點
DataReader效能高、記憶體消耗低、快速前進式讀取必須保持連線、無法隨機存取、綁定功能受限
DataTable(最常見)支援離線操作、隨機存取、適合單表操作效能略遜於 DataReader、大量資料時記憶體負擔較高
DataSet(實際上包含多個DataTable)支援多表操作、建立表間關聯、離線操作記憶體消耗高、操作較複雜、不適合單一表簡單操作

現代的開發系統則不是使用弱型別,而是先定義好資料物件,在讀取後將資料綁到List<物件>當中,這種方式稱為強型別作法,好處是程式比較容易在編譯時發現錯誤,而且可以直接操作List物件做後續資料處哩

這邊我保留DataTable的作法,其他的部分刪除,程式碼如下

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace PartyRegister.Admin.Member
{
    public partial class Manage : System.Web.UI.Page
    {
        /// <summary>
        /// 連線字串
        /// </summary>
        private readonly string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

        protected void Page_Load(object sender, EventArgs e)
        {
            //判斷頁面是第一次載入時才更新
            if(!IsPostBack)
            {
                BindData();
            }
        }

        /// <summary>
        /// GridView 讀取資料表
        /// </summary>
        private void BindData()
        {
            //讀取資料庫指令
            string sql = "SELECT * FROM Member";

            // 取得資料
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();

                using (var cmd = new SqlCommand(sql,conn))
                {
                    // 使用 DataTable讀取資料
                    DataTable dt = new DataTable();
                    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                    {
                        adapter.Fill(dt);
                    }
                    gvList.DataSource = dt;
                    gvList.DataBind();
                }
            }
        }
    }
}

調整Manage.aspx 的GridView顯示欄位

如果出來的列表資料很少,就可以直接在列表上顯示,不過這邊我們要學怎麼在列表只顯示特定資料

調整列表顯示欄位

列表欄位:

  1. ID(主鍵-不顯示)
  2. 姓名
  3. 性別
  4. 報名場次
  5. 報名時間 (顯示yyyy/MM/dd hh:mm)

確定好列表欄位後,接下來我們要做的就是先改SQL

這邊我會直接開SSMS,然後對Member資料表選讀取前1000筆資料表,再將需要的欄位貼進來

//讀取資料庫指令
string sql = @"SELECT [ID],[Name],[Gender],[EventID],[CreateTime] FROM Member";

學員管理列表

調整後的列表確實資訊變少了,不過畫面還是醜醜的,接下來我們要做幾件事

  1. 美化表格 套Bootstrap的CSS ,table table-striped,在GridView加上 CssClass="table table-striped"
  2. 把欄位的英文名稱改成中文
  3. 加上編輯(藍字) 刪除(紅字)按鈕
  4. 性別轉換成 男(藍色) 女 (紅色)
  5. 報名時間改為顯示格式為 yyyy/MM/dd hh:mm 年/月/日 時:分
<%@ Page Title="" Language="C#" MasterPageFile="~/Admin/Admin.Master" AutoEventWireup="true" CodeBehind="Manage.aspx.cs" Inherits="PartyRegister.Admin.Member.Manage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>學員管理</h1>
    <asp:GridView runat="server" ID="gvList" CssClass="table table-striped" AutoGenerateColumns="false">
        <Columns>
            <asp:TemplateField HeaderText="操作">
                <ItemTemplate>
                    <!-- 編輯按鈕 -->
                    <asp:LinkButton ID="btnEdit" runat="server" CommandName="Edit" Text="編輯" CssClass="text-primary"></asp:LinkButton>
                    &nbsp;|&nbsp;
                    <!-- 刪除按鈕,加入確認刪除提示 -->
                    <asp:LinkButton ID="btnDelete" runat="server" CommandName="Delete" Text="刪除" CssClass="text-danger"
                                    OnClientClick="return confirm('是否確定刪除?');"></asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="Name" HeaderText="姓名" SortExpression="Name"></asp:BoundField>
            <asp:TemplateField HeaderText="性別" SortExpression="Gender">
                <ItemTemplate>
                    <asp:Label ID="lblGender" runat="server"
                               Text='<%# Eval("Gender").ToString() == "1" ? "男" : "女" %>'
                               ForeColor='<%# Eval("Gender").ToString() == "1" ? System.Drawing.Color.Blue : System.Drawing.Color.Pink %>'>
                    </asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="EventID" HeaderText="場次" SortExpression="EventID"></asp:BoundField>
            <asp:BoundField DataField="CreateTime" HeaderText="報名時間" SortExpression="CreateTime" 
                            DataFormatString="{0:yyyy/MM/dd hh:mm}" />
        </Columns>
    </asp:GridView>

美化後列表

這邊詳細的說一下:

  • GridView預設會依據欄位自動產生列表,我們將這個功能關閉,加上屬性AutoGenerateColumns="false"
  • 加上<Columns></Columns>來自訂要顯示的欄位
  • 單純顯示資料使用BoundField 就好,寫法是 <asp:BoundField DataField="欄位名稱(資料庫)" HeaderText="欄位名稱(顯示在頁面上)" SortExpression="排序用欄位名稱(資料庫)"></asp:BoundField>
  • 轉換日期時間顯示格式在BoundField 加上 DataFormatString="{0:yyyy/MM/dd hh:mm}"
  • 性別跟編輯欄位要使用TemplateField功能,這個功能是在裡面塞入對應的樣板

偷吃步的作法(問AI)

其實可以先把欄位用BoundField CommandField 列出來就好,然後請ChatGpt幫你做好上面的事情,一般來說我這樣寫剩下的靠AI就能準確地幫我完成大部分的工作

提問如下

<!-- 需求: 把編輯跟刪除合併為一欄,性別=1 顯示男生(藍色) 性別=2顯示為女生(紅色), 報名時間顯示為yyyy/MM/dd -->
<%@ Page Title="" Language="C#" MasterPageFile="~/Admin/Admin.Master" AutoEventWireup="true" CodeBehind="Manage.aspx.cs" Inherits="PartyRegister.Admin.Member.Manage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>學員管理</h1>
    <asp:GridView runat="server" ID="gvList" CssClass="table table-striped" AutoGenerateColumns="false">
        <Columns>
            <asp:CommandField ShowSelectButton="true" SelectText="編輯" />
            <asp:CommandField ShowSelectButton="true" SelectText="刪除" />
            <asp:BoundField DataField="Name" HeaderText="姓名" SortExpression="Name"></asp:BoundField>
            <asp:BoundField DataField="Gender" HeaderText="性別" SortExpression="Gender"></asp:BoundField>
            <asp:BoundField DataField="EventID" HeaderText="場次" SortExpression="EventID"></asp:BoundField>
            <asp:BoundField DataField="CreateTime" HeaderText="報名時間" SortExpression="CreateTime"></asp:BoundField>
        </Columns>
    </asp:GridView>
</asp:Content>

詢問AI

AI產出的程式碼還是要自己看過,然後微調成需要的

加入分頁(單靠GridView的AutoPage)

GridView也能自動幫你處理好分頁,作法如下

  • Manage.aspx 的gvList加入屬性AllowPaging="true" OnPageIndexChanging="gvList_PageIndexChanging" ,告訴伺服器這個要做分頁
  • Manage.aspx.cs 加入 gvList_PageIndexChanging事件處理,分頁處理上對於小資料做法還蠻固定的

注意,這個寫法其實還是把全部資料都讀進來,然後靠GirdView進行分頁,如果你的資料小於萬筆應該還可以,但如果資料是數十萬筆以上就不適合了,資料量大的時候就需要進行優化,詳見後續章節。

完成後的樣子(我有先補了資料 讓資料看起來比較像)

加上分頁

Manage.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Admin/Admin.Master" AutoEventWireup="true" CodeBehind="Manage.aspx.cs" Inherits="PartyRegister.Admin.Member.Manage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>學員管理</h1>
    <asp:GridView runat="server" ID="gvList" CssClass="table table-striped" AutoGenerateColumns="false"
        AllowPaging="true" OnPageIndexChanging="gvList_PageIndexChanging">
        <Columns>
            <asp:TemplateField HeaderText="操作">
                <ItemTemplate>
                    <!-- 編輯按鈕 -->
                    <asp:LinkButton ID="btnEdit" runat="server" CommandName="Edit" Text="編輯" CssClass="text-primary"></asp:LinkButton>
                    &nbsp;|&nbsp;
                    <!-- 刪除按鈕,加入確認刪除提示 -->
                    <asp:LinkButton ID="btnDelete" runat="server" CommandName="Delete" Text="刪除" CssClass="text-danger"
                                    OnClientClick="return confirm('是否確定刪除?');"></asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="Name" HeaderText="姓名" SortExpression="Name"></asp:BoundField>
            <asp:TemplateField HeaderText="性別" SortExpression="Gender">
                <ItemTemplate>
                    <asp:Label ID="lblGender" runat="server"
                               Text='<%# Eval("Gender").ToString() == "1" ? "男" : "女" %>'
                               ForeColor='<%# Eval("Gender").ToString() == "1" ? System.Drawing.Color.Blue : System.Drawing.Color.Pink %>'>
                    </asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="EventID" HeaderText="場次" SortExpression="EventID"></asp:BoundField>
            <asp:BoundField DataField="CreateTime" HeaderText="報名時間" SortExpression="CreateTime" 
                            DataFormatString="{0:yyyy/MM/dd hh:mm}" />
        </Columns>
    </asp:GridView>
</asp:Content>

Manage.aspx.cs

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace PartyRegister.Admin.Member
{
    public partial class Manage : System.Web.UI.Page
    {
        /// <summary>
        /// 連線字串
        /// </summary>
        private readonly string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

        protected void Page_Load(object sender, EventArgs e)
        {
            //判斷頁面是第一次載入時才更新
            if(!IsPostBack)
            {
                BindData();
            }
        }

        /// <summary>
        /// GridView 讀取資料表
        /// </summary>
        private void BindData()
        {
            //讀取資料庫指令
            string sql = @"SELECT [ID],[Name],[Gender],[EventID],[CreateTime] FROM Member";

            // 取得資料
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();

                using (var cmd = new SqlCommand(sql,conn))
                {
                    // 使用 DataTable讀取資料
                    DataTable dt = new DataTable();
                    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                    {
                        adapter.Fill(dt);
                    }
                    gvList.DataSource = dt;
                    gvList.DataBind();
                }
            }
        }

        /// <summary>
        /// GridView 分頁
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void gvList_PageIndexChanging(object sender, System.Web.UI.WebControls.GridViewPageEventArgs e)
        {
            var page = e.NewPageIndex;
            gvList.PageIndex = page;
            BindData();
        }
    }
}

GridView 加入每頁筆數選擇功能

AI指令: 原始碼+幫我加上一個選擇每頁筆數的下拉選單,選項有10 30 50 100 200筆

加入筆數下拉選單

Manage.aspx 前端加入每頁頁數的下拉選單

<!-- 每頁筆數下拉選單 -->
<asp:DropDownList ID="ddlPageSize" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlPageSize_SelectedIndexChanged">
    <asp:ListItem Text="10" Value="10" Selected="True" />
    <asp:ListItem Text="30" Value="30" />
    <asp:ListItem Text="50" Value="50" />
    <asp:ListItem Text="100" Value="100" />
    <asp:ListItem Text="200" Value="200" />
</asp:DropDownList>

Manage.aspx.cs 後端加入ddlPageSize_SelectedIndexChanged 這個事件

/// <summary>
/// 下拉選單變更事件,更新 GridView 每頁筆數
/// </summary>
protected void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e)
{
    //取得每頁筆數設定
    gvList.PageSize = Convert.ToInt32(ddlPageSize.SelectedValue);
    // 重設頁碼,顯示第一頁
    gvList.PageIndex = 0;
    //重新綁定資料
    BindData();
}

GridView前加入總筆數顯示

AI指令: 原始碼+幫我加上總筆數顯示功能

做法:

  1. Manage.aspxGridView前加入總筆數的Label
  2. Manage.aspx.csDataBind()加入總筆數查詢的程式碼

Manage.aspx端,這邊加在每頁筆數下拉選單的前後,如下

每頁 <asp:DropDownList ID="ddlPageSize" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlPageSize_SelectedIndexChanged">
       <asp:ListItem Text="10" Value="10" Selected="True" />
       <asp:ListItem Text="30" Value="30" />
       <asp:ListItem Text="50" Value="50" />
       <asp:ListItem Text="100" Value="100" />
       <asp:ListItem Text="200" Value="200" />
</asp:DropDownList> 筆,查詢結果共<asp:Label ID="lbTotalCount" runat="server"></asp:Label>筆資料

Manage.aspx.cs端,改寫BindData

/// <summary>
/// GridView 讀取資料表
/// </summary>
private void BindData()
{
    //讀取資料庫指令
    string sql = @"SELECT [ID],[Name],[Gender],[EventID],[CreateTime] FROM Member";
    //string sql = @"SELECT * FROM Member";
    // 取得資料
    using (var conn = new SqlConnection(connectionString))
    {
        conn.Open();

        //讀取資料列表
        using (var cmd = new SqlCommand(sql,conn))
        {
            // 使用 DataTable讀取資料
            DataTable dt = new DataTable();
            using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
            {
                adapter.Fill(dt);
            }
            gvList.DataSource = dt;
            gvList.DataBind();
        }

        //取得總共幾筆資料
        string sqlCount = @"SELECT COUNT(1) FROM Member";
        using (var cmd = new SqlCommand(sqlCount, conn))
        {
            lbTotalCount.Text = cmd.ExecuteScalar().ToString();
        }
    }
}

優化GridView分頁

AI指令: 幫我改為透過SQL SERVER進行查詢分頁,使用FETCHOFFSET

要做SQL分頁最常見是透過FETCH NEXTOFFSET 這兩個指令組合出來的分頁,寫法是

SELECT 列表中的欄位
FROM 資料表
ORDER BY 排序欄位 ASC
OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY ;
  • OFFSET 是要跳過幾筆
  • ROWS FETCH NEXT @PageSize ROWS ONLY 是要抓接下來的幾筆
  • @Offset = 頁碼 * 每頁筆數
  • @PageSize = 每頁筆數

在來一改成自行分頁後GridView那邊就要啟用自訂分頁的功能 AllowCustomPaging="true"

Manage.aspx.cs那邊的DataBind()要大幅改寫

改好的程式碼如下

Manage.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Admin/Admin.Master" AutoEventWireup="true" CodeBehind="Manage.aspx.cs" Inherits="PartyRegister.Admin.Member.Manage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>學員管理</h1>
    <!-- 每頁筆數下拉選單 -->
    每頁 <asp:DropDownList ID="ddlPageSize" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlPageSize_SelectedIndexChanged">
        <asp:ListItem Text="10" Value="10" Selected="True" />
        <asp:ListItem Text="30" Value="30" />
        <asp:ListItem Text="50" Value="50" />
        <asp:ListItem Text="100" Value="100" />
        <asp:ListItem Text="200" Value="200" />
    </asp:DropDownList> 筆,查詢結果共<asp:Label ID="lbTotalCount" runat="server"></asp:Label>筆資料
    <asp:GridView runat="server" ID="gvList" CssClass="table table-striped" AutoGenerateColumns="false" AllowCustomPaging="true"
        AllowPaging="true" OnPageIndexChanging="gvList_PageIndexChanging">
        <Columns>
            <asp:TemplateField HeaderText="操作">
                <ItemTemplate>
                    <!-- 編輯按鈕 -->
                    <asp:LinkButton ID="btnEdit" runat="server" CommandName="Edit" Text="編輯" CssClass="text-primary"></asp:LinkButton>
                    &nbsp;|&nbsp;
                    <!-- 刪除按鈕,加入確認刪除提示 -->
                    <asp:LinkButton ID="btnDelete" runat="server" CommandName="Delete" Text="刪除" CssClass="text-danger"
                                    OnClientClick="return confirm('是否確定刪除?');"></asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="Name" HeaderText="姓名" SortExpression="Name"></asp:BoundField>
            <asp:TemplateField HeaderText="性別" SortExpression="Gender">
                <ItemTemplate>
                    <asp:Label ID="lblGender" runat="server"
                               Text='<%# Eval("Gender").ToString() == "1" ? "男" : "女" %>'
                               ForeColor='<%# Eval("Gender").ToString() == "1" ? System.Drawing.Color.Blue : System.Drawing.Color.Pink %>'>
                    </asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="EventID" HeaderText="場次" SortExpression="EventID"></asp:BoundField>
            <asp:BoundField DataField="CreateTime" HeaderText="報名時間" SortExpression="CreateTime" 
                            DataFormatString="{0:yyyy/MM/dd hh:mm}" />
        </Columns>
    </asp:GridView>
</asp:Content>

Manage.aspx.cs

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace PartyRegister.Admin.Member
{
    public partial class Manage : System.Web.UI.Page
    {
        /// <summary>
        /// 連線字串
        /// </summary>
        private readonly string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

        protected void Page_Load(object sender, EventArgs e)
        {
            //判斷頁面是第一次載入時才更新
            if(!IsPostBack)
            {
                // 初始時依下拉選單預設值設定每頁筆數
                gvList.PageSize = Convert.ToInt32(ddlPageSize.SelectedValue);
                BindData();
            }
        }

        /// <summary>
        /// GridView 讀取資料表
        /// </summary>
        private void BindData()
        {
            //查詢結果總筆數
            int totalCount = 0;

            //分頁大小
            int pageSize = gvList.PageSize;

            //第幾頁
            int pageIndex = gvList.PageIndex;

            //計算要跳過幾筆
            int offset = pageIndex * pageSize;


            // 讀取資料庫指令
            string sql = @"
                SELECT [ID], [Name], [Gender], [EventID], [CreateTime]
                FROM Member
                ORDER BY [ID] ASC
                OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY ;";
            //string sql = @"SELECT * FROM Member";
            // 取得資料
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();
                DataTable dt = new DataTable();
                using (var cmd = new SqlCommand(sql,conn))
                {
                    // 開始列
                    cmd.Parameters.AddWithValue("@Offset", offset);
                    cmd.Parameters.AddWithValue("@PageSize", pageSize);
                    
                    // 使用 DataTable讀取資料
                    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                    {
                        adapter.Fill(dt);
                    }
                   
                }

                //取得總共幾筆資料
                string sqlCount = @"SELECT COUNT(1) FROM Member";
                using (var cmd = new SqlCommand(sqlCount, conn))
                {
                    totalCount = Convert.ToInt32(cmd.ExecuteScalar());
                    lbTotalCount.Text = totalCount.ToString();
                }
                // 設定 VirtualItemCount 使 GridView 分頁控制項能顯示正確總頁數
                gvList.VirtualItemCount = totalCount;
                gvList.DataSource = dt;
                gvList.DataBind();
            }
        }

        /// <summary>
        /// GridView 分頁
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void gvList_PageIndexChanging(object sender, System.Web.UI.WebControls.GridViewPageEventArgs e)
        {
            //取得新的所在頁碼
            var page = e.NewPageIndex;
            //設定頁數
            gvList.PageIndex = page;
            //重新綁定資料
            BindData();
        }

        /// <summary>
        /// 下拉選單變更事件,更新 GridView 每頁筆數
        /// </summary>
        protected void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            //取得每頁筆數設定
            gvList.PageSize = Convert.ToInt32(ddlPageSize.SelectedValue);
            // 重設頁碼,顯示第一頁
            gvList.PageIndex = 0;
            //重新綁定資料
            BindData();
        }
    }
}

雖然目前這個範例的資料很少,不過我們還是要學習正規的作法。

GridView 加入刪除功能

前面已經把GridView 主要功能先做好了,接下來我們先從簡單的「刪除」開始做,接著再做編輯功能。

GridView加入

  • 加入當按下編輯跟刪除要觸發的事件OnRowCommand
  • 編輯跟刪除都要加入CommandName CommandArgument 這兩個是觸發OnRowCommand時候會跟著傳入的參數
  • 這邊的CommandName傳入參數不可以用EditDelete,這會導致觸發另一個事件。

GridView增加事件與按鈕屬性

<asp:GridView runat="server" ID="gvList" CssClass="table table-striped" AutoGenerateColumns="false" AllowCustomPaging="true"
     AllowPaging="true" OnPageIndexChanging="gvList_PageIndexChanging" OnRowCommand="gvList_RowCommand">
     <Columns>
         <asp:TemplateField HeaderText="操作">
             <ItemTemplate>
                 <!-- 編輯按鈕 -->
                 <asp:LinkButton ID="btnEdit" runat="server" CommandName="EditData" Text="編輯" CommandArgument='<%# Eval("ID") %>' CssClass="text-primary"></asp:LinkButton>
                 &nbsp;|&nbsp;
                 <!-- 刪除按鈕,加入確認刪除提示 -->
                 <asp:LinkButton ID="btnDelete" runat="server" CommandName="DeleteData" Text="刪除" CommandArgument='<%# Eval("ID") %>' CssClass="text-danger"
                                 OnClientClick="return confirm('是否確定刪除?');"></asp:LinkButton>
             </ItemTemplate>
         </asp:TemplateField>
         <asp:BoundField DataField="Name" HeaderText="姓名" SortExpression="Name"></asp:BoundField>
         <asp:TemplateField HeaderText="性別" SortExpression="Gender">
             <ItemTemplate>
                 <asp:Label ID="lblGender" runat="server"
                            Text='<%# Eval("Gender").ToString() == "1" ? "男" : "女" %>'
                            ForeColor='<%# Eval("Gender").ToString() == "1" ? System.Drawing.Color.Blue : System.Drawing.Color.Pink %>'>
                 </asp:Label>
             </ItemTemplate>
         </asp:TemplateField>
         <asp:BoundField DataField="EventID" HeaderText="場次" SortExpression="EventID"></asp:BoundField>
         <asp:BoundField DataField="CreateTime" HeaderText="報名時間" SortExpression="CreateTime" 
                         DataFormatString="{0:yyyy/MM/dd hh:mm}" />
     </Columns>
</asp:GridView>

後端Manage.aspx.cs的部分則是加入gvList_RowCommand這個事件

/// <summary>
/// GridView 按下編輯或刪除按鈕
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void gvList_RowCommand(object sender, GridViewCommandEventArgs e)
{
    //注意 這邊的CommandName不能只取名為Edit跟Delete 原因是會造成觸發另一種RowEdit事件
    //這邊的CommandName對應前端GridView按鈕的CommandName=""屬性
    if (e.CommandName == "EditData")
    {
        //這邊ID如果取不到就是前端GridView按鈕少寫 CommandArgument='<%# Eval("ID") %>'
        string id = e.CommandArgument.ToString();
        //TODO 執行編輯邏輯
    }
    else if (e.CommandName == "DeleteData")
    {
        string id = e.CommandArgument.ToString();
        //TODO 執行刪除邏輯
    }
}

接下來可以先試看看編輯跟刪除是否能正確觸發並取得ID

如圖

設定中斷點

這樣寫一段,測試一段對於開發上很重要的

接著刪除選定資料邏輯如下,這是直接執行SQL語法,不讀取返回資料,DELETE跟UPDATE都是這樣寫。刪除後記得要重新載入列表。

//刪除選定資料
string id = e.CommandArgument.ToString();
const string sql = "DELETE Member WHERE ID = @id ";
using (var conn = new SqlConnection(connectionString))
{
    conn.Open();
    using (var cmd = new SqlCommand(sql, conn))
    {
        //加入參數
        cmd.Parameters.AddWithValue("@id", id);
        //不讀取返回資料
        cmd.ExecuteNonQuery();
    }
}
//刪除後重新載入列表
BindData();

測試刪除功能,觀察刪除後列表是否有更新,刪除的項目是否已經從列表消失。

測試刪除

這邊介紹的作法叫做「硬刪除」,意思是直接在資料庫刪除資料,另一種作法「軟刪除」則是在資料庫欄位欄位上設定一個刪除旗標,刪除資料時僅更新該旗標。

0 Comments

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *