2015-12-08 3 views
8

У меня есть DataGridView с столбцами «Тип» и «Значение». Пользователь выбирает тип данных и затем вводит значение, совместимое с этим типом, некоторые типы (например, «Текст») принимают любую строку. Другие типы (например, «Да/Нет») ограничены списком возможных значений. Для каждой строки я устанавливаю ячейку в столбце «Значение» как текстовое поле для типов произвольной формы, либо для списка типов списков. DataGridView привязан к DataTable.Пустая ячейка Datagridview со связанным datasourse

Проблема возникает, если пользователь вводит значение для одного типа, но затем переключает строку на другой тип, для которого текущее значение недопустимо. Независимо от того, что я пытаюсь, я не могу очистить ячейку Value. Затем, когда я назначаю combobox для ячейки, я получаю 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; 
     } 
     } 
    } 
} 

Если у вас есть он установлен в "текст" и ввести что-то произвольное, то переключитесь на «YesNo», он выдаст ошибку «System.ArgumentException: значение DataGridViewComboBoxCell недопустимо». Это будет появляться в любое время, когда курсор находится над ячейкой. Изменение его в текстовой строке приводит к тому, что исходное значение снова появляется.

Я предполагаю, что проблема в том, что значение сохраняется в ParameterTable, но я не могу заставить его распространять мою очистку исходного значения ParameterTable. Я пробовал null и DBNull.Value вместо string.Empty, но ни один не имел никакого значения. Я добавил строку «CommitEdit» в надежде заставить ее внести изменения, но это тоже не имело никакого значения.

Edit: Как оказалось, проблема была это код, который я имел в случае изменения клеток:

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

DBType Type = (DataType) cb.Value; не компилируется. –

+0

Я скопировал код, исправил некоторые ошибки компиляции, запустил, выделил оба типа и вводил или выбирал значения, не выходил ни одного сообщения об ошибке. –

+0

@LeiYang - Я строго усекретил код для примера и попытался изменить «DBType» на «DataType», потому что в некоторой библиотеке акций есть «DbType» (который я обнаружил только после того, как выбрал свое имя) я хочу смутить его. Я должен сам попробовать усеченный пример. Хотя я старался не делать этого, я, возможно, удалил что-то важное. –

ответ

1

Проблема заключается не в том, чтобы очистить значение, но и от YesNoList.

Сетка compbobox пытается найти значение для текущей записи, и в вашем YesNoList не пусто пустое значение.

Вы даже получите сообщение об ошибке, если попытаетесь добавить новую запись и сначала установите DataType без установки значения.

Вы можете решить эту проблему, добавив пустой элемент в свой YesNoList или установив значение по умолчанию для существующей записи при переключении DataType.

+0

Благодарим за ответ, но почему он не вызывает ошибку, если я сначала выберу поле YesNo, даже если данные не были введены в поле? Или, если я не назначаю ничего для текстового поля, прежде чем переключиться на выпадающий список? Если это было причиной, у меня также были ошибки в этих случаях. –

+0

Это происходит всякий раз, когда добавляется новая строка. Сетка будет выглядеть в раскрывающемся списке, чтобы найти текущее значение ячейки. В случае, если новая строка появилась из раскрывающегося списка, в списке уже есть значение, если новая строка поступает из другой ячейки, тогда значение равно независимо от значения по умолчанию для столбца в datatable (в вашем случае null). нулевое значение отсутствует в выпадающем списке, и вы получите сообщение об ошибке. –

+0

Извините, но это не то, что я вижу. Я не получаю ошибки, когда добавляю новую строку другим столбцом, даже если поле со списком равно null.Я не получаю ошибку, если текстовое поле было пустым, прежде чем я переключу тип, чтобы вызвать combobox. даже несмотря на то, что поле со списком имеет значение null, когда оно появляется. Я получаю сообщение об ошибке, если текстовое поле содержит текст перед коммутатором. Кроме того, как отметил Лэй Янг, упрощенный код, который я здесь привел, действительно работает, чего бы он не делал, если бы это была проблема. Мне нужно выяснить, что я вырезал, что не должен был, когда я создал пост. –

0

Редактировать: Я знаю ниже, конкретно не отвечает на ваш вопрос, поскольку он заявляет, но пример может помочь вам. Рассмотрим наличие двух контроллеров в одной ячейке.


Оригинал

Я не уверен, если это поможет вам или нет, но я пытался сделать очень простую программу, которую вы обсуждали. Набор данных создается с 2 входами. Первый столбец - это DataType, второй - Value. Если выбран текст 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. Вы можете подождать до тех пор, пока не будет завершена окончательная кнопка «Отправить» или «Принять изменения», чтобы вам не приходилось много раз вызывать SQL, но также, если есть ошибка, которая возникает между началом первый SQL-вызов и последний. Если между ними происходит перерыв, возможно, вы не обязательно хотите совершить.

+0

Это приложение WinForms, а не веб-страница, но я рассмотрю, является ли идея переводимой. Возможно, это можно сделать в WPF, но я едва ли не знаю о существовании WPF и предпочел бы избежать необходимости изучать новую парадигму, чтобы справиться с этим, если смогу. –

+0

Существует не SQL, а просто datatable. «Commit» был частью кода, который я заимствовал из других сообщений (как в SO, так и в каком-то другом форуме, я не помню) о том, как иметь текстовые поля и комбобокс в одном столбце. Я не уверен, почему он был там. –

+0

Теперь, когда у меня есть рабочий образец для экспериментов (благодаря комментариям Лэй Яна к OP), я обнаружил, что точкой события фиксации DirtyCell является то, что переключатель textbox/combobox возникает, как только изменяется тип. Без этого события это происходит не до тех пор, пока ячейка Type не потеряет фокус. –

Смежные вопросы