2015-12-08 11 views
8

"Type"と "Value"の列を持つDataGridViewがあります。ユーザーがデータ型を選択し、その型と互換性のある値を入力すると、いくつかの型(例えば、 "Text")は任意の文字列を受け入れます。他のタイプ(「はい/いいえ」など)は可能な値のリストに限定されています。各行について、「値」列のセルをフリーフォームタイプのテキストボックスまたはリストタイプのコンボボックスに設定します。 DataGridViewはDataTableにバインドされています。バインドされたデータソースを持つ空のDatagridviewセル

ユーザーが1つのタイプの値を入力した後、その行を現在の値が許可されていない別のタイプに切り替えると問題が発生します。何を試しても、私はValueセルをクリアできません。次に、コンボボックスをセルに割り当てると、セルの現在の値が互換性がないため、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のです):あなたはそれを "テキスト" に設定すると、任意のものを入力している場合は、その後、

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; 
     } 
     } 
    } 
} 

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"; 

とイベント"YesNo"に切り替えると、 "System.ArgumentException:DataGridViewComboBoxCellの値が無効です"というエラーが表示され、カーソルがセル上にあるときはいつでも再び表示されます。テキスト行に戻すと、元の値が再び表示されます。

問題は、値がParameterTableに保存されていると仮定していますが、元の値をクリアしてParameterTableに反映させることができません。私はstring.Emptyの代わりにnullDBNull.Valueを試しましたが、いずれも違いはありませんでした。変更を加えるために "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

DBType Type =(DataType)cb.Value;コンパイルされません。 –

+0

私はコードをコピーし、いくつかのコンパイルエラーを修正し、実行し、両方のタイプを選択し、値を入力または選択し、エラーメッセージを表示しませんでした。 –

+0

@LeiYang - 私は、例のコードをseverelly切り捨てて、 "DBType"を "DataType"に変更しようとしました。これは、私の名前を選んだ後でしか見つからなかったいくつかのストックライブラリに "DbType"それを混乱させたくありません。ここで切り捨てた例を試してみる必要があります。私がそうしようとはしなかったが、私は関連性のあるものを削除したかもしれない。 –

答えて

1

値を消去するのではなく、YesNoListを使用して問題を解決してください。

グリッドcompboboxは、現在のレコードの値を見つけようとします。そして、あなたのYesNoListに空の値も空でもありません。

新しいレコードを追加し、最初に値を設定せずにデータタイプを設定しようとすると、エラーが表示されます。

これを解決するには、空のアイテムをYesNoListに追加するか、DataTypeを切り替えるときに既定値を既存のレコードに設定します。

+0

ありがとうございましたが、フィールドにデータが入力されていないのに、最初にYesNoボックスを選択すると、エラーが発生しません。または、コンボボックスに切り替える前にテキストボックスに何も割り当てていないのですか?これが原因であれば、これらのケースでもエラーが発生するはずです。 –

+0

これは、新しい行が追加されるたびに発生します。グリッドは、ドロップダウンリストでセルの現在の値を検索します。新しい行がドロップダウンから来た場合は、既にリストからの値があります。新しい行が別のセルから来た場合、値はデータテーブル内のカラムのデフォルト値が設定されている値(あなたの場合はnull)です。 null値がドロップダウンリストになく、エラーが発生します。 –

+0

申し訳ありませんが、それは私が見ているものではありません。コンボボックスがnullの場合でも、別の列で新しい行を追加するとエラーは表示されません。私はコンボボックスを表示するためにタイプを切り替える前に、テキストボックスが空であればエラーは表示されません。たとえコンボボックスが表示されてもnullであっても。私は、テキストボックスにスイッチの前にテキストが入っている場合にのみ、エラーが発生します。また、Lei Yangが指摘しているように、私がここで提供した単純化されたコードは実際には動作しますが、これが問題であれば動作しません。私は、私が投稿を作成したときに私が持っていてはならないことを切除したものを理解する必要があります。 –

0

編集: 私は以下のことがあなたの質問に具体的に答えているわけではありませんが、この例はあなたを助けてくれるかもしれません。 1つのセルに2つのコントローラを持つことを検討してください。


オリジナル

私は、これはあなたを助けるか、しませんかどうかわからないけど、私はあなたが議論し、非常に基本的なプログラムを作ってみました。データセットは2つのエントリで作成されます。最初の列はDataTypeで、2番目の列はValueです。 DataType Textを選択すると、ValueセルはTextboxに変わります。 DataType Yes/Noを選択すると、Textboxが非表示になり、DropDownListが表示されます。アイデアは、必要でないときに1つのコンポーネントを隠すことです。

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> 

これは私がより多くのコードを見ることなく、現時点では何ができる最高のですが、あなたができるかもしれませんこれを使って。 1つの提案、dgvInputs_CurrentCellDirtyStateChangedはコードをコミットするようです。私はこれがSQLコードであると仮定しています。最後の[送信]ボタンまたは[変更を受け入れる]ボタンが押されるまでコミットするまで待機して、SQLをあまり呼び出す必要はありませんが、SQLの開始点と終了点の間にエラーがある場合は、最初のSQL呼び出しと最後のSQL呼び出し。 2つの間で中断が発生した場合、必ずしもコミットする必要はありません。

+0

これはWebページではなくWinFormsアプリケーションですが、そのアイデアが翻訳可能かどうかを見ていきます。おそらくそれはWPFで行うことができますが、私はWPFの存在をほとんど意識することなく、可能ならばこれを処理する新しいパラダイムを習得する必要もなくなります。 –

+0

SQLはなく、データテーブルのみです。テキストボックスとコンボボックスを同じ列に配置する方法については、他の投稿(SOやその他のフォーラムでも、私は覚えていません)から借りたコードの一部です。私はなぜそれがあったのか分からない。 –

+0

実験のサンプルがあります(Lei YangのOPのコメントのおかげで)、DirtyCellコミットイベントのポイントは、タイプが変更されるとすぐにテキストボックス/コンボボックススイッチが発生することです。このイベントがなければ、タイプセルがフォーカスを失うまで発生しません。 –

関連する問題