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

我在WinForms应用程序上有一个combobox,其中可以选择一个项目,但它不是必需的。 因此,我需要一个’Empty’第一项来表示没有设置任何值。

combobox绑定到从存储过程返回的DataTable(我没有在我的UI控件上为匈牙利表示法道歉:p):

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

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

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

更新:这是DataTable.NewRow(),我已经空白了,谢谢。 我已经对你进行了修改(到目前为止所有3个答案)。 在我决定’回答’之前,我试图让Iterator模式工作

更新:我认为这个编辑让我进入了社区Wiki领域,我决定不指定单个答案,因为它们都有其领域背景的优点。 感谢您的集体意见。

你可以做两件事:

  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 

    然后将dataview设置为ComboBox DataSource

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

 cmbHierarchies.SelectedIndex = -1; 

我通常会为这类事物创建一个迭代器。 它可以避免污染您的数据,并且可以很好地处理数据绑定:

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

免责声明:我没有编译此代码,但多次使用此模式。

注意:我在过去几年里一直在WPF和ASP.Net领域。 显然,Winformscombobox需要IList,而不是IEnumerable。 更昂贵的操作是创建列表。 这段代码真的很简洁,我真的,真的没有编译它:

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

在数据表中插入一个空行,并在validation / update / create中检查它

你不能在将DataTow绑定到DataSource之前将其添加到DataTable吗?

您可以使用DataTable的NewRow函数来实现此目的:

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

我找到了另一个解决方

在创建数据表之后(在使用填充之前),添加新行并对表使用AcceptChanges方法。 新记录将获得RowState = Unchanged,并且不会添加到数据库中,但会在数据表和combobox中显示。

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

我根据Jason Jackson的建议写了这个方法:

 private IEnumerable> GetDisplayTable(DataTable dataTable, DataColumn ValueMember, string sep,params DataColumn[] DisplayMembers) { yield return new KeyValuePair("",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(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的迭代器块,BindingSource可能对您的情况有点过分。

欢迎提出建议和建议。

呃,你不能只是在数据绑定后向ComboBox添加一个默认项吗?

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

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

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

实际上,填充了combobox并选择了第一个项目。 然后取消选择该项目。 可能不是所有案例的可行解决方案。

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

我发现了这种方式:

  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; 

不是向数据表添加新行,而是将数据绑定到combobox,并在加载时将SelectedIndex设置为-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 } 

最后,一旦我validation了我的表单上的数据,如果任何combobox的SelectedIndex是-1然后它被跳过,或者我将生成某种类型的默认值,例如“N / A”或我在这种情况下需要的任何东西。