ASP.NET GridView拖拽排序后无法同步前端变更至后端的问题求助
问题描述
我有一个绑定服务器端Categories表的GridView,前端配备了拖拽(drag and drop)脚本用于调整分类的顺序,同时需要更新其Order字段中的排序位置。但目前,在拖拽调整行顺序后点击Update Order按钮时,后端获取到的仍是绑定到GridView的初始对象,而非变更后的行。我希望将前端的变更传递到服务器端,但系统始终仅发送初始绑定的数据,恳请提供帮助。
页面标记(Markup)
<asp:GridView ID="gvData" runat="server" AutoGenerateColumns="False" CssClass="table table-striped grab" EnableViewState="True" GridLines="None" OnRowDataBound="gvData_RowDataBound" EmptyDataText="No Data Found" EmptyDataRowStyle-CssClass="emptyDataMessage" AllowSorting="True" DataKeyNames="ExternalId"> <Columns> <asp:TemplateField HeaderText="#" ItemStyle-Width="1%" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center"> <ItemTemplate> <asp:Label ID="lbCategoryId" runat="server" CssClass="sortable-field" text='<%# Container.DataItemIndex + 1 %>'/> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Name" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left"> <ItemTemplate> <asp:HyperLink ID="hlCategoryDetail" runat="server" /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Nick" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left"> <ItemTemplate> <asp:Literal ID="ltNick" runat="server" /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Order" ItemStyle-Width="1%" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left"> <ItemTemplate> <asp:HiddenField ID="hfOriginalIndex" runat="server" Value="<%# Container.DataItemIndex %>" /> <asp:Label ID="lbSort" runat="server" CssClass="sortable-field" text='<%# Container.DataItemIndex + 1 %>'/> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="-" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center"> <ItemTemplate> <asp:Label ID="lbAlert" runat="server" CssClass="text-danger" /> </ItemTemplate> </asp:TemplateField> </Columns> <EmptyDataRowStyle CssClass="emptyDataMessage" /> </asp:GridView> <div class="col-sm-4 col-sm-offset-10"> <asp:Button ID="btnUpdateOrder" CssClass="btn btn-primary" Text="Update Order" runat="server" OnClick="UpdateOrder" /> </div>
后端代码(Code Behind)
protected void gvData_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.DataItem != null) { var category = (PaymentCategory)e.Row.DataItem; var hlCategoryDetail = (HyperLink)e.Row.FindControl("hlCategoryDetail"); var ltNick = (Literal)e.Row.FindControl("ltAlias"); var lbOrder = (Label)e.Row.FindControl("lbSort"); var lbAlert = (Label)e.Row.FindControl("lbAlert"); var lbCategoryId = (Label)e.Row.FindControl("lbCategoryId"); lbCategoryId.Text = category.Id.ToString(); hlCategoryDetail.Text = category.Name; hlCategoryDetail.NavigateUrl = "~/Default.aspx?action=EditPaymentCategory&id=" + category.Id; } } protected void UpdateSort(object sender, EventArgs e) { int[] sortIds = (from p in Request.Form["lbSort"].Split(',') select int.Parse(p)).ToArray(); foreach (GridViewRow row in gvData.Rows) { //Access Cell values. //int categoryId = int.Parse(((Label)row.FindControl("lbCategoryId")).Text); var ltNick = (Literal)row.FindControl("ltNick"); int origIndex = int.Parse(((HiddenField)row.FindControl("hfOriginalIndex")).Value); int orderId = int.Parse(((Label)row.FindControl("lbOrder")).Text); string categoryId = gvData.DataKeys[origIndex].Value.ToString(); //Controller.UpdateCategorySortOrder(category, orderId); } Response.Redirect(Request.Url.AbsoluteUri); }
拖拽脚本(Drag and Drop Script)
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.24/themes/smoothness/jquery-ui.css" /> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.24/jquery-ui.min.js"></script> <script src="jquery.tablednd.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { $("[id*=gvData]").sortable({ items: 'tr:not(tr:first-child)', cursor: 'pointer', axis: 'y', dropOnEmpty: false, start: function (e, ui) { ui.item.addClass("selected"); }, stop: function (e, ui) { ui.item.removeClass("selected"); //iterate through rows and update Sort field $("[id*=lbCategoryId]").each(function (index) { $(this).text(index + 1); }); $("[id*=gvData] tbody tr").each(function (index) { $(this).find("[id*=hfOriginalIndex]").val(index); // Update categoryId using data attribute var categoryId = $(this).find("[id*=lbCategoryId]").data("Id").val(categoryId); }); }, receive: function (e, ui) { $(this).find("tbody").append(ui.item); } }); }); </script>
解决方案
问题核心:ASP.NET中Label控件的内容不会随表单回发,只有输入类控件(如HiddenField、TextBox)的值会被提交到服务器。你当前用Label存储排序号和分类ID,拖拽修改后这些值无法传递到后端。
修复步骤
替换Label为HiddenField存储关键数据
保留Label用于前端显示,新增HiddenField存储需要回发的分类ID和当前排序值:<!-- 分类ID列修改 --> <asp:TemplateField HeaderText="#" ItemStyle-Width="1%" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center"> <ItemTemplate> <asp:HiddenField ID="hfCategoryId" runat="server" Value='<%# Eval("Id") %>' /> <asp:Label ID="lbCategoryId" runat="server" CssClass="sortable-field" text='<%# Container.DataItemIndex + 1 %>'/> </ItemTemplate> </asp:TemplateField> <!-- 排序列修改 --> <asp:TemplateField HeaderText="Order" ItemStyle-Width="1%" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left"> <ItemTemplate> <asp:HiddenField ID="hfOriginalIndex" runat="server" Value="<%# Container.DataItemIndex %>" /> <asp:HiddenField ID="hfCurrentSort" runat="server" Value="<%# Container.DataItemIndex + 1 %>" /> <asp:Label ID="lbSort" runat="server" CssClass="sortable-field" text='<%# Container.DataItemIndex + 1 %>'/> </ItemTemplate> </asp:TemplateField>修改前端脚本,更新HiddenField的值
拖拽停止后,同步更新显示用Label和回发用HiddenField的内容:stop: function (e, ui) { ui.item.removeClass("selected"); $("[id*=gvData] tbody tr").each(function (index) { // 更新前端显示的序号 $(this).find("[id*=lbCategoryId]").text(index + 1); $(this).find("[id*=lbSort]").text(index + 1); // 更新回发用的排序值 $(this).find("[id*=hfCurrentSort]").val(index + 1); // 更新原索引(如果需要) $(this).find("[id*=hfOriginalIndex]").val(index); }); },后端调整数据读取逻辑
从HiddenField中获取更新后的分类ID和排序值,不再依赖Label或原DataKeys索引:protected void UpdateSort(object sender, EventArgs e) { List<(int CategoryId, int NewOrder)> sortList = new List<(int, int)>(); foreach (GridViewRow row in gvData.Rows) { int categoryId = int.Parse(((HiddenField)row.FindControl("hfCategoryId")).Value); int newOrder = int.Parse(((HiddenField)row.FindControl("hfCurrentSort")).Value); sortList.Add((categoryId, newOrder)); } // 调用业务逻辑更新排序 // Controller.UpdateCategorySortOrders(sortList); Response.Redirect(Request.Url.AbsoluteUri); }
额外优化
- 移除无用的
jquery.tablednd.js,当前已使用jQuery UI的sortable功能; - 将GridView的
DataKeyNames设置为分类主键Id,方便后续直接通过DataKeys获取主键(可选); - 拖拽脚本中删除无效的
data("Id")赋值代码,直接操作HiddenField即可。
内容的提问来源于stack exchange,提问作者Nara Omur
相关产品推荐
相关产品推荐

