Search code examples
c#wpfdata-binding

WPF Update VievModel bound ListBox from CodeBehind


I'm brand new to WPF/MVVM and getting two ListBox wiht two buttons, one to copy selected elements from left listbox to the right one, and back with the second button.
At startup the left ListBox is correctly populated with data from datastore, when I select some elements and press the btn_add_symbol_Click button the AddSelectedSymbol method is executed as expected, OnPropertyChanged also, and the getter of WidgetSymbolsSelected (I suppose from the right ListBox) is called, returning the right updated CurrentWidget.Symbols. The problem is that the right ListBox, lst_SelectedSymbols, is always empty. What's going on? Thanks.

XAML:

<ListBox Grid.Row="1" Grid.Column="0" ItemsSource="{Binding AvailableSymbols}" SelectionMode="Multiple" Name="lst_AvailableSymbols" />  
<ListBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding WidgetSymbolsSelected}" SelectionMode="Multiple" Name="lst_SelectedSymbols"/>

VM:

public IEnumerable<string> AvailableSymbols
{
    get { return DataManager.AvailableSymbols; }
}

public IEnumerable<string> WidgetSymbolsSelected
{
    get { return CurrentWidget.Symbols; }
}

public void AddSelectedSymbol(string symbol)
{
    if (!CurrentWidget.Symbols.Contains(symbol)) {
        CurrentWidget.Symbols.Add(symbol);
        OnPropertyChanged("WidgetSymbolsSelected");
    }
}

CodeBehind:

private void btn_add_symbol_Click(object sender, RoutedEventArgs e)
{
    var ViewModel = (WidgetEditorViewModel)this.DataContext;
    foreach (var Item in lst_AvailableSymbols.SelectedItems)
    {
        ViewModel.AddSelectedSymbol(Item.ToString());
    }
}

Solution

  • I notice you've got a comment on your post which solves this, but I thought I'd add a little bit of context in case it helps anyone understand why this fixes the problem.

    WPF uses the PropertyChanged event (of the INotifyPropertyChanged interface) to know when to re-fetch the property, by calling the get method for it. If the reference value from the get hasn't changed, WPF is not going to re-populate all of the items from the collection.

    While you've modified the underlying collection, the actual object reference to the collection itself hasn't changed, so WPF's PropertyChanged event won't re-enumerate the values of the collection (if you had, instead replaced the collection entirely with a new list object, that would be a different story).

    ObservableCollection<T> has done you the favor of implementing INotifyCollectionChanged which raises an event when the collection elements change, informing WPF that it needs to adjust the UI to account for new items in the collection. This also means you do not need to raise the property changed event yourself (for this use case), since it's not doing anything.