I want to bind Listbox selectedItems to array. But .NET throws exception at runtime.
d.SetBinding(ListBox.SelectedItemsProperty, new Binding { Source = SomeArray });
Where d is some ListBox from XAML.
Exception:
Selected Item cannot be bound.
Why?
Here a working solution, you could easily adapt to your needs:
In xaml:
<Window x:Class="ListBoxSelectedItems.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="MainWindow" Height="200" Width="200">
<StackPanel>
    <ListBox 
        ItemsSource="{Binding ProductListSource, NotifyOnSourceUpdated=True}" 
        SelectedItem="{Binding SelectedProduct, UpdateSourceTrigger=PropertyChanged}" 
        SelectionMode="Multiple" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Item}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
    <Label Content="{Binding Text}"/>
</StackPanel>
In code:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows;
namespace ListBoxSelectedItems
{
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}
public class ViewModel : MyNotifyPropertyChanged
{
    public ViewModel()
    {
        ProductListSource.Add(new Product() { Item = "Item_1", IsSelected = false });
        ProductListSource.Add(new Product() { Item = "Item_2", IsSelected = false });
        ProductListSource.Add(new Product() { Item = "Item_3", IsSelected = false });
    }
    private ObservableCollection<Product> _productList = new ObservableCollection<Product>();
    public ObservableCollection<Product> ProductListSource
    {
        get => _productList;
        set
        {
            _productList = value;
            RaisePropertyChanged(nameof(ProductListSource));
        }
    }
    private readonly Product _selectedProduct;
    public Product SelectedProduct
    {
        get => _selectedProduct;
        set
        {
            var selectedItems = ProductListSource.Where(x => x.IsSelected).ToList();
            this.RaisePropertyChanged(nameof(SelectedProduct));
            string s = "{";
            int n = selectedItems.Count;
            for (int i=0; i< n; i++)
            {
                s += selectedItems[i].ToString();
                if (i < n - 1) s += ", ";
            }
            s += "}";
            Debug.WriteLine(s + ".Count= " + n);
        }
    }
}
public class Product : MyNotifyPropertyChanged
{
    private string _item;
    public string Item
    {
        get => _item;
        set
        {
            _item = value;
            RaisePropertyChanged(nameof(Item));
        }
    }
    private bool _isSelected;
    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            _isSelected = value;
            RaisePropertyChanged(nameof(IsSelected));
        }
    }
    public new string ToString() => _item;
}
public class MyNotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Hope it helps!
You can subscribe to the SelectionChanged event of the ListBox, and in the handler sync a collection of selected items.
In this example the Windows DataContext was set to itself (this) in its constructor. You could also easily call into a logic layer (ViewModel if you're using MVVM) from the event handler.
In Xaml:
<StackPanel>
    <ListBox
        ItemsSource="{Binding ListBoxItems}"
        SelectionMode="Multiple"
        SelectionChanged="ListBox_SelectionChanged">
    </ListBox>
    <ItemsControl
        ItemsSource="{Binding SelectedItems}">
    </ItemsControl>
</StackPanel>
And in the code-behind:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    foreach (string item in e.RemovedItems)
    {
        SelectedItems.Remove(item);
    }
    foreach (string item in e.AddedItems)
    {
        SelectedItems.Add(item);
    }
}
                        This is the working solution, however when selection changes SelectedItemsProperty does not refresh bindings...
you can create a custom control as follow
public class MyListBox: ListBox{
    public MyListBox()
    { 
         this.SelectionChanged += (s,e)=>{ RefreshBindings(); };
    }
    private void RefreshBindings()
    {
         BindingExpression be = 
             (BindingExpression) GetBindingExpression(
                                      SelectedItemsProperty);
         if(be!=null){
               bd.UpdateTarget();
         }
    }
}
or in your app you can define event in every listbox as shown below ..
myListBox.SelectionChanged += (s,e) => {
    BindingExpression be = 
         (BindingExpression) myListBox.GetBindingExpression(
                                      ListBox.SelectedItemsProperty);
    if(be!=null){
        bd.UpdateTarget();
    }
};
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With