Question

I'm trying to solve a simple problem using LINQ, which I'm just learning.

I have a collection of strings, in this case representing serial ports, that will be displayed in a control, but must be ordered. The original collection is unsorted, and I don't necessarily want to modify it, or make a copy of it. So, I created a property of type IEnumerable and bound it to a ComboBox.

This works great, the ComboBox has the correct contents in the correct order. However, if the original collection changes, either a) The ComboBox doesn't get notified properly when the original collection changes, or b) the LINQ query isn't being refreshed.

After trying some different things, I can't see how the following code doesn't work. I must be missing something.

There might be extra code here that is redundant... Anyway, the source followed by the XAML for the controls:

public partial class MainWindow : Window
{
    ObservableCollection<string> original = new ObservableCollection<string>();
    public ObservableCollection<string> OriginalList {
        get { return (original); }
    }

    private IEnumerable<string> _portList;
    public IEnumerable<string> PortList {
        get { return (_portList); }
    }

    public MainWindow() {
        InitializeComponent();

        original.Add("COM5");
        original.Add("COM1");
        original.Add("COM3");
        original.Add("COM4");
        original.Add("COM2");

        original.CollectionChanged += new NotifyCollectionChangedEventHandler(OriginalChanged);

        _portList = (
            from port in original
            orderby port ascending
            select port
        );

        DataContext = this;
    }

    private void AddPortButton_Click(object sender, RoutedEventArgs e) {
        original.Add("COM2.5");
    }

    void OriginalChanged(Object sender, NotifyCollectionChangedEventArgs args) {
        NotifyPropertyChanged("PortList");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info) {
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

The XAML:

    <ComboBox Name="SerialPortListBox" ItemsSource="{Binding PortList}" Width="100" />
    <ComboBox Grid.Row="1" Name="OriginalListBox" ItemsSource="{Binding OriginalList}" Width="100" Margin="0,5,0,0"/>
    <Button Grid.Column="1" Name="AddPortButton" Content="Add Port 2.5" Width="100" Margin="10,0,0,0" Click="AddPortButton_Click" />
Was it helpful?

Solution

I think you will only get the change notifications you want by binding your ComboBox to an ObservableCollection. This class implements the interface INotifyCollectionChanged, which is what the innards of WPF rely on to notify the UI that updates are required.

I see you are trying to get around this by implementing INotifyPropertyChanged for PortList, however this won't work the way you want. That interface does not trigger the appropriate event to trigger the combobox to refresh. INotifyCollectionChanged tells the listener that a collection has changes (i.e; 'Add', 'Remove', 'Move', 'Replace', 'Reset'), whereas INotifyPropertyChanged only indicates that some value has changed in the bound object. The ComboBox will not respond to an INotifyPropertyChanged event, in fact it's probably not even subscribing to events of that type.

So, either bind directly to the underlying datasource, or implement a 2nd ObservableCollection on top of that, rather than only an IEnumerable, which does not notify on change.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top