提问人:Barnabeck 提问时间:9/1/2023 最后编辑:Barnabeck 更新时间:9/2/2023 访问量:68
DataTable 行索引与绑定到 Gridview 时的顺序不匹配
DataTable row indexes do no match the order when bound to Gridview
问:
我有一个包含不同项目的 Gridview,按整数列“Order”排序。我正在将提供 Gridview 的 DataTable 保存为“会话参数”。
Item Order LinkButtons
----------------------------------
Item1 1 up down
Item2 2 up down
Item3 3 up down
这个想法是通过使用 LinkButton 上下移动项目来自定义项目的顺序。
这就是我所做的:
我从 RowClick 事件中获取行索引
LinkButton lb = (sender as LinkButton); string OrderNr = lb.CommandArgument; int OrderNummer = Convert.ToInt16(OrderNr); GridViewRow row = (GridViewRow)lb.NamingContainer; int idx = row.RowIndex;
调用 Session 表并更改 Order 参数
DataTable t = Session["SessionData"] as DataTable; t.Rows[idx][1] = Convert.ToString(OrderNummer - 1); t.Rows[idx-1][1] = Convert.ToString(OrderNummer);
然后,我对 DataTable 重新排序并将其绑定回 Grid
t.DefaultView.Sort = "OderNr"; t = t.DefaultView.ToTable();
不幸的是,这仅适用于第一次操作。之后,从 t.Rows[idx] 返回的内容对我来说是不可预测的,因为 Gridrow 行索引与 DataTable Row 索引不匹配。
非常混乱
最后,我的解决方案是避免行索引,并使用 CommandArgument “OrderNumber” 在循环中标识 DataTable 行。
protected void SelectOrderUp(object sender, EventArgs e)
{
if (Session["SessionData"] != null)
{
LinkButton lb = (sender as LinkButton);
string OrderNr = lb.CommandArgument;
int OrderNummer = Convert.ToInt16(OrderNr);
DataTable t = Session["SessionData"] as DataTable;
DataRow[] rows = t.Select();
for (int i = 0; i < rows.Length; i++)
{
if (Convert.ToInt16(rows[i][7].ToString()) == OrderNummer)
{
rows[i][7] = Convert.ToString(OrderNummer-1);
rows[i - 1][7] = OrderNummer;
break;
}
}
t.DefaultView.Sort = "OrderNummer";
t = t.DefaultView.ToTable();
Session["SessionData"] = t;
GridView_Criteria.DataSource = t;
GridView_Criteria.DataBind();
}
}
答:
1赞
Shahram Alemzadeh
9/2/2023
#1
在 C# 中没有影响,代码可能需要优化/微调。
aspx的
<asp:GridView ID="GridView1" runat="server">
<Columns>
<asp:TemplateField ShowHeader="false">
<ItemTemplate>
<asp:LinkButton ID="Btn_UP" runat="server" Text="UP" CommandArgument='<%# Eval("Order") %>' OnCommand="Btn_UP_Command"></asp:LinkButton>
<asp:LinkButton ID="Btn_DN" runat="server" Text="Down" CommandArgument='<%# Eval("Order") %>' OnCommand="Btn_DN_Command"></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Page_Load
if (!IsPostBack)
{
GridView1.DataSource = Get_DataTable();
GridView1.DataBind();
}
Get_DataTable
public DataTable Get_DataTable()
{
if (Session["dt"] == null)
{
DataTable dt = new DataTable();
dt.Columns.Add("Item", typeof(string));
dt.Columns.Add("Order", typeof(int));
for (int i = 1; i <= 10; i++)
dt.Rows.Add($"Item {i}", i);
return dt;
}
else
return (DataTable)Session["dt"];
}
Btn_UP_Command
DataTable dt = Get_DataTable();
int order = int.Parse(e.CommandArgument.ToString());
if (order == 1)
return;
dt.Select($"Order={order}")[0]["Order"] = -1;
dt.Select($"Order={order - 1}")[0]["Order"] = order;
dt.Select("Order=-1")[0]["Order"] = order - 1;
dt.DefaultView.Sort = "Order";
Session["dt"] = dt.DefaultView.ToTable();
GridView1.DataSource = dt;
GridView1.DataBind();
Btn_DN_Command
DataTable dt = Get_DataTable();
int order = int.Parse(e.CommandArgument.ToString());
if (order == dt.Rows.Count)
return;
dt.Select($"Order={order}")[0]["Order"] = -1;
dt.Select($"Order={order + 1}")[0]["Order"] = order;
dt.Select("Order=-1")[0]["Order"] = order + 1;
dt.DefaultView.Sort = "Order";
Session["dt"] = dt.DefaultView.ToTable();
GridView1.DataSource = dt;
GridView1.DataBind();
评论
0赞
Barnabeck
9/2/2023
谢谢Shahram,效果很好!我不熟悉 DataTable 操作的语法,因此我可能会考虑 Albert 的解决方案。你能帮我了解 dt 的语法吗?选择操作。我需要过滤那些没有“order”列值的 dt 行。并非所有记录都可以订购
2赞
Albert D. Kallal
9/2/2023
#2
这里的问题是您使用“默认”视图进行网格绑定。
但默认视图不会更改基表的行索引!!
这导致了 2 种可能的解决方案:
请考虑使用 GridView 中的 dataitem 索引,而不是行索引。
或者,更简单的方法是对基表进行排序。
你可以用这个来做到这一点:
rstData.DefaultView.Sort = "MyOrder";
rstData = rstData.DefaultView.ToTable();
Session["rstData"] = rstData;
注意:在上面非常接近,我们必须将 rstData 表推回 session[],因为我们已经用 重新构建了该表。ToTable(),它实际上是对象的新实例。
因此,这是一个工作示例(我在此示例中包含了 MyOrder 列)。另请记住,行是从 0 开始的,您的示例从 1 开始。在此示例中,“MyOrder”列从 0 开始,而不是从 1 开始。
所以,说这个标记:
<asp:GridView ID="GVHotels" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table"
Width="45%" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:BoundField DataField="MyOrder" HeaderText="Order" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdUp" runat="server" Text="UP"
OnClick="cmdUp_Click" CssClass="btn" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdDown" runat="server" Text="Down"
OnClick="cmdDown_Click" CssClass="btn" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
以及背后的代码:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string strSQL = @"SELECT * FROM tblHotelsA
WHERE Active = 1";
rstData = General.MyRst(strSQL);
LoadGrid();
}
else
rstData = (DataTable)Session["rstData"];
}
void LoadGrid()
{
rstData.DefaultView.Sort = "MyOrder";
rstData = rstData.DefaultView.ToTable();
Session["rstData"] = rstData;
GVHotels.DataSource = rstData;
GVHotels.DataBind();
}
protected void cmdUp_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
GridViewRow gRow = (GridViewRow)btn.NamingContainer;
int IX = gRow.RowIndex;
if (IX > 0)
{
rstData.Rows[IX]["MyOrder"] = IX - 1;
rstData.Rows[IX - 1]["MyOrder"] = IX;
LoadGrid();
}
}
protected void cmdDown_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
GridViewRow gRow = (GridViewRow)btn.NamingContainer;
int IX = gRow.RowIndex;
if (IX < rstData.Rows.Count - 1)
{
rstData.Rows[IX]["MyOrder"] = IX + 1;
rstData.Rows[IX + 1]["MyOrder"] = IX;
LoadGrid();
}
}
结果是这样的:
评论
0赞
Barnabeck
9/2/2023
非常感谢你,阿尔伯特!很好地解释了让我发疯的问题。
评论