-1

I have an ObservableCollection bound to a ComboBox and during the program I add or remove elements to this collection. I want these to be reflected in the ComboBox but for some reason no change is being triggered. What am I doing wrong?

ComboBox:

   <ComboBox x:Name="Cbox2" ItemsSource="{Binding ExportChoices}" 
      Height="30" Width="200" Margin="10" FontSize="18" 
      HorizontalAlignment="Center" Grid.Column="1"/>

Code behind with method in which I remove items from bound Observable Collection:

 public partial class ExportPage : Page
    {
        private ObservableCollection<String> exportChoices = new();

        public ObservableCollection<String> ExportChoices
        {
            get { return exportChoices; }
            set
            {
                exportChoices = value;
            }
        }

        public ExportPage()
        {
            InitializeComponent();

            this.DataContext = this;

            ExportChoices = new()
            {
                "Name",
                "Northing",
                "Easting",
                "Elevation",
                "Description"
            };
        }

    private void Cbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ComboBox comboBox = sender as ComboBox;
            switch (comboBox.Name)
            {
                case "Cbox1":
                    if (!ExportChoices.Contains(Cbox1Selection))
                    {
                        ExportChoices.Add(Cbox1Selection);
                    }
                    Cbox1Selection = comboBox.SelectedItem.ToString();
                    break;
                case "Cbox2":
                    if (!ExportChoices.Contains(Cbox2Selection))
                    {
                        ExportChoices.Add(Cbox2Selection);
                    }
                    Cbox2Selection = comboBox.SelectedItem.ToString();
                    break;
                case "Cbox3":
                    if (!ExportChoices.Contains(Cbox3Selection))
                    {
                        ExportChoices.Add(Cbox3Selection);
                    }
                    Cbox3Selection = comboBox.SelectedItem.ToString();
                    break;
                case "Cbox4":
                    if (!ExportChoices.Contains(Cbox4Selection))
                    {
                        ExportChoices.Add(Cbox4Selection);
                    }
                    Cbox4Selection = comboBox.SelectedItem.ToString();
                    break;
                case "Cbox5":
                    if (!ExportChoices.Contains(Cbox5Selection))
                    {
                        ExportChoices.Add(Cbox5Selection);
                    }
                    Cbox5Selection = comboBox.SelectedItem.ToString();
                    break;
            }

            if (ExportChoices.Contains(comboBox.SelectedItem.ToString()))
            {
                ExportChoices.Remove(comboBox.SelectedItem.ToString());
            }
        }
    }

Jombo
  • 15
  • 5
  • It won't fix the issue, but `ObservableCollection` properties should be `{ get; }`-only - you should remove the `set;` from the property. Microsoft's API design guidelines emphasis the need for properties exposing mutable collections should not be able to be replaced outright by an external consumer. – Dai May 08 '23 at 20:22
  • Have you used the XAML Binding Errors tool in VS to see if there's unexpected `DataContext` shenanigans? Also, do you have a good reason for subclassing `Page` instead of using MVVM? – Dai May 08 '23 at 20:23
  • 7
    `ExportChoices = new()` <-- That's your problem: you're _replacing_ the collection object instance. Never replace an `ObservableCollection` - you should only ever add-and-remove from it (even if it sounds inefficient because it doesn't support mass/bulk updates, unfortunatley that's a fact-of-life in WPF) – Dai May 08 '23 at 20:24
  • @Dai Could you give details about why new() should not make ObservableCollection? I was curious. – Ozgur Saklanmaz May 08 '23 at 21:05
  • @OzgurSaklanmaz My apologies, but I’m unsure of what you’re trying to say there. – Dai May 08 '23 at 21:09
  • @Dai Can you explain why ExportChoices = new() should not be done? Why only addition should be made. Why shouldn't the object instance be modified? – Ozgur Saklanmaz May 08 '23 at 21:14
  • @OzgurSaklanmaz Are you familiar with the concept of object-identity? And how INotifyPropertyChanged works? – Dai May 08 '23 at 21:18
  • Because the UI subscribed to events on the ObservableCollection that was just replaced with a brand new ObservableCollection, which has events that aren't hooked to the UI anymore. If you also supported INotifyPropertyChanged interface and raised that even when you call the property setter, it would work, but as @Dai said earlier, collections should not have setters. If you didn't allow that to be set again (as is currently happening) then the problem is just ... not present. – Steve May 08 '23 at 21:38
  • @Steve In this case, though, the problem would still exist: `get;`-only auto-properties can still be overwritten-to multiple times within the same constructor, or by the underlying field initialiser _and_ any subsequent ctor (such as in `: this(…)` ctor calls in C#. – Dai May 08 '23 at 22:12
  • @Dai I have made this change and it's unfortunately still not updating. It does give me a binding failure saying the following: "Collection of type 'netDxf.Collections.ObservableCollection`1[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' has been changed without raising a CollectionChanged event. Support for this is incomplete and inconsistent, and will be removed completely in a future version of WPF. Consider either (a) implementing INotifyCollectionChanged, or (b) avoiding changes to this type of collection." – Jombo May 08 '23 at 23:09
  • 1
    @Jombo please post a link to a complete (but minimal) example project that demonstrates this issue, along with your exact changes. – Dai May 08 '23 at 23:12
  • Bindings are evaluated after the constructor has been executed. During construction time, it does not matter whether you set the DataContext before or after setting a property. So calling `ExportChoices = new ...` is not a problem at all. The property should still be read-only: `public ObservableCollection ExportChoices { get; }` – Clemens May 09 '23 at 06:41
  • @Clemens XAML Bindings will be evaluated after the ctor returns, yes - _but_ I suspect there's something going on in the generated `InitializeComponent` method. – Dai May 09 '23 at 09:37
  • @Dai what exactly should that be - except data binding? The code in the question is working. There is however something going on later that we do not yet know. – Clemens May 09 '23 at 10:05
  • @Clemens Normally, yes - but I've encountered at least 1 misbehaving third-party WPF component library that tried to get too clever... – Dai May 09 '23 at 10:08
  • I noticed the OP is using a `ComboBox` - and this other SO post claims that WPF is very particular about how you do binding with a ComboBox: https://stackoverflow.com/questions/561166/binding-a-wpf-combobox-to-a-custom-list – Dai May 09 '23 at 10:09
  • You could simply try the code to see that it works... – Clemens May 09 '23 at 10:12
  • @Clemens You're assuming we all have instant access to ad-hoc WPF development tools (_rip_ XAMLPad!) - which I don't - or that the issue can be recreated using _only_ the OP's code so-far (of which I'm skeptical to the point of not wanting to waste my time on seculative experiments) - but so far do I agree with you that my point about `ExportChoices = new()` very likely is not the actual cause here (despite the high votecount - as it's still good-advice for handling `ObservableCollection` in general) – Dai May 09 '23 at 10:18
  • As I said, the property should be declared read-only and could then correctly be initialized in the constructor - as OP did. The backing field and its initial assignment are redundant. – Clemens May 09 '23 at 10:23
  • 1
    @Jombo: Please post the code where you "add or remove elements to this collection". Your sample code only includes the initialization of the collection. – mm8 May 10 '23 at 14:18

1 Answers1

0

It turns out my ObservableCollection was defaulting to a type made in an import I was using instead of the typical System.Collections type. It appears that is why I wasn't getting a collection changed event.

Jombo
  • 15
  • 5