The context is as below:
A ComboBox is binded to a BindingSource.
That BindingSource is then binded to a DataModel.
That ComboBox has a DataSource of a list of objects. Yet, this list maybe set to null when under some conditions, and maybe restored under some other conditions.
My problem is on the last step of the below scenario:
- At the begining, ComboBox's dataSource is a List of 3 items (eg. item1, item2, item3)
- Now comboBox show item1 as the default selected item
- User pick item 3 on comboBox
- Now comboBox show item3 as the selected item
- ...some other event happened and cause comboBox's dataSource become null
- Now comboBox has no selected item (which selectedIndex is -1)
- ...some other event happened and cause comboBox's dataSource become the original list
8. Now comboBox's selected item is back to item3, but NOT item1
I would like to understand why will it be so, as i don't want the prvious value being preserved after the dataSource has been null.
Here is how I setup the code, with 1 comboBox binded to the a bindingSource and data-model, and 2 buttons to simulate change of comboBox's dataSource.
BindingModel model = new BindingModel();
public Form1()
{
InitializeComponent();
// BindingSource <--> model
cbxBindingSource.DataSource = typeof(BindingModel);
cbxBindingSource.Add(model);
// ComboxBox <-SelectedValue-> BindingSource <--> model.Result
comboBox.DataBindings.Add(new Binding("SelectedValue", cbxBindingSource, "Result", true, DataSourceUpdateMode.OnPropertyChanged));
}
// Simulate a re-usable list
List<BinderHelper<string>> list = new List<BinderHelper<string>>
{
new BinderHelper<string>("Item1", "1"),
new BinderHelper<string>("Item2", "2"),
new BinderHelper<string>("Item3", "3"),
new BinderHelper<string>("Item4", "4"),
};
private void button2_Click(object sender, EventArgs e)
{
// Simulate triggers that will result in a well-populated data-source
comboBox.DisplayMember = "DisplayMember";
comboBox.ValueMember = "ValueMember";
comboBox.DataSource = list;
}
private void button3_Click(object sender, EventArgs e)
{
// Simulate triggers that will result in a empty data-source
comboBox.DataSource = null;
}
private class BindingModel
{
// Simple model to bind to the selected value of the comboBox
public string Result { get; set; }
}
private class BinderHelper<T>
{
// Use for binding purpose
public string DisplayMember { get; set; }
public T ValueMember { get;set; }
public BinderHelper(string display, T value)
{
DisplayMember = display;
ValueMember = value;
}
}
However, if i create a new list, with new items everytime, then the issue will not be seen. However, it is not possible in my actual code as the list is always the same instance, just a matter of put it on datasource or not.
Sample as below:
private void button2_Click(object sender, EventArgs e)
{
// Create new list everytime
List<BinderHelper<string>> list = new List<BinderHelper<string>>
{
new BinderHelper<string>("Item1", "1"),
new BinderHelper<string>("Item2", "2"),
new BinderHelper<string>("Item3", "3"),
new BinderHelper<string>("Item4", "4"),
};
// Simulate triggers that will result in a well-populated data-source
comboBox.DisplayMember = "DisplayMember";
comboBox.ValueMember = "ValueMember";
comboBox.DataSource = list;
}
A workaround i did is to check the current dataSource:
If the current dataSource is null, and the new dataSource is not, Then set SelectedIndex = 0
But i would really want to avoid this manual intervention other than changing the datasource.
Appreciate anyone have a solution on that, thanks in adv!
Updated the working code with suggestions from Ivan:
public Form1()
{
InitializeComponent();
model = new BindingModel();
// BindingSource <--> model
cbxToModelBindingSource.DataSource = typeof (BindingModel);
cbxToModelBindingSource.Add(model);
// New dedicated source to provide the list in combox
// BindingSource <--> comboBox
listToCbxBindingSource = new BindingSource(components);
listToCbxBindingSource.DataSource = typeof (BinderHelper<string>);
comboBox.DataSource = listToCbxBindingSource;
// ComboxBox <--SelectedValue--> BindingSource <--> model.Result
comboBox.DataBindings.Add(new Binding("SelectedValue", cbxToModelBindingSource, "Result", true, DataSourceUpdateMode.OnPropertyChanged));
}
// Simulate triggers that will result in a well-populated data-source
private void button2_Click(object sender, EventArgs e)
{
listToCbxBindingSource.DataSource = list;
}
// Simulate triggers that will result in a empty data-source
private void button3_Click(object sender, EventArgs e)
{
listToCbxBindingSource.DataSource = null;
}
// Additional button to test two-way-bindings
private void button4_Click(object sender, EventArgs e)
{
model.Result = "4";
}