1
votes

I have a Listbox set to multiple selection mode. When checked, the "IsChecked" attribute in each item of the "StuffList" changes. However, I cannot get the list of items that are selected in the Listbox using this method. There is no "SelectedItems" to bind to in the Listbox, only "SelectedItem".

How can I get the selected items using the code I have below?

<ListBox ItemsSource="{Binding BindStuffList}" SelectedItem="{Binding SelectedStuff, Mode=TwoWay}" SelectionMode="Multiple">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding ID}" Margin="2"/>
    </DataTemplate>
  </ListBox.ItemTemplate>

  <ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
      <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsChecked}"/>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>
2

2 Answers

1
votes

Okay so I have created a simple MVVM pattern based solution.

My ViewModel

public class MainWindowViewModel : INotifyPropertyChanged
{
    public IList SelectedList
    {
        get { return _selectedList; }
        set
        {
            _selectedList = value;
        }
    }
    private IList _selectedList;
    private List<StringValues> _bindStuffList;
    private object _lock = new object();

    public IEnumerable<StringValues> BindStuffList
    {
        get { return _bindStuffList; }
    }

    public MainWindowViewModel()
    {
        _bindStuffList = new List<StringValues>();
        BindingOperations.EnableCollectionSynchronization(_bindStuffList, _lock);
        BuildStuff();
    }

    private async void BuildStuff()
    {
        await Task.Run(() =>
        {
            for (int i = 0; i < 15; i++)
            {
                _bindStuffList.Add(new StringValues { ID = "Item " + i });
            }
        });

        RaisePropertyChanged("BindStuffList");
    }


    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        var pc = PropertyChanged;
        if (pc != null)
            pc(this, new PropertyChangedEventArgs(propertyName));
    }
}

The StringValues is a simple class

public class StringValues
{
    public string ID { get; set; }
    public bool IsChecked { get; set; }
}

Instead of using the classic ListBox I have used a custom one

public class CustomListBox : ListBox
{
    public CustomListBox()
    {
        this.SelectionChanged += CustomListBox_SelectionChanged;
    }

    void CustomListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        this.SelectedItemsList = this.SelectedItems;
    }

    public IList SelectedItemsList
    {
        get { return (IList)GetValue(SelectedItemsListProperty); }
        set { SetValue(SelectedItemsListProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsListProperty =
            DependencyProperty.Register("SelectedItemsList", typeof(IList), typeof(CustomListBox));
}

I Simply used this one in the View

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom ="clr-namespace:StackOverflow"
        Title="MainWindow" Height="350" Width="525">
    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <StackPanel>
            <custom:CustomListBox ItemsSource="{Binding BindStuffList}"
                                  SelectedItem="{Binding SelectedStuff, Mode=TwoWay}"
                                  SelectionMode="Multiple" SelectedItemsList="{Binding SelectedList,Mode=TwoWay}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding ID}" Margin="2"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
                <ListBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ListBoxItem}">
                        <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsChecked}"/>
                    </Style>
                </ListBox.ItemContainerStyle>
            </custom:CustomListBox>
        </StackPanel>
    </ScrollViewer>
</Window>

The SelectedList will always hold all the values selected from the UI. If you want you can get rid of the CheckBox in the DataTemplate as it is reduntant.

0
votes

To get selected items in your current list you would have to change your ItemsSource BindStuffList to yourListBox.SelectedItems;

This way your ItemsSource for the list would get updated containing only selected Items;

I did an example.

Created Two ListBox

Selected mulitple items from FirstList and the SelectedItems where shown in Second List

<ListBox     x:Name="firstList"
             ItemsSource="{Binding MyCollection}"
             SelectionChanged="ListBox_SelectionChanged"
             SelectionMode="Multiple">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding checked, Mode=TwoWay}"></CheckBox>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <ListBox x:Name="secondList" ItemsSource={Binding SecondListCollection}/>



private ObservableCollection<abc> myCollection;

    public ObservableCollection<abc> MyCollection
    {
        get { return myCollection; }
        set { myCollection = value; }
    }

    private ObservableCollection<abc> secondListCollection;

    public ObservableCollection<abc> SecondListCollection
    {
        get { return secondListCollection; }
        set { secondListCollection = value; }
    }


    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        SecondListCollection = (ObservableCollection<abc>)firstList.SelectedItems;
    }