带有绑定数据的空Datagridview单元格

时间:2015-12-08 17:00:17

标签: c# winforms datagridview

我有一个带有“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。我已尝试nullDBNull.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());
}

如果可能的话,我的想法是保留价值,并且我有消息框向我展示我需要捕获的特定异常,因为我不确定。但显然这项任务不会立即引发异常。这种情况只会在以后发生,显然是在其他事件中,我没有处理。

事后证明,我应该在示例中包含此代码。我现在不知道我是如何忽视它的。通过遗漏关键信息,我向所有通过疯狂追逐的人道歉。我非常感谢您的所有帮助。

2 个答案:

答案 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调用之间发生错误,则也是如此。如果两者之间发生中断,您可能不一定要提交。