GridView 雖內建分頁與排序,但實務專案常需「自訂分頁 UI」(首頁、上一頁、下一頁、末頁、下拉選頁數)、「多欄位排序切換」、「匯出 Excel」、「列滑鼠變色/刪除確認」、「空資料顯示表頭」等進階需求。本文整理完整實作模式,可直接套用於專案。
<asp:Label ID="lblTotalPages" runat="server" Text="總頁數:" />
<asp:Label ID="lblPageCount" runat="server" />
<asp:Label ID="lblCurrent" runat="server" Text="當前頁:" />
<asp:Label ID="lblPage" runat="server" Text="1" />
<asp:LinkButton ID="lnkFirst" runat="server" Text="|<" OnClick="lnkFirst_Click" />
<asp:LinkButton ID="lnkPrev" runat="server" Text="<" OnClick="lnkPrev_Click" />
<asp:LinkButton ID="lnkNext" runat="server" Text=">" OnClick="lnkNext_Click" />
<asp:LinkButton ID="lnkLast" runat="server" Text=">|" OnClick="lnkLast_Click" />
<asp:DropDownList ID="ddlPageSize" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlPageSize_SelectedIndexChanged">
<asp:ListItem Text="10" Value="10" />
<asp:ListItem Text="15" Value="15" />
<asp:ListItem Text="20" Value="20" />
<asp:ListItem Text="30" Value="30" />
</asp:DropDownList>
<asp:Label Text="筆/頁" />
PagedDataSource 實作private void BindGrid(int personId)
{
DataTable dt = GetDataByPersonId(personId); // 取得完整資料表
if (dt.Rows.Count > 0)
{
PagedDataSource pds = new PagedDataSource();
pds.DataSource = dt.DefaultView;
pds.AllowPaging = true;
pds.PageSize = Convert.ToInt32(ddlPageSize.SelectedValue);
pds.CurrentPageIndex = CurrentPage - 1; // CurrentPage 為頁面屬性,預設 1
lblPageCount.Text = pds.PageCount.ToString();
lblPage.Text = CurrentPage.ToString();
// 控制導航按鈕啟用狀態
lnkFirst.Enabled = lnkPrev.Enabled = (CurrentPage > 1);
lnkNext.Enabled = lnkLast.Enabled = (CurrentPage < pds.PageCount);
GridView1.DataSource = pds;
GridView1.DataBind();
}
else
{
ShowEmptyGridWithHeader(); // 空資料處理見第六節
}
}
// 頁碼屬性儲存在 ViewState
private int CurrentPage
{
get { return ViewState["CurrentPage"] != null ? (int)ViewState["CurrentPage"] : 1; }
set { ViewState["CurrentPage"] = value; }
}
// 導航按鈕事件
protected void lnkFirst_Click(object sender, EventArgs e) { CurrentPage = 1; BindGrid(personId); }
protected void lnkPrev_Click(object sender, EventArgs e) { CurrentPage--; BindGrid(personId); }
protected void lnkNext_Click(object sender, EventArgs e) { CurrentPage++; BindGrid(personId); }
protected void lnkLast_Click(object sender, EventArgs e) { CurrentPage = Convert.ToInt32(lblPageCount.Text); BindGrid(personId); }
protected void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e) { CurrentPage = 1; BindGrid(personId); }
<asp:GridView ID="GridView1" runat="server" AllowSorting="true" OnSorting="GridView1_Sorting" ...>
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
string sortExpr = e.SortExpression;
string sortDir = ViewState["SortDirection"] as string ?? "ASC";
if (ViewState["SortExpression"] as string == sortExpr)
sortDir = (sortDir == "ASC") ? "DESC" : "ASC"; // 同欄位切換方向
ViewState["SortExpression"] = sortExpr;
ViewState["SortDirection"] = sortDir;
BindGrid(personId); // 重新繫結時套用排序
}
// 繫結前套用 DataView.Sort
private DataView GetSortedView(DataTable dt)
{
DataView dv = dt.DefaultView;
if (ViewState["SortExpression"] != null)
dv.Sort = ViewState["SortExpression"] + " " + ViewState["SortDirection"];
return dv;
}
protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindGrid(personId); // 同一繫結方法
}
<asp:GridView ...>
<Columns>
<asp:CommandField ShowSelectButton="True" SelectText="查看" />
...
</Columns>
<SelectedRowStyle BackColor="AliceBlue" ForeColor="Gray" />
</asp:GridView>
// RowDataBound 註冊客戶端點擊選取(可選)
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
e.Row.Attributes["onclick"] = Page.ClientScript.GetPostBackClientHyperlink(GridView1, "Select$" + e.Row.RowIndex);
}
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
GridViewRow row = GridView1.SelectedRow;
int id = Convert.ToInt32(row.Cells[1].Text); // 或用 DataKeys
Response.Redirect("Detail.aspx?ID=" + id);
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// 滑鼠進入/離開變色
e.Row.Attributes["onmouseover"] = "this.style.backgroundColor='#00A9FF';";
e.Row.Attributes["onmouseout"] = "this.style.backgroundColor='#E6F5FA';";
// 刪除按鈕確認對話框(假設第 2 儲存格為 ImageButton)
ImageButton btnDel = (ImageButton)e.Row.Cells[2].FindControl("IBtnDelete");
if (btnDel != null)
btnDel.Attributes["onclick"] = "return confirm('確定刪除「" + e.Row.Cells[0].Text + "」嗎?');";
// 條件格式:特定值標紅
if (e.Row.Cells[1].Text == "444")
e.Row.Cells[1].BackColor = System.Drawing.Color.Red;
}
}
private void ShowEmptyGridWithHeader()
{
DataTable dt = GetDataByPersonId(personId); // 可能為空
if (dt.Rows.Count == 0)
{
dt.Rows.Add(dt.NewRow()); // 加入空白行以產生表頭
GridView1.DataSource = dt;
GridView1.DataBind();
// 合併儲存格顯示提示
int colCount = GridView1.Rows[0].Cells.Count;
GridView1.Rows[0].Cells.Clear();
GridView1.Rows[0].Cells.Add(new TableCell());
GridView1.Rows[0].Cells[0].ColumnSpan = colCount;
GridView1.Rows[0].Cells[0].Text = "查無資料";
GridView1.Rows[0].Cells[0].Style.Value = "text-align:center; color:gray;";
}
else
{
GridView1.DataSource = dt;
GridView1.DataBind();
}
}
// 或直接使用 GridView 屬性(僅顯示文字,無表頭)
// GridView1.EmptyDataText = "查無資料";
EnableEventValidation="false")public override void VerifyRenderingInServerForm(Control control) { } // 必須覆寫
protected void btnExport_Click(object sender, EventArgs e)
{
Response.Clear();
Response.Buffer = true;
Response.Charset = "UTF-8";
Response.AppendHeader("Content-Disposition", "attachment;filename=Export.xls");
Response.ContentEncoding = System.Text.Encoding.UTF8; // 避免中文亂碼
Response.ContentType = "application/ms-excel";
using (StringWriter sw = new StringWriter())
using (HtmlTextWriter htw = new HtmlTextWriter(sw))
{
GridView1.RenderControl(htw);
Response.Output.Write(sw.ToString());
}
Response.Flush();
Response.End();
}
注意:匯出前請暫時關閉分頁(GridView1.AllowPaging = false; BindGrid();),否則僅匯出當頁資料。
<script type="text/javascript">
var tooltip = document.createElement('div');
tooltip.id = 'gvTooltip';
tooltip.style.cssText = 'position:absolute;display:none;background:#fff;border:1px solid #333;padding:8px;z-index:999;box-shadow:2px 2px 6px rgba(0,0,0,.2);';
document.body.appendChild(tooltip);
function showTooltip(id, detail) {
tooltip.innerHTML = 'ID: ' + id + '
詳情: ' + detail;
tooltip.style.display = 'block';
tooltip.style.left = (event.clientX + 10) + 'px';
tooltip.style.top = (event.clientY + 10) + 'px';
}
function hideTooltip() { tooltip.style.display = 'none'; }
</script>
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string id = e.Row.Cells[0].Text;
string detail = e.Row.Cells[1].Text;
e.Row.Attributes["onmouseover"] = $"showTooltip('{id}', '{detail}');";
e.Row.Attributes["onmouseout"] = "hideTooltip();";
}
}
protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
GridView1.EditIndex = e.NewEditIndex;
BindGrid(personId);
}
protected void GridView1_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
GridView1.EditIndex = -1;
BindGrid(personId);
}
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
GridViewRow row = GridView1.Rows[e.RowIndex];
int id = Convert.ToInt32(GridView1.DataKeys[e.RowIndex].Value);
string newVal = ((TextBox)row.Cells[1].Controls[0]).Text.Trim();
// UpdateDatabase(id, newVal);
GridView1.EditIndex = -1;
BindGrid(personId);
}
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
int id = Convert.ToInt32(GridView1.DataKeys[e.RowIndex].Value);
// DeleteDatabase(id);
BindGrid(personId);
}
EmptyDataText。