Home > Enterprise >  ComboBox bound to List<Class> is not sorted in dropdown but sorts correctly with arrow keys wh
ComboBox bound to List<Class> is not sorted in dropdown but sorts correctly with arrow keys wh

Time:10-01

I have a ComboBox control in my MainWindow.xaml that is bound to a List<Job> where Job is a class that has multiple properties and implements INotifyPropertyChange and IComparable (I also tried removing IComparable implementation). The MainWindow class also implements INotifyPropertyChange. When I load this list from a database, the bound ComboBox control is populated correctly. The ComboBox control is defined in xaml as

<ComboBox x:Name="JobsComboBox" ItemsSource = "{Binding Path=AllJobs, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" 
                  ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="400" 
                  MaxWidth="400" HorizontalContentAlignment="Center" />

I have a separate control that allows the user to sort by a particular property of Job. When the user selects a different sort property, I use a delegate to sort my list and notify the change like so:

public void SortJobsListByProperty(string? propertyName)
    {
        if (AllJobs != null) AllJobs.Sort(delegate(Job x, Job y)
        {
            int result = 0;

            switch (propertyName)
            {
                case "Quantity":
                    result = x.Quantity.CompareTo(y.Quantity);
                    break;
                case "Due Date":
                    result = x.DueDate.CompareTo(y.DueDate);
                    break;
                case "Siemens PN":
                    result = x.Assembly.SiemensPN.CompareTo(y.Assembly.SiemensPN);
                    break;
                case "Customer PN":
                    result = x.Assembly.CustomerPN.CompareTo(y.Assembly.CustomerPN);
                    break;
                default:
                    result = x.JobNumber.CompareTo(y.JobNumber);
                    break;
            }
            return result;
        });
        NotifyPropertyChange("AllJobs");
    }

Here's where it gets weird: If the ComboBox has keyboard focus, I can navigate through the items with the up/down arrow keys, and the selected item is in the correct sorted order. However, if I click the drop-down arrow on the ComboBox, the displayed list is NOT in the correct order, but shows the order as how the list was originally loaded. It's as if the selected item index follows the sorting, but the displayed drop-down is not updated.

I have searched high and low for other threads with similar problems to no avail. The closest solution I found says that anObservableCollection should be used instead of List<Class> if the collection needs to be sorted into a ComboBox. But I can't figure out how to sort an ObservableCollection and feel that the order in the drop-down should match the arrow key navigation. The control is obviously seeing the change to the list or the arrow keys would not navigate in correct order as I understand. I am new to data-binding and property change notifications so I apologize if I'm missing something obvious. Also apologize if poorly formatted or unclear, I can try to clarify. Thanks in advance for your help!

Edit: I also notice that if I use the drop-down to select a ListItem, the current item is actually the correct item, just the string in the combo box selection is incorrect. E.g. If I navigate to the 3rd item in the ListBox either by arrow key or drop-down selection, the current item is actually the 3rd item in the current sort order, but it's properties do not match the ToString() method of that item's field. The 3rd JobNumber is 123456 and the third item in the drop-down is 123456, but the string displayed for that selection contains some other JobNumber like 666666 based on the original sorting. Seems like the ComboBox actually makes the correct selection, but its string doesn't match.

Example of non-matching ListBox selection versus the current item ContentControl

CodePudding user response:

Ok, after messing around with a lot of hopeful fixes, I finally found what works. Apparently the ComboBox items do not update when the sort order of the bound list changes. Instead, the ComboBox's underlying Items property must be refreshed. So I simply added JobsComboBox.Items.Refresh() to the bottom of my SortJobsListByProperty(string) method, after sorting the bound List<Job>, and everything is working like it should. Strings in the ComboBox are updated and match with their synchronized current item.

Hope this helps someone wrestling with this problem in the future.

CodePudding user response:

Slightly more in-depth answer/explanation:

The code NotifyPropertyChange("AllJobs") is notifying all listeners that "the value of this property has changed". But, technically, it hasn't. The AllJobs property is still holding the same List<Job> instance. Something inside that List<Job> has changed (the order of the items), but it's still the same object (at no point was there ever a new List<Job>()). So the data binding framework gets the NotifyPropertyChange call, examines the value of AllJobs, realizes the List<Job> that is there now is the same List<Job> it already has, and then leaves it at that assuming everything is already up to date. It assumes nothing inside that object has changed because nothing is telling it otherwise.

ObservableCollection<T> implements INotifyCollectionChanged, which is how you tell the binding system "one or more of the elements in my list has changed, please update it".

This answer actually has most of what you need. It gives you the "proper solution" of CollectionViewSource, but also does show you how you could sort an ObservableCollection<T> if you really want to (by creating a new ObservableCollection<T> and using OrderBy from LINQ).

  • Related