Gridview为什么所有可见行都被设置为脏行?

5

我正在使用BulkEditGridView控件,如http://roohit.com/site/showArc.php?shid=bbb62讨论的那样,它非常适合我的需求。但是问题在于,每当我保存时,每一行都会被更新(我启用了分页)。通过代码调试,我发现当我点击保存按钮时,grid.DirtyRows.Count等于每页项目数减1。

我找不到哪些行被标记为脏数据。你有什么建议吗?

我的代码只有这个:

using System;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Collections;
using System.Data.Common;

public partial class MSDS_MSDS_Admin_GridUpdate : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            gridMSDS.DataKeyNames = new String[] { "id" };
            gridMSDS.DataBind();
        }
    }
}

编辑:这里是aspx代码。

<%@ Page Language="C#" MasterPageFile="~/MSDS/MSDS.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="GridUpdate.aspx.cs" Inherits="MSDS_MSDS_Admin_GridUpdate" Title="Untitled Page" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>
<%@ Register Assembly="RealWorld.Grids" Namespace="RealWorld.Grids" TagPrefix="cc2" %>
<asp:Content ID="Content1" ContentPlaceHolderID="PageContent" runat="Server">
    <br />
    <asp:Button ID="btnSave" runat="server" Text="Save" Width="100%" />
    <cc2:BulkEditGridView ID="gridMSDS" runat="server" AllowPaging="True" AllowSorting="True"
        DataSourceID="sqlData" EnableInsert="False" InsertRowCount="1" PageSize="20"
        SaveButtonID="btnSave" AutoGenerateColumns="False">
        <Columns>
            <asp:BoundField DataField="ID" HeaderText="ID" InsertVisible="False" Visible="false"
                ReadOnly="True" SortExpression="ID" />
            <asp:BoundField DataField="ChemicalTitle" HeaderText="ChemicalTitle" SortExpression="ChemicalTitle" />
            <asp:TemplateField HeaderText="SheetDate" SortExpression="SheetDate">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("SheetDate") %>' Width="85px"></asp:TextBox>
                    <cc1:CalendarExtender ID="TextBox1_CalendarExtender" runat="server" Enabled="True"
                        TargetControlID="TextBox1">
                    </cc1:CalendarExtender>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label2" runat="server" Text='<%# Eval("SheetDate") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="Filename" HeaderText="Filename" SortExpression="Filename" />
            <asp:BoundField DataField="Manufacturer" HeaderText="Manufacturer" SortExpression="Manufacturer" />
            <asp:BoundField DataField="UsageDept" HeaderText="UsageDept" SortExpression="UsageDept" />
            <asp:TemplateField HeaderText="Notes" SortExpression="Notes">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox5" runat="server" Text='<%# Bind("Notes") %>' TextMode="MultiLine"></asp:TextBox>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label6" runat="server" Text='<%# Bind("Notes") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Status" SortExpression="Status">
                <EditItemTemplate>
                    <asp:DropDownList ID="ddlStatus" runat="server" DataSourceID="sqlStatus" DataTextField="DisplayValue"
                        DataValueField="Value" SelectedValue='<%# Bind("Status") %>'>
                    </asp:DropDownList>
                    <asp:SqlDataSource ID="sqlStatus" runat="server" ConnectionString="<%$ ConnectionStrings:NCLWebConnectionString %>"
                        SelectCommand="getOptionList" SelectCommandType="StoredProcedure">
                        <SelectParameters>
                            <asp:Parameter DefaultValue="msds_Status" Name="ListName" Type="String" />
                        </SelectParameters>
                    </asp:SqlDataSource>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:DropDownList ID="ddlStatus" runat="server" DataSourceID="sqlStatus" disabled="true"
                        BackColor="White" DataTextField="DisplayValue" DataValueField="Value" SelectedValue='<%# Bind("Status") %>'>
                    </asp:DropDownList>
                    <asp:SqlDataSource ID="sqlStatus" runat="server" ConnectionString="<%$ ConnectionStrings:NCLWebConnectionString %>"
                        SelectCommand="getOptionList" SelectCommandType="StoredProcedure">
                        <SelectParameters>
                            <asp:Parameter DefaultValue="msds_Status" Name="ListName" Type="String" />
                        </SelectParameters>
                    </asp:SqlDataSource>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Health" SortExpression="Health">
                <EditItemTemplate>
                    <center>
                        <asp:TextBox ID="TextBox2" runat="server" Style="text-align: center" Text='<%# Bind("Health") %>'
                            Width="25px"></asp:TextBox>
                    </center>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label3" runat="server" Text='<%# Bind("Health") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Fire" SortExpression="Fire">
                <EditItemTemplate>
                    <center>
                        <asp:TextBox ID="TextBox3" runat="server" Text='<%# Bind("Fire") %>' Width="25px"></asp:TextBox></center>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label4" runat="server" Text='<%# Bind("Fire") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Reactivity" SortExpression="Reactivity">
                <EditItemTemplate>
                    <center>
                        <asp:TextBox ID="TextBox4" runat="server" Text='<%# Bind("Reactivity") %>' Width="25px"></asp:TextBox></center>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label5" runat="server" Text='<%# Bind("Reactivity") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:BoundField DataField="DateUpdated" HeaderText="DateUpdated" SortExpression="DateUpdated"
                ReadOnly="True" />
            <asp:BoundField DataField="UpdatedBy" ReadOnly="True" HeaderText="UpdatedBy" SortExpression="UpdatedBy" />
        </Columns>
    </cc2:BulkEditGridView>
    <asp:SqlDataSource ID="sqlData" runat="server" ConnectionString="<%$ ConnectionStrings:NCLWebConnectionString %>"
        SelectCommand="SELECT [ID], [ChemicalTitle], dbo.dateOnly([SheetDate]) As [SheetDate], [Filename], [Manufacturer], [UsageDept], [Notes], isnull([Status], 4) as [Status], [Health], [Fire], [Reactivity], [DateUpdated], [UpdatedBy] FROM [msds_Sheets] ORDER BY [ChemicalTitle]"
        UpdateCommand="msds_UpdateSheet" UpdateCommandType="StoredProcedure">
        <UpdateParameters>
            <asp:Parameter Name="ID" Type="Int32" />
            <asp:Parameter Name="ChemicalTitle" Type="String" />
            <asp:Parameter Name="SheetDate" DbType="DateTime" />
            <asp:Parameter Name="Filename" Type="String" />
            <asp:Parameter Name="Manufacturer" Type="String" />
            <asp:Parameter Name="UsageDept" Type="String" />
            <asp:Parameter Name="Notes" Type="String" />
            <asp:Parameter Name="Status" Type="Int16" />
            <asp:Parameter Name="Health" Type="Int16" />
            <asp:Parameter Name="Fire" Type="Int16" />
            <asp:Parameter Name="Reactivity" Type="Int16" />
            <asp:ProfileParameter Name="UpdatedBy" Type="String" PropertyName="Username" />
        </UpdateParameters>
    </asp:SqlDataSource>
</asp:Content>

测试流程如下:
-加载页面。
-编辑第一行的内容。
-点击保存按钮。


看看这篇文章是否有帮助 http://onthefencedevelopment.com/?p=68 - Bala R
我在处理GridView中的Dirty Rows时遇到了问题,而不是整个页面都被标记为dirty。 - MAW74656
1
我无法复制这种行为,即使启用了分页。(即它只更新我更改的行。) 你能发布带有属性的BulkEditGridView标记吗?此外,您能否发布与分页器相关的任何内容? - Joel Lee
1
我进行了一些额外的测试,包括使用CalendarExtenderSheetData字段。我尝试了很多东西,你的代码对我来说运行良好。有几个问题:(1) btnSave_Click做了什么?它不需要保存,因为BEGV控件已经为您完成了。(2) 即使您什么也没有编辑,行是否会变脏?(即只需按“保存”)(3) 您的测试序列是什么?(即在按“保存”之前,给我一些关于您所做的事情、分页等的详细信息)(4) 页面上是否有任何脚本(除了由ToolkitScriptManager添加的脚本之外)?谢谢 - Joel Lee
1
我还是无法使其出现错误,因此我们需要弄清楚我的代码/环境与你的有何不同。如果是我的话,我会:(1)创建一个新的虚拟主页,并让你的页面引用它。(2)注释掉“SheetDate”的“TemplateField”,并将其替换为“BoundField”。(你需要这样做是因为我认为你的“CalendarExtender”没有使用你的主页,显然这个主页有“ToolKitScriptManager”。) - Joel Lee
显示剩余16条评论
2个回答

4
您发布的代码看起来很好。我的测试代码与您的几乎完全相同,但我无法产生您所看到的不需要的结果。
您的代码与我的最显著的区别在于,我没有您的主页面代码。我的主页面是由VS 2008创建的默认页面。除此之外,差异在于SQL和基础数据库。具体来说,我没有使用任何存储过程,并且我对涉及的数据类型做出了合理的假设:ID是int,Status是smallint,SheetDate是Date,DateUpdated是DateTime,其他所有内容都是varchar。
编辑:您的SheetDate似乎只是来自数据库的基础DateTime值的日期部分。查询转换底层数据值以显示目的的事实并不重要,因为网格视图无法知道它正在获取修改后的值。编辑结束。
以下是一些诊断问题的建议:
- 在没有主页面或具有非常简单的虚拟主页面的情况下进行测试,该虚拟主页面保证不会有与BulkEditGridView交互的脚本。由于ToolkitScriptManager显然在您的主页面上,因此您需要将脚本管理器控件移动到页面本身,或者暂时摆脱CalendarExtender元素,该元素需要脚本管理器。 - 暂时将Status模板字段替换为绑定数据字段,以消除与DropDownList的交互可能性。在我的测试中,下拉列表没有引起问题,但您的代码与我的之间可能存在微妙差异。 - 如果以上两种方法都无法解决问题,则可能原因是您的BulkEditGridView版本存在问题。
编辑 - 诊断的其他建议:
  • 根据对BulkEditGridView源代码的检查,代码似乎正确跟踪了网格中哪些行是“脏”的。因此,虚假脏行最有可能的原因是网格中的一个或多个控件错误地检测到其数据内容的更改。您可以使用BulkEditGridView的源代码而不是预编译的DLL,在调试器中检测到这一点。

  • HandleRowChanged事件处理程序的开头设置断点,该处理程序检测网格中任何单元格的更改。通过在调试器中检查该对象的sender参数,您可以确定网格中正在进行值更改的控件。特别是,您将能够确定哪些控件(如果有)错误地触发值更改事件。

  • 确定哪些控件错误地报告其值已更改后,您可以专注于为什么会发生这种情况。

编辑结束

改进代码的一些其他建议如下。这些建议不会解决原始问题,但会使代码更加清晰:

  • 在所有模板字段中删除ItemTemplate。它们永远不会被使用,因为BulkEditGridView强制每一行都处于编辑状态。

  • 在代码后台中,不需要调用BindData(),因为源数据在声明性标记中指定,因此网格视图控件将自动绑定数据。


我可以不用它试试,但我相信在尝试使排序和分页工作时,我放置了那个databind()。此外,可能不需要Item Templates。我只是让它们自动生成,然后我改变了一些东西。 - MAW74656
1
@MAW - 数据绑定在排序和分页时不是必需的,因为这需要回发,所以您的数据绑定代码在那时不会执行。我有点认为项目模板是自动生成的。直到我查看了BEGV源代码,才意识到它们不是必需的。 - Joel Lee
刚刚在新的 Web 表单中尝试了 BEGV 代码,结果相同。我打算重新下载 BEGV 并替换我的 DLL。 - MAW74656
1
你需要使用源代码而不是预编译的dll。有几种方法可以做到这一点。 - Joel Lee
1
我很困惑,因为我无法复制这个。请尝试在查询中仅返回sheetDate,而不调用dbo.dateOnly,看看代码是否有效。我已经在这里使用完整的DateTime值测试了我的代码,它可以正常工作。我无法看出截断日期会有任何区别,但这是我的代码和你的代码之间唯一的显著差异。我会在午餐时间尝试检查响应。 - Joel Lee
显示剩余21条评论

0

我不是BulkEditGridView控件的专家,但这是我在Matt Dotson的博客文章中找到的。

除非你明确地观察变化,否则你可能无法知道行已经更新。首先,为每一行添加一个更改处理程序。

protected override void InitializeRow(GridViewRow row, DataControlField[] fields)
   {
      base.InitializeRow(row, fields);
      foreach (DataControlFieldCell cell in row.Cells)
      {
         if (cell.Controls.Count > 0)
         {
            AddChangedHandlers(cell.Controls);
         }
      }
   }

你可以使用这个代码片段

private void AddChangedHandlers(ControlCollection controls)
   {
      foreach (Control ctrl in controls)
      {
         if (ctrl is TextBox)
         {
            ((TextBox)ctrl).TextChanged += new EventHandler(this.HandleRowChanged);
         }
         else if (ctrl is CheckBox)
         {
            ((CheckBox)ctrl).CheckedChanged += new EventHandler(this.HandleRowChanged);
         }
         else if (ctrl is DropDownList)
         {
            ((DropDownList)ctrl).SelectedIndexChanged += new EventHandler(this.HandleRowChanged);
         }
      }
   }

然后您定义一个脏行列表,并将需要更新的行添加到其中(在事件处理程序中)

   private List<int> dirtyRows = new List<int>();
   void HandleRowChanged(object sender, EventArgs args)
   {
      GridViewRow row = ((Control) sender).NamingContainer as GridViewRow;
      if (null != row && !dirtyRows.Contains(row.RowIndex))
      {
         dirtyRows.Add(row.RowIndex);
      }
   }

最后,为了提交更改,请迭代所有脏行并保存更改。
   public void Save()
   {
      foreach (int row in dirtyRows)
      {
         this.UpdateRow(row, false);
      }

      dirtyRows.Clear();
   }

以下是您的ASPX代码

<asp:Button runat="server" ID="SaveButton" Text="Save Data" />
   <blog:BulkEditGridView runat="server" id="EditableGrid" DataSourceID="AdventureWorks" AutoGenerateColumns="False" DataKeyNames="LocationID" SaveButtonID="SaveButton" >
      <Columns>
         <asp:BoundField DataField="LocationID" HeaderText="LocationID" InsertVisible="False" ReadOnly="True" SortExpression="LocationID" />
         <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
         <asp:BoundField DataField="Availability" HeaderText="Availability" SortExpression="Availability" />
         <asp:BoundField DataField="CostRate" HeaderText="CostRate" SortExpression="CostRate" />
      </Columns>
   </blog:BulkEditGridView>

我看到了,但是看控件的源代码,这一切都是内置在控件中的。关键是使用了this.、base.和override。这就是让我感到困惑的部分。 - MAW74656
这似乎是BulkEditGridView控件本身的代码,理论上你只需要调用Save方法。我在你的代码中没有看到你实际调用了Save,你有吗? - oleksii
他的文章说这不是必需的。他为提供良好的开发者体验感到非常自豪。我会看看它是否有任何影响。 - MAW74656
显式调用Save()没有效果。 - MAW74656
@MAW74656 - 你说得对。当单击“保存”按钮时,控件会在内部调用Save()方法。再次单击它不会有任何作用,因为保存方法会清除dirtyRows列表(请参见上面答案中的代码)。 - Joel Lee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接