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 s
this.recieptDateTimePicker.SelectedIndex = -1;
this.dateCompoBox.SelectedIndex = -1;
I wrote this method based on the suggestions here by 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]);
}
}
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.
Сomments and suggestions are welcome.
I had a similar challenge. As part of the form load event I set the SelectedIndex of the control to -1
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.
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:
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";
Instead of adding a new row to your datatable, just bind the data to the combobox and at load set the SelectedIndex to -1. This will cause the selection to be null until the user selects an item.
I clipped this from one of my current projects.
Attorney_List_CB.DataSource = DA_Attorney_List.BS.DataSource;
Attorney_List_CB.DisplayMember = "Attorney Name";
Attorney_List_CB.SelectedIndex = -1;
In order to clear the selection I usually insert a button that sets the SelectedIndex back to -1.
private void Clear_Selection_BTN_Click(object sender, EventArgs e)
{
Attorney_List_CB.SelectedIndex = -1; // Clears user selection
}
Finally, once I validate the data on my form, if the SelectedIndex of any combobox is -1 then it is skipped, or I will generate some type of default value such as "N/A" or whatever I need under the circumstances.
I have found this way:
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;