访问GridView单元格ItemTemplate中控件时出现Null引用异常
GridView下拉框Null引用异常及取值问题
问题现象
调试时,以下代码触发Null引用异常:
string disposResult = ((HtmlSelect)row.Cells[1].FindControl("DropDownList1")).Value;
需求是获取GridView中每行下拉框的选中值。
现有代码
GridView定义
<asp:GridView ID="MyGridView" runat="server" AutoGenerateColumns="false"> <Columns> <asp:BoundField DataField="SerialNo" HeaderText="Serial No" /> <asp:TemplateField HeaderText="Disposition result"> <ItemTemplate> <%# Eval("Disposition result") %> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Disposition comment"> <ItemTemplate> <textarea runat="server" ID="txtDispositionComment" Rows="5" CssClass="form-control" /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Note for Group"> <ItemTemplate> <textarea runat="server" ID="txtNoteForGroup" Rows="5" CssClass="form-control" /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
数据源生成代码
protected async void serialNoFieldData() { string NMRValue = Request.QueryString["NMRValue"]; string apiUrl = "https://itapps/nmrapi_V2/api/nmr?NMR_Number=" + NMRValue; using (HttpClient client = new HttpClient()) { string apiResponse = await client.GetStringAsync(apiUrl); JObject responseData = JObject.Parse(apiResponse); JArray data = JArray.Parse(responseData["data"].ToString()); DataTable dt = new DataTable(); dt.Columns.Add("SerialNo"); dt.Columns.Add("Disposition result"); dt.Columns.Add("Disposition comment"); dt.Columns.Add("Note for Group"); // 获取Disposition result列的下拉框选项 string dropdownApiUrl = "https://itapps/nmrapi_V2/api/DBParameter/NMR_Form?PN=DisposRes"; string dropdownApiResponse = await client.GetStringAsync(dropdownApiUrl); JObject dropdownData = JObject.Parse(dropdownApiResponse); List<string> dropdownValues = new List<string>(); if ((bool)dropdownData["success"]) { JArray dataArray = (JArray)dropdownData["data"]; foreach (string item in dataArray) { dropdownValues.Add(item); } } foreach (JObject item in data) { string serialNumber = (string)item["SerialNo"]; // 填充表单字段 // 添加新行到DataTable DataRow dr = dt.NewRow(); dr["SerialNo"] = serialNumber; string dropdownHtml = CreateDropDownList(dropdownValues); dr["Disposition result"] = dropdownHtml; dr["Disposition comment"] = CreateTextArea(); dr["Note for Group"] = CreateTextArea(); dt.Rows.Add(dr); } // 将DataTable绑定到GridView控件 MyGridView.DataSource = dt; MyGridView.DataBind(); } }
下拉框HTML生成代码
private string CreateDropDownList(List<string> values) { StringBuilder sb = new StringBuilder(); sb.Append("<select id='DropDownList1' class='form-control' style='width: 200px;'>"); foreach (string value in values) { sb.Append("<option value='" + value + "'>" + value + "</option>"); } sb.Append("</select>"); return sb.ToString(); }
问题原因
当前实现是将纯HTML字符串直接输出到页面,生成的<select>是客户端控件,没有runat="server"属性,ASP.NET无法将其识别为服务器端控件,因此FindControl("DropDownList1")会返回null,导致Null引用异常。另外,每行的下拉框id都设为DropDownList1,会造成页面元素id重复,不符合HTML规范。
解决方案
方案1:使用服务器端DropDownList控件(推荐)
步骤1:修改GridView的Disposition result列
将原来的ItemTemplate替换为服务器端DropDownList:
<asp:TemplateField HeaderText="Disposition result"> <ItemTemplate> <asp:DropDownList runat="server" ID="DropDownList1" CssClass="form-control" Width="200px"></asp:DropDownList> </ItemTemplate> </asp:TemplateField>
步骤2:存储下拉框选项到全局变量
在页面类中定义全局变量存储下拉选项:
private List<string> _dropdownValues;
然后在serialNoFieldData方法中,获取到dropdownValues后赋值给_dropdownValues:
// 获取Disposition result列的下拉框选项 string dropdownApiUrl = "https://itapps/nmrapi_V2/api/DBParameter/NMR_Form?PN=DisposRes"; string dropdownApiResponse = await client.GetStringAsync(dropdownApiUrl); JObject dropdownData = JObject.Parse(dropdownApiResponse); _dropdownValues = new List<string>(); if ((bool)dropdownData["success"]) { JArray dataArray = (JArray)dropdownData["data"]; foreach (string item in dataArray) { _dropdownValues.Add(item); } }
同时,DataTable中不需要再存下拉框HTML,直接留空即可:
DataRow dr = dt.NewRow(); dr["SerialNo"] = serialNumber; dr["Disposition result"] = ""; dr["Disposition comment"] = ""; dr["Note for Group"] = ""; dt.Rows.Add(dr);
步骤3:添加GridView的RowDataBound事件绑定下拉框
protected void MyGridView_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { DropDownList ddl = (DropDownList)e.Row.FindControl("DropDownList1"); ddl.DataSource = _dropdownValues; ddl.DataBind(); // 如需设置默认选中值,可从DataRow中获取 // string defaultVal = (string)DataBinder.Eval(e.Row.DataItem, "Disposition result"); // ddl.SelectedValue = defaultVal; } }
记得在GridView标签中添加事件绑定:OnRowDataBound="MyGridView_RowDataBound"
步骤4:正确获取选中值
此时可以正常通过FindControl获取服务器端控件:
foreach (GridViewRow row in MyGridView.Rows) { if (row.RowType == DataControlRowType.DataRow) { DropDownList ddl = (DropDownList)row.FindControl("DropDownList1"); string disposResult = ddl.SelectedValue; // 后续处理逻辑 } }
方案2:使用客户端HTML控件,通过Request.Form取值
如果坚持使用纯HTML下拉框,需要修改生成逻辑,给每个下拉框设置唯一的name属性:
步骤1:修改CreateDropDownList方法,传入SerialNo作为标识
private string CreateDropDownList(List<string> values, string serialNo) { StringBuilder sb = new StringBuilder(); // 设置唯一的name属性,包含SerialNo sb.Append($"<select name='DropDownList_{serialNo}' class='form-control' style='width: 200px;'>"); foreach (string value in values) { sb.Append("<option value='" + value + "'>" + value + "</option>"); } sb.Append("</select>"); return sb.ToString(); }
调用时传入SerialNo:
string dropdownHtml = CreateDropDownList(dropdownValues, serialNumber);
步骤2:后台取值
通过Request.Form根据唯一name获取选中值:
foreach (GridViewRow row in MyGridView.Rows) { if (row.RowType == DataControlRowType.DataRow) { string serialNo = row.Cells[0].Text; string disposResult = Request.Form[$"DropDownList_{serialNo}"]; // 后续处理逻辑 } }
内容的提问来源于stack exchange,提问作者Guru
相关产品推荐
相关产品推荐

