I have two ListBoxes. One ListBox displays some items. Each item has a list of subitems. The second ListBox displays the subitems of the current item in the first ListBox. A typical item/subitem scenario. When I add a value to the subitem list, I cannot get the second ListBox to update. How can I force the second ListBox to update my subitems?
My simplified XAML and view model are below. Note that my view model inherits from Prism's BindableBase. In my view model I have a method to add a value to the subitems list and update the Items property with RaisePropertyChanged.
<ListBox Name="Items" ItemsSource="{Binding Items}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding ItemValue, Mode=TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Name="SubItems" ItemsSource="{Binding Items.CurrentItem.SubItems}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
class Item
{
public int ItemValue { get; set; }
public List<int> SubItems { get; set; }
}
class MyViewModel : BindableBase
{
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public MyViewModel()
{
List<Item> myItems = new List<Item>() { /* a bunch of items */ };
_items = new ObservableCollection<Item>(myItems);
Items = new ListCollectionView(_items);
}
public ICollectionView Items { get; private set; }
private void AddNewSubItem(object obj)
{
Item currentItem = Items.CurrentItem as Item;
currentItem.SubItems.Add(123);
RaisePropertyChanged("Items");
}
}
The issue is that your
Itemtype contains aList<int>, which does not notify on collection changes, because it does not implement theINotifyCollectionChangedinterface. Consequently, bindings do not get notified to update their values in the user interface when the collection is modified. Using a collection that implements this interface, e.g. anObservableCollection<int>will solve the issue.By the way, the same applies to
ItemValueandSubItemsproperties as well withINotifyPropertyChanged. Both properties have setters, which means they can be reassigned, but the bindings will not get notified then either. Since you useBindableBase, you can use theSetPropertymethod with backing fields to automatically raise thePropertyChangedevent when a property is set.A remark about your project. What you try to do is a master-detail view for hierarchical data. Since you already expose an
ICollectionView, you do not have to use theCurrentItemexplicitly.There is a special binding syntax, where you can use a
/to indicate the current item, e.g.:Additionally a few remarks about your view model code.
_itemsfield initializer is useless, as you reassign the field in the constructor anyway._items, you can use a local variable in the constructor.Itemsis not used, you can remove it.Items.CurrentItemdirectly toItemto get an invalid cast exception that is more specific that the null reference exception that you would get later when accessing the value from usingasand gettingnull. If you expect that the value could be something else than anItem, you could check fornullafterasor use pattern matching to handle both cases, success and error (CurrentItemis not of typeItem) appropriately.