news 2026/10/21 19:41:02

ASP.NET GridView无刷新实现:UpdatePanel与AJAX局部更新实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ASP.NET GridView无刷新实现:UpdatePanel与AJAX局部更新实战

简介:这份资源是一套面向 ASP.NET 开发者的 GridView 实用实例,基于 ASP.NET 4.0 与 SQL Server 2008 环境,针对 Visual Studio 自带 GridView 控件在信息管理系统中操作繁琐、体验欠佳的问题,提供了一套可移植性强的替代方案。实例虽结构简洁,却完整实现了数据的增、删、改功能,并借助 Ajax 技术达成无刷新交互,适合正在开发信息系统、需要快速搭建数据列表管理模块的中初级开发者参考借鉴。压缩包共包含 2 个文件,以 htm 说明页与 rar 源码包为主,整体约 1.52MB,体积轻巧便于下载与本地部署。目前已有 650 人学习关注,说明其在同类需求中具备一定参考价值。读者可从中获取可直接运行的 GridView 操作范例、Ajax 无刷新增删改的实现思路以及可迁移到自身项目的代码结构,便于对照调试与二次开发,适合收藏备用。

1. 无刷新 GridView 到底解决了什么体验问题

做过 ASP.NET WebForms 的人对 GridView 大概都有一种复杂感情:拖控件、配数据源、勾选分页排序,十分钟就能出一个能用的列表页。但真到用户手里,问题立刻暴露——点一下「下一页」,整页白屏闪一下;改一个下拉框筛选,页面从头加载到尾;编辑一行数据,滚动条跳回顶部。用户嘴上不说,心里已经给这个系统打了「卡、旧、难用」的标签。

所谓「非常好用的 GridView(ASP.NET,AJAX 效果)」,核心不是换一个更炫的控件,而是把 GridView 的**回发(PostBack)**从「整页刷新」改造成「局部更新」。做法上,常见路线有三条:一是UpdatePanel把 GridView 包起来做局部回发;二是用GridView配合ScriptManager的 PageMethods 或 WebMethod 走纯 AJAX 拿 JSON 自己渲染;三是干脆用ListView/Repeater加前端模板。三条路各有代价,本文按「先跑通 UpdatePanel 最小闭环,再逐步替换成可控的 AJAX 数据通道」的顺序讲,适合正在维护老 WebForms 项目、又不想推倒重来的开发者。

2. 用 UpdatePanel 跑通第一个无刷新 GridView

2.1 最小可运行页面结构

先别急着上复杂架构,把「点分页不闪屏」这件事跑通,是判断这条路值不值得走下去的第一步。下面是一个最小.aspx页面,GridView 放在UpdatePanel里,分页和排序都走局部回发。

<%-- Default.aspx --%> <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GridDemo.Default" %> <!DOCTYPE html> <html> <head runat="server"> <title>无刷新 GridView 演示</title> </head> <body> <form id="form1" runat="server"> <%-- ScriptManager 必须存在,否则 UpdatePanel 退化为整页回发 --%> <asp:ScriptManager ID="sm" runat="server" /> <%-- UpdateMode=Conditional 只刷新本面板,避免联动刷新 --%> <asp:UpdatePanel ID="upGrid" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true"> <ContentTemplate> <asp:GridView ID="gvData" runat="server" AutoGenerateColumns="false" AllowPaging="true" PageSize="10" AllowSorting="true" OnPageIndexChanging="gvData_PageIndexChanging" OnSorting="gvData_Sorting" OnRowEditing="gvData_RowEditing" OnRowUpdating="gvData_RowUpdating" OnRowCancelingEdit="gvData_RowCancelingEdit" DataKeyNames="Id"> <Columns> <asp:BoundField DataField="Id" HeaderText="编号" ReadOnly="true" /> <asp:BoundField DataField="Name" HeaderText="名称" SortExpression="Name" /> <asp:BoundField DataField="Amount" HeaderText="数量" SortExpression="Amount" /> <asp:CommandField ShowEditButton="true" /> </Columns> </asp:GridView> </ContentTemplate> </asp:UpdatePanel> </form> </body> </html>

这段标记里有三个关键点。第一,ScriptManager是UpdatePanel生效的前提,它负责注入客户端脚本、接管异步回发;漏掉它,页面照样能跑,但每次操作都是整页刷新,你会误以为「AJAX 没生效」。第二,UpdateMode="Conditional"表示只有本面板内的触发器才刷新它,多个面板并存时不会互相牵连。第三,ChildrenAsTriggers="true"让面板内子控件的回发自动成为刷新触发器,分页、排序、编辑按钮都算。

2.2 后台事件里必须补的那几行

前端标记只是壳,真正决定体验的是后台事件处理。GridView 的分页和排序默认会触发回发,但如果你不在事件里重新绑定数据,页面就会「翻页后空白」——这是新手最常见的翻车点。

// Default.aspx.cs using System; using System.Data; using System.Web.UI.WebControls; namespace GridDemo { public partial class Default : System.Web.UI.Page { // 用 ViewState 暂存排序方向,避免每次回发丢失状态 private string SortDirection { get { return ViewState["SortDir"] as string ?? "ASC"; } set { ViewState["SortDir"] = value; } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindGrid(); } } private void BindGrid() { // 模拟数据源,实际项目替换为业务层查询 DataTable dt = GetMockData(); DataView dv = dt.DefaultView; if (ViewState["SortExpr"] != null) { dv.Sort = ViewState["SortExpr"] + " " + SortDirection; } gvData.DataSource = dv; gvData.DataBind(); } protected void gvData_PageIndexChanging(object sender, GridViewPageEventArgs e) { // 先改页码,再重新绑定,顺序反了会翻页无效 gvData.PageIndex = e.NewPageIndex; BindGrid(); } protected void gvData_Sorting(object sender, GridViewSortEventArgs e) { ViewState["SortExpr"] = e.SortExpression; SortDirection = SortDirection == "ASC" ? "DESC" : "ASC"; BindGrid(); } protected void gvData_RowEditing(object sender, GridViewEditEventArgs e) { gvData.EditIndex = e.NewEditIndex; BindGrid(); } protected void gvData_RowCancelingEdit(object sender, EventArgs e) { gvData.EditIndex = -1; BindGrid(); } protected void gvData_RowUpdating(object sender, GridViewUpdateEventArgs e) { // 取主键和编辑后的值,写回数据源 int id = Convert.ToInt32(gvData.DataKeys[e.RowIndex].Value); string name = ((TextBox)gvData.Rows[e.RowIndex] .Cells[1].Controls[0]).Text; // 实际项目在此调用业务层更新 gvData.EditIndex = -1; BindGrid(); } private DataTable GetMockData() { DataTable dt = new DataTable(); dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("Name", typeof(string)); dt.Columns.Add("Amount", typeof(int)); for (int i = 1; i <= 53; i++) { dt.Rows.Add(i, "项目" + i, i * 10); } return dt; } } }

逻辑上要盯住两件事。其一,Page_Load里用!IsPostBack包住首次绑定,否则每次异步回发都会重置PageIndex和EditIndex,表现为「翻页永远停在第一页」「编辑框刚打开就关闭」。其二,PageIndexChanging里必须先赋值PageIndex再BindGrid(),顺序颠倒的话新页码不会生效。参数方面,PageSize建议控制在 10 到 20 之间,太大虽然减少翻页次数,但每次局部回发传输的 ViewState 会明显膨胀,移动端尤其明显。

2.3 让 UpdatePanel 只更新它自己

多个UpdatePanel共存时,默认行为可能让无关面板一起刷新,白白浪费一次往返。控制手段有两个:把UpdateMode设为Conditional,以及显式声明Triggers。

<asp:UpdatePanel ID="upFilter" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:DropDownList ID="ddlType" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlType_SelectedIndexChanged" /> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="ddlType" EventName="SelectedIndexChanged" /> </Triggers> </asp:UpdatePanel>

AsyncPostBackTrigger表示这个事件走异步回发,只刷新所属面板。如果筛选面板和列表面板是分开的,就在筛选事件里调用upGrid.Update()手动触发列表刷新,这样能精确控制刷新范围,而不是让整个页面区域一起抖动。

3. 从 UpdatePanel 迁移到纯 AJAX 数据通道

3.1 为什么 UpdatePanel 迟早会不够用

UpdatePanel的本质仍是「把整页的 ViewState 和部分 HTML 打包回传」,它省掉的是浏览器白屏,不是网络传输量。一个 20 列的 GridView,ViewState 动辄几十 KB,翻一页就传一次,用户在网络差的环境下依然会觉得慢。更麻烦的是,它和前端框架、自定义 JS 交互很难配合,你想在行上挂个右键菜单、做个拖拽排序,会发现事件被ScriptManager接管得七零八落。

所以当列表需要「无限滚动」「前端二次筛选」「和图表联动」时,正确做法是把数据通道换成返回 JSON 的 WebMethod,前端自己渲染表格。GridView 可以保留作为服务端渲染的降级方案,也可以彻底换成Repeater加模板。

3.2 用 WebMethod 返回 JSON 数据

在.aspx.cs里加一个静态的WebMethod,配合ScriptManager的EnablePageMethods,前端就能直接调用。

// 在页面类中新增,需引用 System.Web.Services [System.Web.Services.WebMethod] public static object GetPagedData(int pageIndex, int pageSize, string sortExpr) { // 模拟分页查询,实际项目替换为业务层 DataTable dt = GetMockDataStatic(); DataView dv = dt.DefaultView; if (!string.IsNullOrEmpty(sortExpr)) { dv.Sort = sortExpr; } var rows = new System.Collections.Generic.List<object>(); int start = pageIndex * pageSize; int end = Math.Min(start + pageSize, dv.Count); for (int i = start; i < end; i++) { var row = dv[i]; rows.Add(new { Id = row["Id"], Name = row["Name"], Amount = row["Amount"] }); } return new { Total = dv.Count, Rows = rows }; }

前端调用时,ScriptManager需要开启EnablePageMethods="true",然后:

// 前端分页请求,替换 GridView 的默认回发 function loadPage(pageIndex) { PageMethods.GetPagedData(pageIndex, 10, "Name ASC", function (result) { renderTable(result.Rows); renderPager(result.Total, pageIndex); }, function (err) { // 失败时给出可见提示,别让用户对着空白表格发呆 console.error("加载失败:" + err.get_message()); }); }

这里的关键差异是:服务端只返回数据,不返回 HTML 和 ViewState,传输量通常能降到原来的三分之一以下。参数上,pageIndex从 0 开始还是从 1 开始要和后端约定死,混用是分页错位的经典原因。sortExpr建议只允许白名单字段,直接拼接到DataView.Sort上虽然不会 SQL 注入,但非法字段名会抛异常,前端要能兜住。

3.3 前端渲染与事件委托

拿到 JSON 后自己拼表格,比想象中简单,但要注意事件绑定方式。行内按钮如果用onclick逐个绑定,翻页后新行会丢失事件,正确做法是用事件委托。

function renderTable(rows) { var html = rows.map(function (r) { return '<tr>string cacheKey = string.Format("grid_{0}_{1}_{2}", pageIndex, pageSize, sortExpr); var cached = HttpRuntime.Cache[cacheKey]; if (cached != null) return cached; // ...查询逻辑... HttpRuntime.Cache.Insert(cacheKey, result, null, DateTime.Now.AddSeconds(30), System.Web.Caching.Cache.NoSlidingExpiration);

第二个习惯是给所有异步请求加统一的失败处理。局部刷新最大的隐患是「失败了但用户看不出来」,表格停在旧数据上,用户以为操作成功了。做法是在前端封装一个请求函数,超时和异常都弹出可见提示,并在服务端WebMethod里用try/catch包住,返回结构化的错误信息而不是直接抛 500。

第三个习惯是控制单页数据量。GridView 再怎么做无刷新,一次渲染几千行 DOM 也会卡。分页大小建议不超过 50,需要展示大量数据时改用滚动加载,每次追加一小批。判断标准很简单:在目标用户的低配设备上滚动列表,如果出现明显掉帧,就该减小批次。

我自己维护这类页面时,最深的教训是「先量再改」——不要凭感觉判断是 ViewState 的锅还是数据库的锅,打开开发者工具看请求体大小和响应时间,数据会告诉你瓶颈在哪。把 UpdatePanel 当过渡、把 JSON 通道当目标,中间用缓存和错误处理兜底,这套组合在多数 WebForms 老项目里都能平稳落地。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 19:40:35

Python+Vue健身房管理系统实战:Django与Flask选型及部署全记录

前段时间帮朋友做了一套健身房管理系统&#xff0c;技术栈锁定在 Python Vue&#xff0c;后端用的 Django&#xff0c;IDE 用的是 Pycharm&#xff0c;中间也仔细对比过 Flask。整套系统从会员建档、课程表安排、私教预约到月末营收报表&#xff0c;全部跑通上线&#xff0c;现…

作者头像 李华
网站建设 2026/10/19 11:59:54

sqldeveloper-4.0.3.16.84-x64 安装配置与避坑指南

简介&#xff1a;Oracle SQL Developer 4.0.3.16.84-x64 是 Oracle 官方推出的免费数据库管理工具&#xff0c;面向开发人员与数据库管理员&#xff0c;尤其适合刚接触 Oracle 的新手快速上手。它提供图形化集成环境&#xff0c;可完成数据库连接配置、SQL 编写与执行、表与视图…

作者头像 李华
网站建设 2026/10/16 7:21:47

Claude Code安装配置与DeepSeek接入实战:终端AI编程工具完全指南

国内搞开发的同行&#xff0c;最近应该没少刷到Claude Code。这是Anthropic官方的终端AI编程工具&#xff0c;不是IDE插件&#xff0c;也不是普通聊天窗口&#xff0c;它直接在命令行里跟你协作&#xff0c;能读项目、改代码、跑测试、提交git&#xff0c;一套指令下来把活干完…

作者头像 李华
网站建设 2026/10/15 15:09:08

在VSCode中用huggingface_hub高效下载Hugging Face数据集

第一次在VSCode里和Hugging Face打交道的人&#xff0c;十有八九都是从“浏览器打开huggingface.co&#xff0c;找到数据集页面&#xff0c;然后一个个点文件下载”开始的。我最早也这样&#xff0c;下COCO 2017的时候点了二十来个压缩包&#xff0c;中途网络断了一次&#xff…

作者头像 李华
网站建设 2026/10/16 11:46:20

Java定时任务上云:从Spring @Scheduled到K8s CronJob与EventBridge的迁移指南

先说个现象&#xff1a;很多团队的Java定时任务从单体架构一路演进到微服务、容器化之后&#xff0c;定时任务这块反而成了“历史遗留”。有人继续在应用进程里用SpringScheduled&#xff0c;有人把任务塞进K8s的CronJob&#xff0c;还有人想用AWS EventBridge这类事件驱动服务…

作者头像 李华
网站建设 2026/10/16 14:30:42

Cursor Agent工作流:重构软件开发全生命周期

1. 项目概述&#xff1a;当写代码不再是开发终点&#xff0c;而只是Agent工作流的起点“写代码只是第一步”——这句话放在五年前可能被当成玩笑&#xff0c;放在今天&#xff0c;它正被越来越多一线开发者当作日常事实来接受。我接触Cursor这个工具是在去年底一个内部技术分享…

作者头像 李华