web-dev-qa-db-ja.com

DataTableにバインドされたComboBoxに「空」フィールドを挿入する方法

WinFormsアプリにアイテムを選択できるコンボボックスがありますが、必須ではありません。したがって、値が設定されていないことを示す最初の項目「空」が必要です。

コンボボックスは、ストアドプロシージャから返されるDataTableにバインドされています(UIコントロール:pでのハンガリー語表記については謝罪しません):

 DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP
 cmbHierarchies.DataSource = hierarchies;
 cmbHierarchies.ValueMember = "guid";
 cmbHierarchies.DisplayMember = "ObjectLogicalName";

このような空のアイテムを挿入するにはどうすればよいですか?

私はSPを変更するアクセス権を持っていますが、UIロジックでそれを「汚染」しないことを本当に望んでいます。

pdate:おかげで私が非表示にしたのはDataTable.NewRow()でした。私はあなたすべてを更新しました(とにかくこれまでのところ3つの回答すべて)。 「答え」を決める前に、イテレーターパターンを機能させようとしています

更新:この編集によりコミュニティウィキの土地に入ると思います。すべてのドメインのコンテキストでメリットがあるため、単一の回答を指定しないことにしました。集合的なご意見ありがとうございます。

18
johnc

できることは2つあります。

  1. ストアドプロシージャから返されるDataTableに空の行を追加します。

    DataRow emptyRow = hierarchies.NewRow();
    emptyRow["guid"] = "";
    emptyRow["ObjectLogicalName"] = "";
    hierarchies.Rows.Add(emptyRow);
    

    DataViewを作成し、ObjectLogicalName列を使用して並べ替えます。これにより、新しく追加された行がDataViewの最初の行になります。

    DataView newView =           
         new DataView(hierarchies,       // source table
         "",                             // filter
         "ObjectLogicalName",            // sort by column
         DataViewRowState.CurrentRows);  // rows with state to display
    

    次に、データビューをDataSourceComboBoxとして設定します。

  2. 上記のように本当に新しい行を追加したくない場合。 "Delete" keypressイベントを処理するだけで、ユーザーがComboBox値をnullに設定できるようにすることができます。ユーザーがDeleteキーを押したら、SelectedIndexを-1に設定します。また、ComboBox.DropDownStyleDropDownListに設定する必要があります。これにより、ユーザーがComboBoxの値を編集できなくなります。

25
Vivek
cmbHierarchies.SelectedIndex = -1;
14
Andrei Nemes

私は通常、この種のイテレータを作成します。それはあなたのデータを汚染することを避け、データバインディングでうまく機能します:

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP
cmbHierarchies.DataSource = GetDisplayTable(hierarchies);
cmbHierarchies.ValueMember = "guid";
cmbHierarchies.DisplayMember = "ObjectLogicalName";

...

private IEnumerable GetDisplayTable(DataTable tbl)
{
    yield return new { ObjectLogicalName = string.Empty, guid = Guid.Empty };

    foreach (DataRow row in tbl.Rows)
        yield return new { ObjectLogicalName = row["ObjectLogicalName"].ToString(), guid = (Guid)row["guid"] };
}

免責事項:私はこのコードをコンパイルしていませんが、このパターンを何度も使用しています。

注:私は過去2年間、WPFおよびASP.Netの土地にいます。どうやら、Winformsコンボボックスには、IEnumerableではなくIListが必要です。よりコストのかかる操作は、リストを作成することです。このコードは本当に簡潔なものであり、私は本当にそれをコンパイルしていません。

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();
List<KeyValuePair<string, Guid>> list = new List<KeyValuePair<string, Guid>>(hierarchies.Rows.Cast<DataRow>().Select(row => new KeyValuePair<string, Guid>(row["Name"].ToString(), (Guid)row["Guid"])));
list.Insert(0, new KeyValuePair<string,Guid>(string.Empty, Guid.Empty));
cmbHierarchies.DataSource = list;
cmbHierarchies.ValueMember = "Value";
cmbHierarchies.DisplayMember = "Key";
8
Jason Jackson

データテーブルに空白行を挿入し、検証/更新/作成で確認します

3
Steven A. Lowe

私は別の解決策を見つけました:

データテーブルが作成された直後(fillを使用する前)に、新しい行を追加し、テーブルにAcceptChangesメソッドを使用します。新しいレコードはRowState = Unchangedを取得し、データベースに追加されませんが、データテーブルとコンボボックスに表示されます。

   DataTable dt = new DataTable();
    dt.Rows.Add();
    dt.AcceptChanges();
    ...
    dt.Fill("your query");
2
Ray

DataSourceにバインドする前に、DataTableに新しいDataRowを追加できませんか?

これを行うには、DataTableのNewRow関数を使用できます。

http://msdn.Microsoft.com/en-us/library/system.data.datatable.newrow.aspx

2
Mark

このメソッドは、Jason Jacksonの提案に基づいて作成しました。

private IEnumerable<KeyValuePair<object,object>> GetDisplayTable(DataTable dataTable,  DataColumn ValueMember, string sep,params DataColumn[] DisplayMembers)
{
    yield return new KeyValuePair<object,object>("<ALL>",null);

    if (DisplayMembers.Length < 1)
        throw new ArgumentException("At least 1 DisplayMember column is required");

    foreach (DataRow r in dataTable.Rows)
    {
        StringBuilder sbDisplayMember = new StringBuilder();
        foreach(DataColumn col in DisplayMembers)
        {
            if (sbDisplayMember.Length > 0) sbDisplayMember.Append(sep);
            sbDisplayMember.Append(r[col]);
        }
        yield return new KeyValuePair<object, object>(sbDisplayMember.ToString(), r[ValueMember]);
    }
}

使用法:

bindingSource1.DataSource = GetDisplayTable(
            /*DataTable*/typedDataTable, 
            /*ValueMember*/typedDataTable.IDColumn, 
            /*DisplayColumn Seperator*/" - ",
            /*List of Display Columns*/
            typedDataTable.DB_CODEColumn,
            typedDataTable.DB_NAMEColumn);

comboBox1.DataSource = bindingSource1;
comboBox1.DisplayMember = "Key";
comboBox1.ValueMember = "Value";

//another example without multiple display data columns:
bindingSource2.DataSource = GetDisplayTable(
            /*DataTable*/typedDataTable, 
            /*ValueMember*/typedDataTable.IDColumn, 
            /*DisplayColumn Seperator*/null,
            /*List of Display Columns*/
                typedDataTable.DESCColumn );

さらに下へ、選択された値が消費されます:

if (comboBox1.SelectedValue != null)
     // Do Something with SelectedValue   
else 
     // All was selected (all is my 'empty')

これにより、ComboBoxに連結された複数の列を表示できますが、Valueメンバーを単一の識別子に維持します+ BindingSourceでイテレーターブロックを使用します。

コメントや提案は大歓迎です。

1
Vincent De Smet

私はこの方法を見つけました:

    DataTable hierarchies = new DataTable(); 

    cmbHierarchies.BeginUpdate();
    cmbHierarchies.ValueMember = this.Value;
    cmbHierarchies.DisplayMember = this.Display;
    hierarchies = DataView.ToTable();
    cmbHierarchies.DataSource = table;
    cmbHierarchies.EndUpdate();

    //Add empty row
    DataRow row = table.NewRow();
    table.Rows.InsertAt(row, 0);
    cmbHierarchies.SelectedIndex = 0;
0
Zen

えっと、データバインド後にComboBoxにデフォルトの項目を追加するだけではどうですか。

0
flipdoubt

私も同じような挑戦をしました。フォーム読み込みイベントの一部として、コントロールのSelectedIndexを-1に設定しました

すなわち

private void Form1_Load(object sender, EventArgs e)
{         
    this.TableAdapter.Fill(this.dsListOfCampaigns.EvolveCampaignTargetListMasterInfo);
    this.comboCampaignID.SelectedIndex = -1;
}

事実上、コンボボックスにデータが入力され、最初の項目が選択されます。その後、アイテムの選択が解除されます。すべてのケースで実行可能なソリューションとは限りません。

0
Trevor

データテーブルに新しい行を追加する代わりに、データをコンボボックスにバインドし、ロード時にSelectedIndexを-1に設定します。これにより、ユーザーがアイテムを選択するまで、選択はnullになります。

私は現在のプロジェクトの1つからこれを切り取りました。

        Attorney_List_CB.DataSource = DA_Attorney_List.BS.DataSource;
        Attorney_List_CB.DisplayMember = "Attorney Name";
        Attorney_List_CB.SelectedIndex = -1;      

選択をクリアするには、通常、SelectedIndexを-1に戻すボタンを挿入します。

    private void Clear_Selection_BTN_Click(object sender, EventArgs e)
    {
        Attorney_List_CB.SelectedIndex = -1;  // Clears user selection
    }

最後に、フォームのデータを検証したら、コンボボックスのSelectedIndexが-1の場合はスキップされます。または、「N/A」などのデフォルト値のタイプや、状況に応じて必要なものを生成します。

0
joshman1019

this.recieptDateTimePicker.SelectedIndex = -1; this.dateCompoBox.SelectedIndex = -1;

データをバインドし、ComboxBox.Items.Insertメソッドを使用して位置0に空白のアイテムを挿入します。フリップダウトが提案したものに似ていますが、アイテムを上部に追加します。

0
Arry