1

I have tried numerous approaches based on many examples on StackOverflow and elsewhere, but just can't get this to work. I want the list of choices for ComboBox B to be different based on the selected item in ComboBox A. Could someone please help me determine what I'm doing wrong? (I am using MVVMLight.)

Here are the ItemsSources for the two comboboxes:

private ObservableCollection<SetupSF> _sourceForSFList = new ObservableCollection<SetupSF>();

public ObservableCollection<SetupSF> SourceForSFList
{
    get { return _sourceForSFList; }
    set
    {
        _sourceForSFList = value;
    }
}

private ObservableCollection<SetupSFE> _sourceForSFEList = new ObservableCollection<SetupSFE>();
public ObservableCollection<SetupSFE> SourceForSFEList
{
    get { return _sourceForSFEList; }
    set
    {
        _sourceForSFEList = value;
    }
}

Here is the selector. I tried using RelayPropertyChanged even though it's an observable collection, but that had no effect. I've verified that contents of the list is updating by setting a breakpoint. The list updates, but apparently does not notify the second combobox. _selectedSF.OwnedSFEs is itself an ObservalbleCollection.

public SetupSubfactor SelectedSF
{
    get { return _selectedSF; }
    set 
    {
        _selectedSF = value;
        if (_selectedSF != null)
        {
            SourceForSFEList = _selectedSF.OwnedSFEs;
        }
    } 
}   

Here is the code for the SF type. It is defined outside the ViewModel. (Could that be the problem?

public class SetupSF : ViewModelBase
{
    private string _iD;
    public string Id
    {
        get { return _iD; }
        set
        {
            _iD = value;
            RaisePropertyChanged(nameof(Id));
        }
    }

    private string _sfName;
    public string SFName
    {
        get { return _sfName; }
        set
        {
            _sfName = value;
            RaisePropertyChanged(nameof(SFName));
        }
    }

    public ObservableCollection<SetupSFE> OwnedSFEs { get; set; }
}

Here is the XAML for the comboboxes:

<ComboBox x:Name="ComboBox A" 
          Width="350" 
          ItemsSource="{x:Bind ViewModel.SourceForSFList}"
          SelectedItem="{x:Bind ViewModel.SelectedSF, Mode=TwoWay}"
          DisplayMemberPath="SFName"/>

<ComboBox x:Name="Comobox B"
          Width="350"
          ItemsSource="{x:Bind ViewModel.SourceForSFEList}"
          SelectedItem="{x:Bind ViewModel.SelectedSFE, Mode=TwoWay}   
          DisplayMemberPath="SFEName"/>
Matt
  • 145
  • 1
  • 13
  • 1
    Make sure that your model containing `SourceForSFEList` is inheriting `INotifyPropertyChanged` and add your `RaisePropertyChanged` inside the setter of your `SourceForSFEList` to notify your UI about the changed `ItemSource` of `Combobox B` – Nebelkraehe Aug 11 '18 at 16:26
  • Thanks. I took your advice and double checked. The SourceForSFEList is defined in the ViewModel. The ViewModel inherits from MVVMLight's ViewModelBase, which inherits from ObservableObject, which inherits INotifyPropertyChanged. I also added RaisePropertyChanged to the setter of SourceForSFEList. I'm afraid it's still not working. – Matt Aug 11 '18 at 17:34
  • Have you checked if the `SelectedItem` of `ComboBox A` gets correctly transferred to your model? Set a breakpoint to the line where you call `SourceForSFEList = _selectedSF.OwnedSFEs;` and take a look if it gets called if you change the selection of `ComboBox A`. If so, does the `OwnedSFEs` you assign to `SourceForSFEList` contains any elements? – Nebelkraehe Aug 11 '18 at 17:41
  • Thanks again. Yes, I have checked and the breakpoint is hit if the selection is changed in ComboBox A. The contents of SourceforSFEList is correct. (I have two items in ComboBox A to test with, and the contents of SourceforSFEList is correct for both test cases.) – Matt Aug 11 '18 at 20:05

1 Answers1

1

I believe you want to change your SourceForSFEList to look like this:

private ObservableCollection<SetupSFE> _sourceForSFEList = new ObservableCollection<SetupSFE>();
public ObservableCollection<SetupSFE> SourceForSFEList
{
    get { return _sourceForSFEList; }
    set
    {
        _sourceForSFEList.Clear();
        if (value != null)
        {
            foreach (var item in value)
            {
                _sourceForSFEList.Add(item);
            }

        }
    }
}

The Binding system is calling your "get" once, and is getting the initial Collection that _sourceForSFEList points to. Later, you are changing _sourceForSFEList to point to another collection, but the Binding system doesn't know anything about that collection, and so the UI doesn't update. Once the binding system binds a UIElement to _sourceForSFEList, you need to update that original collection, instead of changing the value of _sourceForSFEList to point to a new collection.

I've tried to infer some of your original classes. Here is a working example that I used to have one CombBox's selection update another's.

   public class ViewModel
    {
        private ObservableCollection<SetupSF> _sourceForSFList = new ObservableCollection<SetupSF>();

        public ViewModel()
        {
            _sourceForSFList = new ObservableCollection<SetupSF>()
            {
                new SetupSF() { SFName = "One", OwnedSFEs = new ObservableCollection<SetupSFE> () { new SetupSFE() { SFEName = "Three" }, new SetupSFE() { SFEName = "Four" } } },
                new SetupSF() { SFName = "Two", OwnedSFEs = new ObservableCollection<SetupSFE> () { new SetupSFE() { SFEName = "Five" }, new SetupSFE() { SFEName = "Six" } } }
            };
            _sourceForSFEList = new ObservableCollection<SetupSFE>()
            {
                new SetupSFE() { SFEName = "One"},
                new SetupSFE() { SFEName = "Two"}
            };
        }

        private SetupSF _selectedSF;
        public SetupSF SelectedSF
        {
            get { return _selectedSF; }
            set
            {
                _selectedSF = value;
                if (_selectedSF != null)
                {
                    SourceForSFEList = _selectedSF.OwnedSFEs;
                }
            }
        }

        public ObservableCollection<SetupSF> SourceForSFList
        {
            get { return _sourceForSFList; }
            set
            {
                _sourceForSFList = value;
            }
        }

        private ObservableCollection<SetupSFE> _sourceForSFEList = new ObservableCollection<SetupSFE>();
        public ObservableCollection<SetupSFE> SourceForSFEList
        {
            get { return _sourceForSFEList; }
            set
            {
                _sourceForSFEList.Clear();
                if (value != null)
                {
                    foreach (var item in value)
                    {
                        _sourceForSFEList.Add(item);
                    }

                }
            }
        }

    }

    public class SetupSF
    {
        public String SFName { get; set; }
        public ObservableCollection<SetupSFE> OwnedSFEs;
    }

    public class SetupSFE
    {
        public String SFEName { get; set; }
    }

<Page
    x:Class="App7.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App7"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.DataContext>
        <local:ViewModel />
    </Page.DataContext>

    <StackPanel Orientation="Horizontal">
        <ComboBox x:Name="ComboBoxA" 
          Width="350" 
          ItemsSource="{Binding SourceForSFList}"
          SelectedItem="{Binding SelectedSF, Mode=TwoWay}"
          DisplayMemberPath="SFName" Margin="10"/>

        <ComboBox x:Name="ComoboxB"
          Width="350"
          ItemsSource="{Binding SourceForSFEList}"
          SelectedItem="{Binding SelectedSFE, Mode=TwoWay}"   
          DisplayMemberPath="SFEName" Margin="10"/>
    </StackPanel>
</Page>
Greg Thatcher
  • 1,303
  • 20
  • 29
  • This works. Thanks. I also discovered that I had to make a copy of the SourceForSFE collection. This is because I was using the objects in the collection elsewhere in this ViewModel. I didn't realize that clearing a collection destroyed the objects contained within it. This thread has a good explanation of how to clone a collection: https://stackoverflow.com/questions/26126660/how-do-i-clone-a-collectiont – Matt Aug 13 '18 at 14:01