I have two ListBox
es. 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
Item
type contains aList<int>
, which does not notify on collection changes, because it does not implement theINotifyCollectionChanged
interface. 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
ItemValue
andSubItems
properties 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 theSetProperty
method with backing fields to automatically raise thePropertyChanged
event 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 theCurrentItem
explicitly.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.
_items
field initializer is useless, as you reassign the field in the constructor anyway._items
, you can use a local variable in the constructor.Items
is not used, you can remove it.Items.CurrentItem
directly toItem
to get an invalid cast exception that is more specific that the null reference exception that you would get later when accessing the value from usingas
and gettingnull
. If you expect that the value could be something else than anItem
, you could check fornull
afteras
or use pattern matching to handle both cases, success and error (CurrentItem
is not of typeItem
) appropriately.