我有一个带有“Type”和“Value”列的DataGridView。用户选择数据类型,然后输入与该类型兼容的值,某些类型(例如“Text”)接受任何字符串。其他类型(例如“是/否”)限于可能值的列表。对于每一行,我将“值”列中的单元格设置为自由形式类型的文本框或列表类型的组合框。 DataGridView绑定到DataTable。
如果用户输入一种类型的值,但是然后将该行切换为不允许当前值的其他类型,则会出现问题。无论我尝试什么,我都无法清除Value cell。然后当我给单元格分配一个组合框时,我得到一个DataError,因为单元格的当前值是不兼容的。如何在将单元格从文本框更改为组合框之前清除该值?
public enum DataType
{
Text,
YesNo
}
public class IndexedItem
{
public string Name { get; set; }
public int ID {get; set; }
public string strID
{
get { return ID.ToString(); }
}
//constructors & other methods;
}
public static DataTable ParameterTable;
public List<IndexedItem> YesNoList;
在表单构造函数中(dgvInputs是DataGridView):
ParameterTable = new DataTable("ParameterTable");
ParameterTable.Columns.Add("Type", typeof(DataType));
ParameterTable.Columns.Add("Value", typeof(string));
YesNoList = new List<IndexedItem>();
YesNoList.Add(new IndexedItem("Yes", 1));
YesNoList.Add(new IndexedItem("No", 0));
var D = (DataGridViewComboBoxColumn)dgvInputs.Columns[0];
D.ValueMember = "Value";
D.DisplayMember = "Display";
D.DataSource = new DataType[] {
DataType.Text,
DataType.YesNo
}.Select(x => new { Display = x.ToString(), Value = (int)x }).ToList();
BindingSource ParamSource = new BindingSource();
ParamSource.DataSource = ParameterTable;
dgvInputs.AutoGenerateColumns = false;
dgvInputs.DataSource = ParamSource;
dgvInputs.Columns[0].DataPropertyName = "Type";
dgvInputs.Columns[1].DataPropertyName = "Value";
事件:
private void dgvInputs_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
if (dgvInputs.IsCurrentCellDirty) {
dgvInputs.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void dgvInputs_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex >= 0 && e.ColumnIndex == 0) {
var cb = (DataGridViewComboBoxCell)dgvInputs[0, e.RowIndex];
if (cb.Value != null && cb.Value != DBNull.Value) {
DataType Type = (DataType)cb.Value;
dgvInputs[1, e.RowIndex].Value = string.Empty;
dgvInputs.CommitEdit(DataGridViewDataErrorContexts.Commit);
switch (Type) {
case DataType.YesNo:
dgvInputs[1, e.RowIndex].Dispose();
var newBox = new DataGridViewComboBoxCell();
newBox.DisplayMember = "Name";
newBox.ValueMember = "strID";
newBox.DataSource = YesNoList;
dgvInputs[1, e.RowIndex] = newBox;
break;
default:
dgvInputs[1, e.RowIndex] = new DataGridViewTextBoxCell();
break;
}
}
}
}
如果你将它设置为“text”并输入任意内容,则切换到“YesNo”,它会给出一个错误“System.ArgumentException:DataGridViewComboBoxCell值无效。”,它将在光标结束时重新出现细胞。将其更改回文本行会导致原始值重新出现。
我假设问题是该值保存在ParameterTable中,但我无法将其原始值的清除传播到ParameterTable。我已尝试null
和DBNull.Value
而不是string.Empty
,但两者都没有任何区别。我添加了“CommitEdit”行,希望能让它做出改变,但这也没有任何区别。
修改 事实证明,问题是我在单元格更改事件中使用的代码:
string Default = dgvInputs[4, e.RowIndex].Value as string;
// code switching out text box and combo box above
try
{
dgvInputs[4, e.RowIndex].Value = Default;
} catch (Exception e2) {
MessageBox.Show(e2.GetType().ToString());
}
如果可能的话,我的想法是保留价值,并且我有消息框向我展示我需要捕获的特定异常,因为我不确定。但显然这项任务不会立即引发异常。这种情况只会在以后发生,显然是在其他事件中,我没有处理。
事后证明,我应该在示例中包含此代码。我现在不知道我是如何忽视它的。通过遗漏关键信息,我向所有通过疯狂追逐的人道歉。我非常感谢您的所有帮助。
答案 0 :(得分:1)
问题不是清除值,而是使用YesNoList。
网格compbobox尝试查找当前记录的值,并且YesNoList中没有空的空值。
如果您尝试添加新记录并首先设置DataType而不设置值,您甚至会收到错误。
您可以通过向YesNoList添加空项目或在切换DataType时将现有记录设置为默认值来解决此问题。
答案 1 :(得分:0)
编辑: 我知道下面没有具体回答你提出的问题,但这个例子可能会帮助你。考虑在一个单元中有两个控制器。
原始
我不确定这是否会对您有所帮助,但我试图制作一个您讨论过的非常基本的程序。使用2个条目创建数据集。第一列是DataType,第二列是Value。如果选择了“数据类型文本”,则“值”单元格将变为“文本框”。如果选择了DataType Yes / No,它将隐藏文本框并显示DropDownList。这个想法是在不需要时隐藏一个组件。
Default.aspx.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DataGridViewBounds
{
public partial class _Default : Page
{
public enum DataType
{
Text,
YesNo
}
public class IndexedItem
{
public string Name
{ get; set; }
public int ID
{ get; set; }
public string strID
{ get { return ID.ToString(); } }
}
protected void Page_Load (object sender, EventArgs e)
{
if (!IsPostBack)
{
Bind();
}
for (int i = 0; i < dg.Items.Count; ++i)
{
bool ShowText = ((DropDownList)dg.Items[i].Cells[0].Controls[1]).SelectedValue.Equals("text");
((DropDownList)dg.Items[i].Cells[1].Controls[1]).Visible = !ShowText;
((TextBox)dg.Items[i].Cells[1].Controls[3]).Visible = ShowText;
}
}
private void Bind ()
{
DataTable ParameterTable = new DataTable("ParameterTable");
ParameterTable.Columns.Add("", typeof(string));
ParameterTable.Columns.Add("Type", typeof(DataType));
ParameterTable.Columns.Add("Value", typeof(string));
List<ListItem> YesNoList = new List<ListItem>(); // Should be ListItem, not IndexedItem
YesNoList.Add(new ListItem("Yes", "1"));
YesNoList.Add(new ListItem("No", "0"));
DataRow row = ParameterTable.NewRow();
row["Type"] = DataType.Text;
row["Value"] = "Some text";
DataRow row2 = ParameterTable.NewRow();
ParameterTable.Rows.Add(row);
row2["Type"] = DataType.YesNo;
row2["Value"] = "false";
ParameterTable.Rows.Add(row2);
dg.DataSource = ParameterTable;
dg.DataBind();
dg.ShowHeader = true;
dg.Visible = true;
for (int i = 0; i < dg.Items.Count; ++i)
{ // Showing 2 ways to bind the DropDownList items
((DropDownList)dg.Items[i].Cells[0].Controls[1]).Items.Add(new ListItem("Text", "text"));
((DropDownList)dg.Items[i].Cells[0].Controls[1]).Items.Add(new ListItem("Yes/No", "bool"));
((DropDownList)dg.Items[i].Cells[1].Controls[1]).DataSource = YesNoList;
((DropDownList)dg.Items[i].Cells[1].Controls[1]).DataBind();
}
}
}
}
Default.aspx页面
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DataGridViewBounds._Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<asp:DataGrid ID="dg" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateColumn HeaderText="Type">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddListType" AutoPostBack="true"></asp:DropDownList>
<asp:Label id="TypeLabel" runat="server" Visible="false"></asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Value">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddListValue" AutoPostBack="true" Visible="false"></asp:DropDownList>
<asp:TextBox id="ValueLabel" runat="server" Visible="false"></asp:TextBox>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
</asp:Content>
这是我目前无法看到更多代码的最佳方法,但您可以使用它。一个建议,dgvInputs_CurrentCellDirtyStateChanged似乎提交代码。我假设这是SQL代码。你可能要等到作出决定直到最后一次提交&#39;按钮或&#39;接受更改&#39;按钮已被按下,因此您不必非常调用SQL,但如果在第一次SQL调用的开始和最后一次SQL调用之间发生错误,则也是如此。如果两者之间发生中断,您可能不一定要提交。