C# 如何在绑定到 DataTable 的 ComboBox 中插入“空”字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/199642/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:43:10  来源:igfitidea点击:

How to insert 'Empty' field in ComboBox bound to DataTable

c#combobox

提问by johnc

I have a combo box on a WinForms app in which an item may be selected, but it is not mandatory. I therefore need an 'Empty' first item to indicate that no value has been set.

我在 WinForms 应用程序上有一个组合框,可以在其中选择一个项目,但这不是强制性的。因此,我需要一个“空”的第一项来指示尚未设置任何值。

The combo box is bound to a DataTable being returned from a stored procedure (I offer no apologies for Hungarian notation on my UI controls :p ):

组合框绑定到从存储过程返回的数据表(我不为我的 UI 控件上的匈牙利表示法道歉 :p ):

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

How can I insert such an empty item?

我怎样才能插入这样一个空项目?

I do have access to change the SP, but I would really prefer not to 'pollute' it with UI logic.

我确实有权更改 SP,但我真的不想用 UI 逻辑“污染”它。

Update:It was the DataTable.NewRow() that I had blanked on, thanks. I have upmodded you all (all 3 answers so far anyway). I am trying to get the Iterator pattern working before I decide on an 'answer'

更新:这是我空白的 DataTable.NewRow(),谢谢。我已经升级了你们所有人(到目前为止所有 3 个答案)。在我决定一个“答案”之前,我试图让迭代器模式工作

Update:I think this edit puts me in Community Wiki land, I have decided not to specify a single answer, as they all have merit in context of their domains. Thanks for your collective input.

更新:我认为这个编辑让我进入了社区 Wiki 领域,我决定不指定一个单一的答案,因为它们在其域的上下文中都有优点。感谢您的集体投入。

采纳答案by Vivek

There are two things you can do:

您可以做两件事:

  1. Add an empty row to the DataTablethat is returned from the stored procedure.

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

    Create a DataView and sort it using ObjectLogicalName column. This will make the newly added row the first row in DataView.

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

    Then set the dataview as DataSourceof the ComboBox.

  2. If you really don't want to add a new row as mentioned above. You can allow the user to set the ComboBoxvalue to null by simply handling the "Delete" keypress event. When a user presses Delete key, set the SelectedIndexto -1. You should also set ComboBox.DropDownStyleto DropDownList. As this will prevent user to edit the values in the ComboBox.

  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. 如果您真的不想如上所述添加新行。您可以允许用户ComboBox通过简单地处理“删除”按键事件来将该值设置为 null。当用户按下 Delete 键时,设置SelectedIndex为 -1。您还应该设置ComboBox.DropDownStyleDropDownList. 因为这将阻止用户编辑ComboBox.

回答by Mark

Cant you add a new DataRow to the DataTable before you bind it to your DataSource?

在将新的 DataRow 绑定到 DataSource 之前,您不能向 DataTable 添加一个新的 DataRow 吗?

You can use the NewRow function of the DataTable to achieve this:

您可以使用 DataTable 的 NewRow 函数来实现这一点:

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

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

回答by Steven A. Lowe

insert a blank row in your datatable, and check for it in validation/update/create

在数据表中插入一个空行,并在验证/更新/创建中检查它

回答by Jason Hymanson

I usually create an iterator for this type of thing. It avoids polluting your data, and works well with data-binding:

我通常为这种类型的东西创建一个迭代器。它避免污染您的数据,并且与数据绑定配合得很好:

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

Disclaimer: I have not compiled this code, but have used this pattern many times.

免责声明:我没有编译过这段代码,但已经多次使用过这种模式。

Note:I have been in WPF and ASP.Net land for the last couple of years. Apparently the Winforms combo box wants an IList, not an IEnumerable. A more costly operation would be to create a list. This code is really stream-of-conciseness and I really, really have not compiled it:

注意:过去几年我一直在 WPF 和 ASP.Net 领域工作。显然,Winforms 组合框需要一个 IList,而不是一个 IEnumerable。成本更高的操作是创建列表。这段代码真的很简洁,我真的,真的没有编译它:

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

回答by flipdoubt

Er, can't you just add a default item to the ComboBox after data binding?

呃,数据绑定后不能直接在ComboBox中添加一个默认项吗?

回答by Arry

I would bind the data then insert an blank item at position 0 using the ComboxBox.Items.Insert method. Similar to what flipdoubt suggested, but it adds the item to the top.

我将绑定数据,然后使用 ComboxBox.Items.Insert 方法在位置 0 插入一个空白项目。与flipdoubt 建议的类似,但它将项目添加到顶部。

回答by Arry

cmbHierarchies.SelectedIndex = -1;

回答by Vincent De Smet

I wrote this method based on the suggestions here by Jason Hymanson:

我根据 Jason Hymanson 的建议编写了此方法:

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

Usage:

用法:

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

further down, where the Selected Value is consumed:

再往下,选择的值被消耗:

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

This will allow to display several columns concatenated in the ComboBox, while keeping the Value member to the single identifier + it uses the iterator block with the BindingSource, BindingSource might be overkill for your situation.

这将允许显示在 ComboBox 中连接的多个列,同时将 Value 成员保持为单个标识符+它使用带有 BindingSource 的迭代器块,BindingSource 对您的情况可能有点过分。

Сomments and suggestions are welcome.

欢迎提出意见和建议。

回答by Ray

I found another solution:

我找到了另一个解决方案:

Just after your data table is created (before using fill), add new row and use AcceptChanges method to the table. The new record would get RowState = Unchanged, and would not be added to database, but would be visible in your datatable and combobox.

在创建数据表之后(在使用填充之前),添加新行并使用 AcceptChanges 方法到表中。新记录将获得 RowState = Unchanged,并且不会添加到数据库中,但会在您的数据表和组合框中可见。

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

回答by Trevor

I had a similar challenge. As part of the form load event I set the SelectedIndex of the control to -1

我也遇到过类似的挑战。作为表单加载事件的一部分,我将控件的 SelectedIndex 设置为 -1

ie

IE

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

Effectively, the combo box is populated and the first item is selected. Then the item is unselected. May not be a viable solution for all cases.

实际上,组合框已填充并选择了第一个项目。然后取消选择该项目。可能不是所有情况下都可行的解决方案。