.NET Framework 4.8中为GridView行添加额外数据的替代方案
问题描述
我通过构建DataTable作为GridView的数据源展示数据,代码如下:
DataTable details = new DataTable(); details.Columns.Add("Name"); details.Columns.Add("Cost"); details.Columns.Add("Time"); foreach(Source s in MySource){ DataRow detailRow = details.NewRow(); detailRow["Id"] = 10; detailRow["Name"] = "test"; detailRow["Cost"] = 10; detailRow["Time"] = s.GetSimplifiedTime(); details.Rows.Add(detailRow); } myGrid.DataSource = details ; myGrid.DataBind();
现在需要为每行的部分列保存额外数据(比如显示08/01对应的精确时间08/01/2023 10:23:11),并在RowSelecting事件处理器中使用这些数据。
目前我想到的方法是添加额外的DataKeys列,但这不符合DataKey的设计初衷,而且我已经把Id作为唯一标识符了:
<asp:GridView ID="myGrid" DataKeyNames="ID, DetailedTime, FullName" runat="server" CssClass="table table-hover" AutoGenerateColumns="false" OnSelectedIndexChanged="MyHandler"> <Columns> <asp:CommandField HeaderText="Process" ShowSelectButton="True" ButtonType="Button" ControlStyle-CssClass="btn btn-outline-success"/> <asp:BoundField DataField="Name" HeaderText="Name"></asp:BoundField> <asp:BoundField DataField="Cost" HeaderText="Cost"></asp:BoundField> <asp:BoundField DataField="Time" HeaderText="Due"></asp:BoundField> </Columns> </asp:GridView>
我想知道有没有其他方法可以为每行添加额外数据?更理想的是能为每个单元格添加数据,方便分别访问显示文本(如Cells[0].Text)和实际值(如Cells[0].Value)。我使用的是.NET Framework 4.8。
可行解决方案
方法1:在DataTable中添加隐藏列
直接在DataTable里新增存储额外数据的列,然后在GridView中将这些列设置为隐藏,既不影响页面显示,又能在事件中轻松获取数据。
修改DataTable代码:
DataTable details = new DataTable(); details.Columns.Add("Name"); details.Columns.Add("Cost"); details.Columns.Add("Time"); // 添加隐藏列存储精确时间 details.Columns.Add("DetailedTime", typeof(DateTime)); foreach(Source s in MySource){ DataRow detailRow = details.NewRow(); detailRow["Id"] = 10; detailRow["Name"] = "test"; detailRow["Cost"] = 10; detailRow["Time"] = s.GetSimplifiedTime(); // 存入精确时间数据 detailRow["DetailedTime"] = s.GetExactTime(); details.Rows.Add(detailRow); } myGrid.DataSource = details ; myGrid.DataBind();
修改GridView配置:
在Columns节点中添加隐藏列,设置Visible="false":
<asp:GridView ID="myGrid" DataKeyNames="ID" runat="server" CssClass="table table-hover" AutoGenerateColumns="false" OnSelectedIndexChanged="MyHandler"> <Columns> <asp:CommandField HeaderText="Process" ShowSelectButton="True" ButtonType="Button" ControlStyle-CssClass="btn btn-outline-success"/> <asp:BoundField DataField="Name" HeaderText="Name"></asp:BoundField> <asp:BoundField DataField="Cost" HeaderText="Cost"></asp:BoundField> <asp:BoundField DataField="Time" HeaderText="Due"></asp:BoundField> <!-- 隐藏列存储精确时间,前端不显示 --> <asp:BoundField DataField="DetailedTime" Visible="false"></asp:BoundField> </Columns> </asp:GridView>
在事件中获取数据:
protected void MyHandler(object sender, EventArgs e) { GridViewRow selectedRow = myGrid.SelectedRow; // 方式1:通过单元格索引获取 DateTime detailedTime = Convert.ToDateTime(selectedRow.Cells[4].Text); // 方式2:通过DataRowView直接读取DataTable列 DataRowView rowView = (DataRowView)selectedRow.DataItem; DateTime detailedTime = (DateTime)rowView["DetailedTime"]; }
方法2:利用RowDataBound事件将额外数据存入控件Attributes
如果需要为单个单元格绑定显示文本和实际值,可以在RowDataBound事件中,把额外数据存入单元格或子控件的Attributes集合中,相当于给元素加自定义属性。
代码示例:
protected void myGrid_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { DataRowView rowView = (DataRowView)e.Row.DataItem; // 获取时间列对应的单元格 TableCell timeCell = e.Row.Cells[3]; // 将精确时间存入单元格的自定义属性 timeCell.Attributes["data-detailed-time"] = rowView["DetailedTime"].ToString(); // 如果用TemplateField嵌套Label控件,也可以存到Label的Attributes里 // Label timeLabel = (Label)e.Row.FindControl("timeLabel"); // timeLabel.Attributes["data-detailed-time"] = rowView["DetailedTime"].ToString(); } }
在事件中获取数据:
protected void MyHandler(object sender, EventArgs e) { GridViewRow selectedRow = myGrid.SelectedRow; TableCell timeCell = selectedRow.Cells[3]; string detailedTimeStr = timeCell.Attributes["data-detailed-time"]; DateTime detailedTime = DateTime.Parse(detailedTimeStr); }
方法3:用TemplateField替代BoundField,直接绑定控件属性
通过TemplateField可以更灵活地控制单元格内容,同时给内部控件添加自定义属性来存储额外数据,完美实现"显示文本+实际值"的需求。
修改GridView配置:
<asp:GridView ID="myGrid" DataKeyNames="ID" runat="server" CssClass="table table-hover" AutoGenerateColumns="false" OnSelectedIndexChanged="MyHandler"> <Columns> <asp:CommandField HeaderText="Process" ShowSelectButton="True" ButtonType="Button" ControlStyle-CssClass="btn btn-outline-success"/> <asp:BoundField DataField="Name" HeaderText="Name"></asp:BoundField> <asp:BoundField DataField="Cost" HeaderText="Cost"></asp:BoundField> <asp:TemplateField HeaderText="Due"> <ItemTemplate> <!-- Text绑定显示的简化时间,自定义属性绑定精确时间 --> <asp:Label ID="timeLabel" runat="server" Text='<%# Eval("Time") %>' data-detailed-time='<%# Eval("DetailedTime") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
在事件中获取数据:
protected void MyHandler(object sender, EventArgs e) { GridViewRow selectedRow = myGrid.SelectedRow; Label timeLabel = (Label)selectedRow.FindControl("timeLabel"); // 读取自定义属性里的精确时间 string detailedTimeStr = timeLabel.Attributes["data-detailed-time"]; DateTime detailedTime = DateTime.Parse(detailedTimeStr); }
方法4:将原始数据源存入ViewState/Session(适合小数据量)
如果MySource的数据量不大,可以把原始数据源存储到ViewState或Session中,在RowSelecting事件里通过行索引直接获取原始对象的完整数据,不用额外处理DataTable。
存储数据源:
// 绑定DataTable前先把原始数据源存到ViewState ViewState["MySource"] = MySource; DataTable details = new DataTable(); // 构建DataTable的代码保持不变
在事件中获取原始数据:
protected void myGrid_SelectedIndexChanged(object sender, EventArgs e) { int selectedIndex = myGrid.SelectedIndex; List<Source> sourceList = (List<Source>)ViewState["MySource"]; Source selectedSource = sourceList[selectedIndex]; // 直接调用原始对象的方法获取精确时间 DateTime detailedTime = selectedSource.GetExactTime(); }
内容的提问来源于stack exchange,提问作者SyndRain
相关产品推荐
相关产品推荐

