Why do I need to update a bound combobox manually?

Viewed 77

My Setup

I have following (pseudo) EF classes:

class Department {
    int ID,
    string Name
}

class DepartmentCustomer {
    int ID,
    int CustomerID,
    Customer Customer,
    string Information,
    int DepartmentID,
    Department Department
}

Class Customer {
    int ID,
    string Name,
    int? CityID,
    City City
}

Class City{
    int ID,
    string Name,
    string PostalCode
}

I have a form with a BindingSource for List(of DepartmentCustomer) and the following fields :

  • DepartmentCustomer.Information as a (read-write) textbox
  • DepartmentCustomer.Customer.Name as a (readonly) textbox
  • DepartmentCustomer.Customer.City as a (read-write) combobox.

CustomerForm

The combobox for the city name has following properties:

ComboBoxCity.DataSource = ListOfCities  ' = List(Of City)
ComboBoxCity.ValueMember = "Id"
ComboBoxCity.DisplayMember = "Name"
ComboBoxCity.DataBindings.Add(New Binding("SelectedItem", DepartmentCustomerBindingSource, "Customer.City", True, DataSourceUpdateMode.OnPropertyChanged))

My Problem

When DepartmentCustomerBindingSource.CurrentItemChanged occurs, the Combobox is not synchronized; it sometimes updates to the correct value, but when navigating further through the BindingSource it keeps the previous item selected

So, I have to do the following to update the combobox manually

Private Sub DepartmentCustomerBindingSource_CurrentItemChanged(sender As Object, e As EventArgs) Handles DepartmentCustomerBindingSource.CurrentItemChanged
    If DepartmentCustomerBindingSource.Current.Contact.City Is Nothing then
        ComboBoxCity.SelectedIndex = -1
    Else
        ComboBoxCity.SelectedItem = DepartmentCustomerBindingSource.Current.Contact.City
    End if
End Sub

(for the sake of simplicity I have ommitted casting in my code above. Also these example classes might not make sense IRL)

EDIT

Even the code above does not do what I want. For example: I have two instances of DepartmentCustomer, one with a Contact.City, the second without a Contact.City . When the form opens at the first instance, it shows the city; when I navigate to the second record, the combobox becomes empty, but when I go back to the first combobox, it stays empty; even more surprising, the first record has been updated to Contact.City = Nothing :'(

EDIT 2: My own not-preferred solution

I have removed the databinding from the combobox (ComboBoxCity.DataBindings.Add(New Binding("SelectedItem", DepartmentCustomerBindingSource, "Customer.City", True, DataSourceUpdateMode.OnPropertyChanged))) and added the following sub

Private Sub ComboBoxCity_SelectedValueChanged(sender As Object, e As EventArgs) Handles ComboBoxCity.SelectedValueChanged
    DepartmentCustomerBindingSource.Current.Contact.City = ComboBoxCity.SelectedItem
End Sub

This works, but since I have quite a lot of these comboboxes in my form, I think there has to be an "automatic" way of synchronizing a two-way bound combobox...

Edit 3: I throw in the towel

Even my above "solution" is not working as expected; the Contact.City of the instances are not updated correctly when using the above code...

My Question

Why do I have to to this manually; Am I missing something? I would think that by setting the DataBinding, it would update the SelectedItem whenever the bound BindingSource is being navigated;

3 Answers

There seems to be an issue (bug?) with ComboBox data binding to SelectedItem property and null (Nothing) source values.

From the other side binding to SelectedValue has no such issue. So the solution/workaround is to bind to SelectedItem for updating the data soource and to SelectedValue for updating the control:

ComboBoxCity.DataBindings.Add(New Binding("SelectedItem", DepartmentCustomerBindingSource, "Customer.City", True, DataSourceUpdateMode.OnPropertyChanged))    
ComboBoxCity.DataBindings.Add(New Binding("SelectedValue", DepartmentCustomerBindingSource, "Customer.City.ID", True, DataSourceUpdateMode.Never))

Most of time when a control doesn't update it's an issue related to the interfaces INotifyPropertyChanged and IBinding​List not (correctly) implemented. – Marco Guignard

This got me thinking: I was indeed using the following to populate the DepartmentCustomerBindingSource

DepartmentCustomerBindingSource.DataSource = DBContext.DepartmentCustomer.Local.ToList

When changing that to

DepartmentCustomerBindingSource.DataSource = DBContext.DepartmentCustomer.Local.ToBindingList

it works :-/

However, how do I get this working when setting the datasource to a specific record instead of a (Binding)List?

DepartmentCustomerBindingSource.DataSource = DBContext.DepartmentCustomer.Local.First

After a lot of trial-and-error I found the following solution:

Taking following code in consideration:

ComboBoxCity.DataSource = ListOfCities  ' = List(Of City)
ComboBoxCity.ValueMember = "Id"
ComboBoxCity.DisplayMember = "Name"
ComboBoxCity.DataBindings.Add(New Binding("SelectedItem", DepartmentCustomerBindingSource, "Customer.City", True, DataSourceUpdateMode.OnPropertyChanged))

the thing that got me going was to set the ComboBoxCity.DataSource to a type that implements IBindingList, like following:

DBContext.Cities.Load
CitiesBindingSource.DataSource = DBContext.Cities.Local.ToBindingList
ComboBoxCity.DataSource = CitiesBindingSource

So, it was not the bound data (in this case DepartmentCustomerBindingSource) that had to be a BindingList, but only the Datasource had to implement IBindingSource...

Related