ASP.NET Webform 簡易活動報名系統(9) 學員資料管理頁 Member/Manage.aspx (進行撰寫中)

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

Views: 4

前面已經做好報名功能,這邊要開始做學員資料管理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 @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();

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

測試刪除

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

編輯資料的幾種常見方式說明

在 ASP.NET WebForm 或其他網頁應用中,針對「編輯資料」的操作方式大致可以分為以下幾類,每種有不同的適用場景與技術手法:


1. 使用 iFrame 開啟編輯視窗(嵌入頁面)

  • 做法:在主頁面內開啟一個彈出區塊(或新區塊),透過 <iframe> 載入編輯用的頁面。
  • 優點
    • 隔離主畫面與編輯頁,互不干擾。
    • 簡單快速,不需重構畫面。
  • 缺點
    • 體驗略顯老舊,無法完全控制 iframe 內的互動與樣式。
    • 和主頁面溝通需額外透過 JavaScript。
  • 適合場景:快速套用既有頁面、想省事的舊系統維護。

2. 開新視窗或分頁(獨立編輯頁)

  • 做法:點選編輯按鈕後,透過 Response.Redirect() 或 JavaScript 的 window.open() 開新頁面,編輯完後點儲存並關閉頁面。
  • 優點
    • 編輯頁獨立清楚,適合複雜資料。
    • 前後流程簡單直覺。
  • 缺點
    • 需注意使用者操作流程與視窗關閉時機。
    • 無法直接即時回傳主頁更新,通常需靠 refresh
  • 適合場景:後台系統、複雜編輯表單、表單需要多欄位或分頁的情境。

3. 編輯與清單同一頁(使用 Panel 隱藏/顯示)

  • 做法:主頁面下方或右側內建編輯區塊(如 Panel),平時隱藏,點選「編輯」時載入該筆資料顯示出來。
  • 優點
    • 使用者不需跳頁,體驗流暢。
    • 簡單控制,易於資料同步。
  • 缺點
    • 畫面需預留空間。
    • 當資料過多或多欄位時畫面會變擁擠。
  • 適合場景:小型表單、資料量少、希望「單頁操作」的情況。

4. 使用 Modal(彈跳視窗)編輯

  • 做法:點選「編輯」後,顯示 Bootstrap 或 jQuery UI 的 Modal 彈窗進行編輯。
  • 優點
    • 使用者體驗佳,現代化介面。
    • 適合單一欄位或簡單表單編輯。
  • 缺點
    • WebForm 實作較複雜,不像 MVC 那樣有原生支援。
    • 控制項的生命週期與事件管理難度較高。
  • 適合場景:較新式或自定義的 UI、搭配 jQuery 或 Bootstrap 的專案。

編輯的作法整理

方法頁面跳轉使用體驗適合資料量備註
iFrame中小維護舊系統時可用
開新視窗中上中大傳統常見方式
同頁 Panel 切換單頁應用適合
Modal 彈出編輯視窗小中現代介面,WebForm需額外處理事件控制

個人生涯遇到的頻率

開新視窗 >>>> Panel切換 > iFrame>>>>Modal

接下來的教學以最常見的開新視窗做說明。

編輯報名資料- 開新視窗

開新視窗有兩個重點

  1. 點下去會開新視窗(這個比較簡單)
  2. 關掉視窗會觸發列表更新

要做的工作

  1. 設計新視窗用Master Page的Dialog.Master
  2. 設計編輯頁Member/Edit.aspx
  3. 管理頁加入開新視窗的指令
  4. 管理頁加入關閉視窗會更新列表的指令

新增Dialog.Master

加入Master

對著專案中的Admin資料夾按右鍵 > 加入 >新增項目

Dialog.Master

選擇 使用主版頁面的Web Form ,名稱輸入Dialog.Master 按下新增

編輯Dialog.Master

Dialog.MasterAdmin.Master差別在於沒有Navbar跟footer,所以可以直接拷貝Admin.Master的內容後貼上,然後刪除Navbar跟footer

拷貝Admin.Master範圍

弄完的結果如下

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Dialog.master.cs" Inherits="PartyRegister.Admin.Dialog" %>

<!DOCTYPE html>

<html lang="zh">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%: Page.Title %></title>

    <asp:PlaceHolder runat="server">
        <%: Scripts.Render("~/bundles/modernizr") %>
    </asp:PlaceHolder>

    <webopt:bundlereference runat="server" path="~/Content/css" />
    <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body>
    <form runat="server">
        <asp:ScriptManager runat="server">
            <Scripts>
                <%--To learn more about bundling scripts in ScriptManager see https://go.microsoft.com/fwlink/?LinkID=301884 --%>
                <%--Framework Scripts--%>
                <asp:ScriptReference Name="MsAjaxBundle" />
                <asp:ScriptReference Name="jquery" />
                <asp:ScriptReference Name="WebForms.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebForms.js" />
                <asp:ScriptReference Name="WebUIValidation.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebUIValidation.js" />
                <asp:ScriptReference Name="MenuStandards.js" Assembly="System.Web" Path="~/Scripts/WebForms/MenuStandards.js" />
                <asp:ScriptReference Name="GridView.js" Assembly="System.Web" Path="~/Scripts/WebForms/GridView.js" />
                <asp:ScriptReference Name="DetailsView.js" Assembly="System.Web" Path="~/Scripts/WebForms/DetailsView.js" />
                <asp:ScriptReference Name="TreeView.js" Assembly="System.Web" Path="~/Scripts/WebForms/TreeView.js" />
                <asp:ScriptReference Name="WebParts.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebParts.js" />
                <asp:ScriptReference Name="Focus.js" Assembly="System.Web" Path="~/Scripts/WebForms/Focus.js" />
                <asp:ScriptReference Name="WebFormsBundle" />
                <%--Site Scripts--%>
            </Scripts>
        </asp:ScriptManager>
        <div class="container body-content">
            <asp:ContentPlaceHolder ID="MainContent" runat="server">
            </asp:ContentPlaceHolder>
        </div>
    </form>
    <asp:PlaceHolder runat="server">
        <%: Scripts.Render("~/Scripts/bootstrap.js") %>
    </asp:PlaceHolder>
</body>
</html>

編輯Dialog.Master.cs

因為這是給後台用的,所以同樣要加入權限驗證

如果覺得權限驗證在開發時候導致每次都要登入令人煩躁,可以之後再加入(不過務必要記得加)

using System;

namespace PartyRegister.Admin
{
    public partial class Dialog : System.Web.UI.MasterPage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //檢查是否登入
            if (Session["User"] == null)
            {
                //導向登入頁(或導向首頁)
                Response.Redirect("~/Admin/Login.aspx");
            }
        }
    }
}

新增Edit.aspx

加入Edit

Member資料夾按右鍵 > 加入> 使用主版頁面的Web Form

檔名輸入Edit.aspx

檔名輸入Edit.aspx

接著Master Page選擇

選擇Dialog.aspx

選Admin > Dialog.Master > 確定

新增完成

新增Edit.aspx完成

Edit頁的內容

Edit.aspx

這邊的內容其實跟先前活動報名的Regist.aspx幾乎一樣,差別在於送出的按鈕變成更新的按鈕,然後多一個取消的按鈕

<%@ Page Title="" Language="C#" MasterPageFile="~/Admin/Dialog.Master" AutoEventWireup="true" CodeBehind="Edit.aspx.cs" Inherits="PartyRegister.Admin.Member.Edit" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <asp:Label ID="lbName" runat="server" Text="姓名"></asp:Label>
    <asp:TextBox ID="tbName" runat="server" required></asp:TextBox>
    <br />
    <br />
    <asp:Label runat="server" ID="lbGender" Text="性別"></asp:Label>
    <asp:RadioButtonList runat="server" ID="rbGender" RepeatDirection="Horizontal">
        <asp:ListItem Text="男" Value="1"></asp:ListItem>
        <asp:ListItem Text="女" Value="2"></asp:ListItem>
        <asp:ListItem Text="其他" Value="3"></asp:ListItem>
    </asp:RadioButtonList>
    <asp:RequiredFieldValidator
        ID="rfvGender"
        runat="server"
        ControlToValidate="rbGender"
        InitialValue=""
        ErrorMessage="請選擇性別"
        ForeColor="Red"
        Display="Dynamic" />
    <br />
    <asp:Label ID="lbPhone" runat="server" Text="聯絡電話"></asp:Label>
    <asp:TextBox ID="tbPhone" runat="server" required></asp:TextBox>
    <br />
    <asp:Label ID="lbBirthday" runat="server" Text="生日"></asp:Label>
    <asp:TextBox ID="tbBirthday" runat="server" required></asp:TextBox>

    <br />
    <asp:Label ID="lbROCID" runat="server" Text="身分證字號"></asp:Label>
    <asp:TextBox ID="tbROCID" runat="server" required></asp:TextBox>
    <br />
    <asp:Label ID="lbEmail" runat="server" Text="信箱"></asp:Label>
    <asp:TextBox ID="tbEmail" runat="server" required></asp:TextBox>
    <br />
    <br />
    <asp:Label runat="server" ID="lbFood" Text="飲食"></asp:Label>
    <asp:RadioButtonList runat="server" ID="rbFood" RepeatDirection="Horizontal">
        <asp:ListItem Text="葷" Value="1"></asp:ListItem>
        <asp:ListItem Text="素" Value="2"></asp:ListItem>
        <asp:ListItem Text="其他" Value="3"></asp:ListItem>
    </asp:RadioButtonList>
    <asp:RequiredFieldValidator
        ID="rfvFood"
        runat="server"
        ControlToValidate="rbFood"
        InitialValue=""
        ErrorMessage="請選擇飲食"
        ForeColor="Red"
        Display="Dynamic" />
    <br />
    <asp:Label runat="server" ID="lbEventId" Text="活動場次"></asp:Label>
    <asp:RadioButtonList runat="server" ID="rbEventId" RepeatDirection="Horizontal">
        <asp:ListItem Text="1月份" Value="1"></asp:ListItem>
        <asp:ListItem Text="2月份" Value="2"></asp:ListItem>
        <asp:ListItem Text="3月份" Value="3"></asp:ListItem>
    </asp:RadioButtonList>
    <asp:RequiredFieldValidator
        ID="rfvEventId"
        runat="server"
        ControlToValidate="rbFood"
        InitialValue=""
        ErrorMessage="請選擇活動場次"
        ForeColor="Red"
        Display="Dynamic" />
    <br />
    <asp:Label runat="server" ID="lbErrorMsg" ForeColor="red"></asp:Label>
    <br />
    <asp:Button ID="btnSubmit" CssClass="btn btn-primary" Text="更新" runat="server" OnClick="btnSubmit_OnClick" />
    <asp:Button ID="btnCancel" CssClass="btn btn-secondary" Text="取消" runat="server" OnClick="btnCancel_OnClick" />
</asp:Content>

Edit.aspx.cs

這邊的內容就跟Regedit.aspx.cs不同了,具體上有下列幾個大項

  1. Page_Load() 頁面開啟時,要讀取網址帶入的id (例如Edit.aspx?id=6 ,會取得6),
  2. BindData() :id讀取後將對應的資料從資料庫取出,並寫入頁面的欄位中。
  3. btnSubmit_OnClick() 更新存檔時:將頁面的資料讀取後更新至資料庫(這邊我略過邏輯規則檢查),並且關閉與呼叫Manage.aspxreloadGrid() 進行更新頁面動作。
  4. btnCancel_OnClick()取消時,直接關閉視窗。
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace PartyRegister.Admin.Member
{
    public partial class Edit : System.Web.UI.Page
    {
        /// <summary>
        /// 資料ID
        /// </summary>
        private string id;

        /// <summary>
        /// 連線字串
        /// </summary>
        private readonly string connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

        protected void Page_Load(object sender, EventArgs e)
        {
            //Get the id from the query string
            id = Request.QueryString["id"];
            if (string.IsNullOrEmpty(id))
            {
                //alert message and close the window
                Response.Write("<script>alert('查無相關資料');window.close();</script>");
                Response.End();
            }
            if (!IsPostBack)
            {
                BindData();
            }
        }

        /// <summary>
        /// 讀取表單資料綁定至物件
        /// </summary>
        private void BindData()
        {
            string sql = @"SELECT Top 1 * FROM [Member] WHERE ID = @id";
            var dt = new DataTable();
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();
                //read data
                using (var comm = new SqlCommand(sql,conn))
                using (var reader = new SqlDataAdapter(comm))
                {
                    reader.SelectCommand.Parameters.AddWithValue("@id", id);
                    reader.Fill(dt);
                    //Bind data to the form
                    if (dt.Rows.Count > 0)
                    {
                        tbName.Text = dt.Rows[0]["Name"].ToString();
                        tbBirthday.Text = dt.Rows[0]["Birthday"].ToString();
                        tbEmail.Text = dt.Rows[0]["Email"].ToString();
                        tbPhone.Text = dt.Rows[0]["Phone"].ToString();
                        tbROCID.Text = dt.Rows[0]["ROCID"].ToString();
                        rbEventId.SelectedValue = dt.Rows[0]["EventId"].ToString();
                        rbGender.SelectedValue = dt.Rows[0]["Gender"].ToString();
                        rbFood.SelectedValue = dt.Rows[0]["Food"].ToString();
                    }
                    else
                    {
                        //alert message and close the window
                        Response.Write("<script>alert('查無相關資料');window.close();</script>");
                        Response.End();
                    }
                }
            }
        }

        protected void btnSubmit_OnClick(object sender, EventArgs e)
        {
            //update data
            string sql = @"
                UPDATE [Member] 
                SET 
                      [Name] = @Name
                      ,[Gender] = @Gender
                      ,[Birthday] = @Birthday
                      ,[ROCID] = @ROCID
                      ,[Email] = @Email
                      ,[Phone] = @Phone
                      ,[Food] = @Food
                      ,[EventID] = @EventID
                WHERE ID=@id";

            try
            {
                using (var conn = new SqlConnection(connectionString))
                {
                    conn.Open();
                    using (var comm = new SqlCommand(sql, conn))
                    {
                        comm.Parameters.AddWithValue("@Name", tbName.Text);
                        comm.Parameters.AddWithValue("@Gender", rbGender.SelectedValue);
                        comm.Parameters.AddWithValue("@Birthday", tbBirthday.Text);
                        comm.Parameters.AddWithValue("@ROCID", tbROCID.Text);
                        comm.Parameters.AddWithValue("@Email", tbEmail.Text);
                        comm.Parameters.AddWithValue("@Phone", tbPhone.Text);
                        comm.Parameters.AddWithValue("@Food", rbFood.SelectedValue);
                        comm.Parameters.AddWithValue("@EventID", rbEventId.SelectedValue);
                        comm.Parameters.AddWithValue("@id", id);
                        comm.ExecuteNonQuery();
                    }
                    //alert message 更新成功 並且呼叫父頁面的js reloadGrid()
                    Response.Write("<script>alert('更新成功'); window.opener.reloadGrid(); window.close();</script>");
                    Response.End();


                }
            }
            catch (Exception exception)
            {
                //alert message 更新失敗
                Response.Write("<script>alert('更新失敗');</script>");
                Response.End();
                Console.WriteLine(exception);
                throw;
            }
            
        }

        protected void btnCancel_OnClick(object sender, EventArgs e)
        {
            //close the window
            Response.Write("<script>window.close();</script>");
            Response.End();
        }
    }
}

Manage.aspx跟Edite.aspx互動

這邊流程就比較複雜,坑也不少。

在這邊描述需求步驟

  1. 使用者按下Manage.aspx的編輯,系統會開新視窗Edit.aspx
  2. 使用者在Edit.aspx修改好資料後 按下更新,Edit.aspx這個視窗會
    • 存檔
    • 關閉+呼叫Manage.aspx 更新列表

完整的Manager.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">
    <script>
        var btnRebindId = '<%= btnRebind.UniqueID %>';
        function reloadGrid() {
            __doPostBack(btnRebindId, '');
        }
        function OpenDialog(url, width, height, showMenu, canResize, showScrollbars) {
            var childWindow = window.open(url, "", "width=" + width + ",height=" + height + ",menubar=" + (showMenu ? "1" : "0") + ",scrollbars=" + (showScrollbars ? "1" : "0") + ",resizable=" + (canResize ? "1" : "0"));
            if (childWindow) {
                childWindow.resizeTo(width, height);
                
            }
        }

    </script>
    <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:Button ID="btnRebind" runat="server" OnClick="btnRebind_OnClick" CssClass="d-none" />

    <asp:UpdatePanel ID="upGrid" runat="server">
        <ContentTemplate>
    <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" 
                        Text="編輯" 
                        CssClass="text-primary"
                        OnClientClick='<%# string.Format("OpenDialog(\"Edit.aspx?id={0}\",850,625,false,false,true); return false;", Eval("ID")) %>'>
                    </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>
        </ContentTemplate>
    </asp:UpdatePanel>
</asp:Content>

完整的Manager.aspx.cs

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web.UI.WebControls;

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();
        }

        /// <summary>
        /// GridView 按下編輯或刪除按鈕
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void gvList_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            if (e.CommandName == "DeleteData")
            {
                //刪除選定資料
                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();
            }
            
        }

        protected void btnRebind_OnClick(object sender, EventArgs e)
        {
            BindData();
        }
    }
}

0 Comments

Submit a Comment

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