I have two classes A and B which both implement an interface IThingWithList.
public interface IThingWithList
{
ObservableCollection<int> TheList;
}
TheList in A contains 1, 2, 3 TheList in B contains 4, 5, 6
I have a controller class which has a list of IThingWithList which contains A and B
public class MyControllerClass
{
public ObservableCollection<IThingWithList> Things { get; } = new ObservableCollection<IThingWithList>() { A, B };
public IThingWithList SelectedThing { get; set; }
}
Now, in xaml I have two ComboBoxes as follows
<ComboBox
ItemsSource="{Binding MyController.Things}"
SelectedValue="{Binding MyController.SelectedThing, Mode=TwoWay}" />
<ComboBox
DataContext="{Binding MyController.SelectedThing}"
ItemsSource="{Binding TheList}" />
The first ComboBox controls which (A or B) is the data context of the second combo box.
Problem:
When I select A or B from the first ComboBox The list items of the second ComboBox are not updated.
What I have tried:
Making both A and B ObservableObjects
Making IThingWithList implement INotifyPropertyChanged
Adding UpdateSourceTrigger to the ItemsSource Bindings
Scouring Google.
Here is how I typically do the ViewModel (in your case "Controller") Base Class in order to get the functionality you are looking for:
This is the base class that all VMs derive from.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetAndNotify<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(property, value))return;
property = value;
this.OnPropertyChanged(propertyName);
}
}
Here is how I would adjust your ControllerClass:
public class MyControllerClass : ViewModelBase
{
private ObservableCollection<IThingWithList> _things;
public ObservableCollection<IThingWithList> Things
{
get => _things;
set { SetAndNotify(ref _things, value); }
}
private IThingWithList _selectedthing;
public IThingWithList SelectedThing
{
get => _selectedThing;
set{SetAndNotify(ref _selectedThing, value);}
}
}
Now adjust your XAML to have the DataContext of the container set instead of each Control (makes life easier)
<UserControl xmlns:local="clr-namespace:YourMainNamespace">
<UserControl.DataContext>
<local:MyControllerClass/>
</UserControl.DataContext>
<StackPanel>
<ComboBox
ItemsSource="{Binding Things}"
SelectedValue="{Binding SelectedThing, Mode=TwoWay}" />
<!-- ComboBox ItemsSource="See next lines" /-->
</StackPanel>
</Window>
You can change SelectedThing
to be an ObservableCollection or you can have a second object that is the list and updates accordingly:
//Add into MyControllerClass
public MyInnerThingList => SelectedThing.TheList;
//Edit the SelectedThing to look like:
private IThingWithList _selectedthing;
public IThingWithList SelectedThing
{
get => _selectedThing;
set
{
SetAndNotify(ref _selectedThing, value);
RaisePropertyChanged(nameof(MyInnerThingList));
}
}
Then change the binding to:
<ComboBox ItemsSource="{Binding MyInnerThingList, Mode=OneWay}" />
You may also want to add a SelectedMyInnerThing property, but not sure if that is needed.