ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ASP.NET WebForms GridView AJAX 实战:六步实现行内编辑与Excel导出

ASP.NET WebForms GridView AJAX 实战:六步实现行内编辑与Excel导出 简介这是一份面向ASP.NET Web开发初学者与中级工程师的实用GridView增强组件专为解决VS默认GridView控件在增删改操作中体验差、缺乏Ajax交互等问题而设计。资源基于ASP.NET 4.0 SQL Server 2008环境构建支持无刷新数据操作代码简洁、移植性强适用于信息系统快速开发与教学实践场景。压缩包共2个文件1个HTML说明页 1个RAR源码包总大小1.52MBHTML页提供使用引导与效果预览RAR包内含完整可运行的GridView增删改逻辑代码及配套SQL脚本结构清晰、开箱即用。目前已有650人学习下载读者可直接复用核心Ajax交互逻辑、掌握服务端数据绑定与客户端事件协同机制并借鉴其轻量级无框架实现思路快速提升Web表格控件的工程化应用能力。1. 为什么说“非常好用的 GridViewASP.NET AJAX 效果”不是营销话术而是真实可落地的生产力提升点你有没有在 ASP.NET WebForms 项目里改过十次分页逻辑有没有因为点击“编辑”后整个页面闪一下、状态丢失、JS 绑定失效而默默注释掉UpdatePanel有没有被产品经理指着表格说“这个列表要支持行内编辑、实时保存、带加载动画、还要能导出 Excel”然后盯着你等一个不刷新页面的方案——这时候“非常好用的 GridViewASP.NET AJAX 效果”就不是一句空泛夸奖而是一个明确的技术信号它指向一种在 WebForms 框架约束下用最小侵入方式实现现代交互体验的成熟路径。它不等于抛弃 WebForms也不等于硬套 Vue/React而是利用GridView原生事件模型 ScriptManager 精准的UpdatePanel划分 客户端脚本协同把“服务器控件”的稳定性和“AJAX 的流畅感”真正焊死在一起。适合正在维护中大型 WebForms 遗留系统、又面临 UX 升级压力的工程师——你不需要重写前端但必须让老控件“活”起来。本文不讲原理图或概念对比只讲我在线上系统里跑通、压测过、被 QA 连续点了 200 次没翻车的六步落地法从最简 AJAX 包裹开始到行内编辑防重复提交再到导出不阻塞 UI每一步都附可直接粘贴的代码块、必调参数说明、以及三个我亲手踩穿的坑。2. 用 ScriptManager UpdatePanel 实现 GridView 最小化 AJAX 封装三行配置定生死ASP.NET WebForms 的 AJAX 支持不是靠 JS 库堆出来的而是靠服务端控件生命周期与ScriptManager的深度耦合。很多人一上来就写 jQuery AJAX 调后台方法结果发现ViewState乱了、__EVENTVALIDATION校验失败、或者GridView的RowCommand根本不触发——问题不在 JS而在没走对 WebForms 的 AJAX 正确通道。核心就一条所有需要局部刷新的 GridView 及其关联按钮必须包裹在同一个UpdatePanel内且该UpdatePanel必须由页面级ScriptManager驱动。2.1 页面级 ScriptManager 是前提且只能有一个% Page LanguageC# AutoEventWireuptrue CodeBehindDefault.aspx.cs InheritsWebApp.Default % !DOCTYPE html html head runatserver titleGridView AJAX 示例/title /head body form idform1 runatserver !-- ScriptManager 必须放在 form 内且只能有一个 -- asp:ScriptManager IDScriptManager1 runatserver EnablePartialRenderingtrue / !-- 后续所有 UpdatePanel 都依赖它 --提示EnablePartialRenderingtrue是开关默认为 true但显式写出是血泪经验——某次部署到测试环境因 IIS 版本差异导致默认值被覆盖整个 AJAX 失效查了两天才发现这行漏了。2.2 UpdatePanel 封装 GridView 与操作按钮范围划定决定性能边界asp:UpdatePanel IDupGridView runatserver UpdateModeConditional ContentTemplate asp:GridView IDgvData runatserver AutoGenerateColumnsfalse OnRowCommandgvData_RowCommand OnPageIndexChanginggvData_PageIndexChanging OnRowEditinggvData_RowEditing OnRowCancelingEditgvData_RowCancelingEdit OnRowUpdatinggvData_RowUpdating DataKeyNamesID Columns asp:BoundField DataFieldName HeaderText姓名 / asp:BoundField DataFieldEmail HeaderText邮箱 / asp:CommandField ShowEditButtontrue ShowDeleteButtontrue / /Columns /asp:GridView asp:Label IDlblStatus runatserver Text CssClassstatus-label / !-- 所有触发 GridView 更新的操作按钮必须放在这里 -- asp:Button IDbtnRefresh runatserver Text刷新数据 OnClickbtnRefresh_Click / asp:Button IDbtnExport runatserver Text导出 Excel OnClickbtnExport_Click / /ContentTemplate Triggers !-- 显式声明异步触发器避免隐式全量刷新 -- asp:AsyncPostBackTrigger ControlIDbtnRefresh EventNameClick / asp:AsyncPostBackTrigger ControlIDbtnExport EventNameClick / asp:AsyncPostBackTrigger ControlIDgvData EventNameRowCommand / asp:AsyncPostBackTrigger ControlIDgvData EventNamePageIndexChanging / asp:AsyncPostBackTrigger ControlIDgvData EventNameRowEditing / asp:AsyncPostBackTrigger ControlIDgvData EventNameRowCancelingEdit / asp:AsyncPostBackTrigger ControlIDgvData EventNameRowUpdating / /Triggers /asp:UpdatePanel /form /body /html关键参数说明UpdateModeConditional这是性能命门。设为Always会导致每次任何UpdatePanel刷新都连带刷新它极易引发连锁反应。Conditional表示只响应自己Triggers里定义的事件。Triggers块必须显式列出所有可能触发刷新的事件。GridView的RowCommand是复合事件编辑、删除、自定义命令都走它所以必须单独加一条分页、编辑、取消编辑、更新都是独立事件各加一条。漏掉任意一条对应操作就会整页刷新。DataKeyNamesID行操作时获取主键的唯一安全方式。不要用Cells[0].Text那是玄学DataKeys[e.RowIndex].Value才是正解。2.3 后台代码保持事件处理逻辑不变但需适配 AJAX 上下文// Default.aspx.cs protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) // 首次加载才绑定 { BindGridView(); } } private void BindGridView() { // 示例从数据库取数据实际应走仓储层 var data GetSampleData(); gvData.DataSource data; gvData.DataBind(); } protected void btnRefresh_Click(object sender, EventArgs e) { BindGridView(); // 刷新即重新绑定 lblStatus.Text 数据已刷新; } protected void btnExport_Click(object sender, EventArgs e) { // 导出逻辑见第 5 章此处先占位 ExportToExcel(); } protected void gvData_RowCommand(object sender, GridViewCommandEventArgs e) { int rowIndex Convert.ToInt32(e.CommandArgument); int id Convert.ToInt32(gvData.DataKeys[rowIndex].Value); switch (e.CommandName) { case Delete: DeleteRecord(id); BindGridView(); lblStatus.Text $ID {id} 已删除; break; case CustomAction: // 自定义按钮逻辑 break; } } protected void gvData_PageIndexChanging(object sender, GridViewPageEventArgs e) { gvData.PageIndex e.NewPageIndex; BindGridView(); } protected void gvData_RowEditing(object sender, GridViewEditEventArgs e) { gvData.EditIndex e.NewEditIndex; BindGridView(); } protected void gvData_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e) { gvData.EditIndex -1; BindGridView(); } protected void gvData_RowUpdating(object sender, GridViewUpdateEventArgs e) { int id Convert.ToInt32(gvData.DataKeys[e.RowIndex].Value); string name ((TextBox)gvData.Rows[e.RowIndex].FindControl(txtName)).Text; string email ((TextBox)gvData.Rows[e.RowIndex].FindControl(txtEmail)).Text; UpdateRecord(id, name, email); gvData.EditIndex -1; BindGridView(); lblStatus.Text $ID {id} 已更新; }逻辑说明Page_Load中!IsPostBack判断是铁律否则每次 AJAX 回发都会重新绑定覆盖用户编辑状态。BindGridView()被多个事件调用但每次都是干净的全量重绑——这是 WebForms AJAX 的设计哲学用服务端状态一致性换客户端简单性。RowUpdating中通过FindControl获取编辑态 TextBox这是GridView编辑模板的标准写法与 AJAX 无关但必须确保模板里控件 ID 正确见第 3 章。3. 让 GridView 支持行内编辑模板列 动态控件 防重复提交的三重加固原生GridView的编辑功能在 AJAX 下会“失灵”典型表现是点击编辑后文本框出现但点击更新时RowUpdating不触发或触发后取不到新值。根本原因在于UpdatePanel的局部刷新机制与GridView模板列中动态控件的生命周期不同步。解决方案不是绕开模板而是用TemplateField显式定义编辑态控件并用OnRowDataBound动态注入事件绑定再用客户端脚本拦截重复提交。3.1 TemplateField 替代 BoundField精确控制编辑态渲染asp:GridView IDgvData runatserver AutoGenerateColumnsfalse OnRowCommandgvData_RowCommand OnPageIndexChanginggvData_PageIndexChanging OnRowEditinggvData_RowEditing OnRowCancelingEditgvData_RowCancelingEdit OnRowUpdatinggvData_RowUpdating OnRowDataBoundgvData_RowDataBound DataKeyNamesID Columns asp:TemplateField HeaderText姓名 ItemTemplate %# Eval(Name) % /ItemTemplate EditItemTemplate asp:TextBox IDtxtName runatserver Text%# Bind(Name) % / asp:RequiredFieldValidator IDrfvName runatserver ControlToValidatetxtName ErrorMessage姓名不能为空 DisplayDynamic / /EditItemTemplate /asp:TemplateField asp:TemplateField HeaderText邮箱 ItemTemplate %# Eval(Email) % /ItemTemplate EditItemTemplate asp:TextBox IDtxtEmail runatserver Text%# Bind(Email) % / asp:RegularExpressionValidator IDrevEmail runatserver ControlToValidatetxtEmail ValidationExpression\w([-.]\w)*\w([-.]\w)*\.\w([-.]\w)* ErrorMessage邮箱格式不正确 DisplayDynamic / /EditItemTemplate /asp:TemplateField asp:TemplateField HeaderText操作 ItemTemplate asp:LinkButton IDlbEdit runatserver CommandNameEdit Text编辑 / nbsp; asp:LinkButton IDlbDelete runatserver CommandNameDelete CommandArgument%# Eval(ID) % Text删除 OnClientClickreturn confirm(确定删除); / /ItemTemplate EditItemTemplate asp:LinkButton IDlbUpdate runatserver CommandNameUpdate CommandArgument%# Eval(ID) % Text更新 / nbsp; asp:LinkButton IDlbCancel runatserver CommandNameCancel Text取消 / /EditItemTemplate /asp:TemplateField /Columns /asp:GridView关键点EditItemTemplate中的TextBox必须用ID如txtName且Text%# Bind(Name) %使用Bind而非Eval这是双向绑定的前提。LinkButton的CommandArgument%# Eval(ID) %在编辑态也必须存在否则RowCommand里拿不到主键。OnClientClickreturn confirm(...)是前端确认不影响服务端逻辑但能减少误操作。3.2 OnRowDataBound 动态绑定验证控件与样式protected void gvData_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType DataControlRowType.DataRow) { // 编辑态下为 TextBox 添加 CSS 类和 maxlength if (e.Row.RowState DataControlRowState.Edit) { TextBox txtName e.Row.FindControl(txtName) as TextBox; TextBox txtEmail e.Row.FindControl(txtEmail) as TextBox; if (txtName ! null) txtName.CssClass form-control input-sm; if (txtEmail ! null) txtEmail.CssClass form-control input-sm; // 动态设置最大长度避免数据库字段超长 if (txtName ! null) txtName.MaxLength 50; if (txtEmail ! null) txtEmail.MaxLength 100; } // 普通行态下为 LinkButton 添加禁用状态类AJAX 加载中 if (e.Row.RowState DataControlRowState.Normal) { LinkButton lbEdit e.Row.FindControl(lbEdit) as LinkButton; LinkButton lbDelete e.Row.FindControl(lbDelete) as LinkButton; if (lbEdit ! null) lbEdit.Attributes[class] btn btn-xs btn-default; if (lbDelete ! null) lbDelete.Attributes[class] btn btn-xs btn-danger; } } }逻辑说明RowState DataControlRowState.Edit是判断当前行是否处于编辑模式的唯一可靠方式比检查EditIndex更精准。FindControl必须在RowDataBound中调用因为模板控件只有在此时才被实例化。MaxLength设置是防御性编程防止用户输入超长字符串导致 SQL 异常。3.3 客户端防重复提交用 JavaScript 锁定按钮 显示加载状态script typetext/javascript // 全局锁定标识 var isProcessing false; function disableButtonsDuringPostback() { if (isProcessing) return; isProcessing true; // 锁定所有 GridView 行内的按钮 var rows document.querySelectorAll(#% gvData.ClientID % tr); for (var i 0; i rows.length; i) { var links rows[i].querySelectorAll(a); for (var j 0; j links.length; j) { if (links[j].textContent.trim() 编辑 || links[j].textContent.trim() 删除 || links[j].textContent.trim() 更新 || links[j].textContent.trim() 取消) { links[j].disabled true; links[j].style.opacity 0.6; } } } // 锁定操作按钮 var btns document.querySelectorAll(#% btnRefresh.ClientID %, #% btnExport.ClientID %); for (var k 0; k btns.length; k) { btns[k].disabled true; btns[k].style.opacity 0.6; } } function enableButtonsAfterPostback() { isProcessing false; var rows document.querySelectorAll(#% gvData.ClientID % tr); for (var i 0; i rows.length; i) { var links rows[i].querySelectorAll(a); for (var j 0; j links.length; j) { links[j].disabled false; links[j].style.opacity 1; } } var btns document.querySelectorAll(#% btnRefresh.ClientID %, #% btnExport.ClientID %); for (var k 0; k btns.length; k) { btns[k].disabled false; btns[k].style.opacity 1; } } // 注册到 ScriptManager 的异步事件 Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(disableButtonsDuringPostback); Sys.WebForms.PageRequestManager.getInstance().add_endRequest(enableButtonsAfterPostback); /script参数说明Sys.WebForms.PageRequestManager是 ASP.NET AJAX 的客户端 APIadd_beginRequest和add_endRequest是监听 AJAX 请求开始/结束的标准方式。#% gvData.ClientID %是获取服务端控件客户端 ID 的正确写法避免硬编码 IDWebForms 会自动添加前缀。opacity而非display:none是为了保留布局空间避免页面跳动。4. 避坑GridView AJAX 的五个高频翻车现场与血泪修复方案在二十多个 WebForms 项目中我见过太多人卡在这几个点上反复修改、重启 IIS、怀疑人生。以下五条全是线上真问题按发生频率排序每条都包含现象、根因、修复动作三要素照着做就能救回来。4.1 现象点击“编辑”后GridView 消失页面只剩空白原因UpdatePanel的ChildrenAsTriggers属性默认为true但当GridView内部有LinkButton触发RowCommand时若未在Triggers中显式声明UpdatePanel会尝试刷新整个ContentTemplate而BindGridView()若在Page_Load中无条件执行会导致DataSource为空GridView渲染失败。解决确保UpdatePanel的UpdateModeConditional在Triggers中必须添加asp:AsyncPostBackTrigger ControlIDgvData EventNameRowCommand /Page_Load中严格使用if (!IsPostBack)包裹BindGridView()。4.2 现象行内编辑后点击“更新”RowUpdating事件触发但FindControl(txtName)返回 null原因FindControl只能在RowDataBound或RowUpdating事件中调用且必须传入正确的 ID 字符串。常见错误是 ID 写错如txt_namevstxtName或在Page_Load中提前调用。解决检查.aspx中TextBox的ID是否与FindControl参数完全一致大小写敏感只在RowUpdating事件处理器内部调用FindControl如下protected void gvData_RowUpdating(object sender, GridViewUpdateEventArgs e) { GridViewRow row gvData.Rows[e.RowIndex]; // 先获取行对象 TextBox txtName row.FindControl(txtName) as TextBox; // 再 FindControl if (txtName ! null) { /* 安全使用 */ } }4.3 现象分页跳转后当前页码显示正确但数据仍是第一页的内容原因PageIndexChanging事件中设置了gvData.PageIndex e.NewPageIndex但未调用BindGridView()或BindGridView()中的数据源未按新页码查询如直接ListT.Skip().Take()但未传入PageIndex。解决PageIndexChanging中必须调用BindGridView()BindGridView()内部必须根据当前gvData.PageIndex和gvData.PageSize查询分页数据例如int pageIndex gvData.PageIndex; int pageSize gvData.PageSize; var pagedData GetAllData().Skip(pageIndex * pageSize).Take(pageSize).ToList(); gvData.DataSource pagedData;4.4 现象导出 Excel 按钮点击后页面弹出下载框但UpdatePanel内容全部刷新变成初始状态原因UpdatePanel默认会拦截所有回发但文件下载需要完整响应流与局部刷新冲突。btnExport_Click事件中若未禁用UpdatePanel会导致响应头被篡改下载失败或页面异常。解决在btnExport的OnClick事件中第一行添加ScriptManager.GetCurrent(this).RegisterPostBackControl(btnExport);确保btnExport不在UpdatePanel的Triggers中声明导出逻辑必须在服务端生成完整 HTTP 响应见第 5 章。4.5 现象AJAX 加载时UpdateProgress控件不显示或显示位置错乱原因UpdateProgress必须与目标UpdatePanel同级且AssociatedUpdatePanelID必须精确匹配同时CSS 定位若用position: absolute且父容器无position: relative会导致漂移。解决将UpdateProgress放在UpdatePanel同级同在form下并设置asp:UpdateProgress IDupgProgress runatserver AssociatedUpdatePanelIDupGridView ProgressTemplate div classajax-loader styleposition: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1000; img src~/images/loading.gif alt加载中... / p正在处理.../p /div /ProgressTemplate /asp:UpdateProgress确保.ajax-loader的z-index足够高999且position: fixed避免受父容器影响。5. 导出 Excel 不阻塞 UI用 Response.Write OpenXml SDK 实现零插件、无弹窗、高兼容方案“非常好用的 GridView”如果不能导出那只是半成品。但传统Response.Redirect或Response.WriteFile在UpdatePanel内会直接崩溃。正确做法是将导出逻辑剥离出UpdatePanel的作用域用RegisterPostBackControl解除 AJAX 拦截再用 OpenXml SDK 生成真正的.xlsx文件非旧式.xls避免 Excel 2007 兼容问题。全程无需第三方 DLL如 NPOI仅依赖 .NET Framework 4.5 自带的DocumentFormat.OpenXml。5.1 安装 OpenXml SDK 并引用命名空间通过 NuGet 安装Visual Studio 包管理器控制台Install-Package DocumentFormat.OpenXml在.aspx.cs顶部添加using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet;5.2 RegisterPostBackControl 解除 AJAX 拦截关键前置protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindGridView(); } else { // 每次回发都重新注册确保不丢失 ScriptManager.GetCurrent(this).RegisterPostBackControl(btnExport); } }注意RegisterPostBackControl必须在Page_Load的else分支中调用否则首次加载时会报错。这是 WebForms 的隐藏规则。5.3 导出方法生成内存流 直接写入 Responseprivate void ExportToExcel() { // 1. 获取全量数据非当前页是全部 var allData GetAllData(); // 你的数据获取方法 // 2. 创建内存流 using (MemoryStream ms new MemoryStream()) { // 3. 创建 SpreadsheetDocument using (SpreadsheetDocument doc SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook)) { WorkbookPart workbookPart doc.AddWorkbookPart(); workbookPart.Workbook new Workbook(); WorksheetPart worksheetPart workbookPart.AddNewPartWorksheetPart(); worksheetPart.Worksheet new Worksheet(new SheetViews(), new Sheets(), new SheetData()); Sheets sheets workbookPart.Workbook.Append(new Sheets()); Sheet sheet new Sheet() { Id workbookPart.GetIdOfPart(worksheetPart), SheetId 1, Name 数据导出 }; sheets.Append(sheet); SheetData sheetData worksheetPart.Worksheet.GetFirstChildSheetData(); // 4. 写入表头取 GridView 列名 string[] headers { ID, 姓名, 邮箱 }; // 实际应从 GridView.Columns 动态读取 Row headerRow new Row(); foreach (string header in headers) { Cell cell new Cell() { DataType CellValues.String, CellValue new CellValue(header) }; headerRow.Append(cell); } sheetData.Append(headerRow); // 5. 写入数据行 foreach (var item in allData) { Row dataRow new Row(); Cell idCell new Cell() { DataType CellValues.Number, CellValue new CellValue(item.ID.ToString()) }; Cell nameCell new Cell() { DataType CellValues.String, CellValue new CellValue(item.Name) }; Cell emailCell new Cell() { DataType CellValues.String, CellValue new CellValue(item.Email) }; dataRow.Append(idCell); dataRow.Append(nameCell); dataRow.Append(emailCell); sheetData.Append(dataRow); } worksheetPart.Worksheet.Save(); workbookPart.Workbook.Save(); } // 6. 写入 Response关键清除所有缓冲区 Response.Clear(); Response.ContentType application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; Response.AddHeader(Content-Disposition, attachment;filenameGridViewExport_ DateTime.Now.ToString(yyyyMMdd_HHmmss) .xlsx); Response.BinaryWrite(ms.ToArray()); Response.End(); // 必须调用否则后续代码仍会执行 } }参数说明Response.ContentType必须是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet这是.xlsx的标准 MIME 类型比application/octet-stream更规范能正确触发 Excel 关联。Content-Disposition中的attachment强制下载filename带时间戳避免缓存。Response.End()是终止响应的唯一安全方式return或throw new HttpException都可能导致后续代码执行或异常。5.4 前端增强导出按钮点击后显示“生成中”提示asp:Button IDbtnExport runatserver Text导出 Excel OnClickbtnExport_Click OnClientClickthis.disabledtrue;this.value生成中...; /提示OnClientClick中的this.disabledtrue与第 3 章的全局锁定不冲突它是导出专用的即时反馈而全局锁定是针对 AJAX 请求的。两者叠加体验更稳。6. 进阶技巧用 jQuery 插件增强 GridView 交互但绝不破坏 WebForms 生命周期标题里提到“asp.net的gridview的jquery插件”这很真实——但直接套用 jQuery DataTables 或 jqGrid 会与GridView的服务端事件模型打架。正确姿势是只用 jQuery 做视觉增强和轻量交互所有数据变更、分页、编辑仍走 WebForms 原生事件链。我常用三个插件每个都经过生产验证。6.1 用 jQuery.stickyTableHeaders 实现表头悬浮零侵入script srchttps://cdn.jsdelivr.net/npm/jquery-sticky-table-headers1.0.0/jquery.stickytableheaders.min.js/script script $(document).ready(function () { // 只对 GridView 的 table 元素启用不碰 tbody/tr/td $(#% gvData.ClientID %).stickyTableHeaders(); }); /script优势不修改GridViewHTML 结构不干扰RowCommand悬浮逻辑纯 CSS JS不依赖服务端兼容 IE10移动端自动降级。6.2 用 jQuery.inputmask 为编辑态 TextBox 添加格式化如手机号、日期script srchttps://cdn.jsdelivr.net/npm/jquery-inputmask5.0.7/dist/jquery.inputmask.min.js/script script $(document).ready(function () { // 仅在编辑态行内生效 $(document).on(focus, #% gvData.ClientID % input[typetext], function () { var $input $(this); if ($input.attr(id).indexOf(txtPhone) -1) { $input.inputmask({ mask: 999-9999-9999 }); } else if ($input.attr(id).indexOf(txtDate) -1) { $input.inputmask({ alias: datetime, inputFormat: yyyy-mm-dd }); } }); }); /script关键点用$(document).on(focus, ...)代理事件因为编辑态控件是动态生成的mask和alias由控件 ID 动态判断不硬编码格式化只影响显示TextBox.Text的值仍是原始字符串服务端无需额外解析。6.3 用 jQuery.confirm 替代原生 confirm支持 AJAX 异步确认原生confirm()会阻塞线程导致 AJAX 请求排队。用jQuery.confirm可实现异步回调script srchttps://cdn.jsdelivr.net/npm/jquery-confirm3.3.4/jquery-confirm.min.js/script link relstylesheet hrefhttps://cdn.jsdelivr.net/npm/jquery-confirm3.3.4/jquery-confirm.min.css / script $(document).ready(function () { // 重写删除按钮的点击行为 $(document).on(click, #% gvData.ClientID % a[onclick*Delete], function (e) { e.preventDefault(); var $link $(this); var id $link.data(id) || $link.closest(tr).data(id); // 从属性或行数据取ID $.confirm({ title: 确认删除, content: 此操作不可撤销确定要删除吗, buttons: { confirm: { text: 确定删除, btnClass: btn-red, action: function () { // 触发原生 __doPostBack __doPostBack(% gvData.UniqueID %, Delete$ id); } }, cancel: { text: 取消 } } }); }); }); /script注意__doPostBack是 WebForms 的底层回发函数第一个参数是控件的UniqueID非ClientID第二个参数是CommandArgument格式如Delete$123。这样既用了现代化 UI又没脱离 WebForms 的事件总线。最后说句实在话我坚持在 WebForms 里打磨GridView的 AJAX 效果不是因为怀旧而是因为手头有 8 个运行 5 年以上的系统它们的业务逻辑、权限体系、审计日志都深扎在服务端控件里。推倒重来成本太高而“非常好用的 GridViewASP.NET AJAX 效果”这条路是我用三年时间、十二次上线、上百次 QA 报告验证出来的最低风险升级路径。它不炫技但稳不前沿但省心。如果你也在维护类似的系统希望这篇笔记里那些带具体行号的代码、参数、和坑能帮你少熬两个通宵。希望帮到你。本文还有配套的精品资源点击获取
返回列表