I am testing Kelly Elias' article on Creating a WPF checkListBox. I'm needing to get the selectedIndex and the checkedbox text. Everything works as needed until I change the listbox's SelectionMode to "Multiple" which I need implemented. After that, the SelectedIndex nor the SelectedItem does not change using the SelectionChanged event. These two properties only show info of the first checkedbox. However, all of the checkedboxes are added to the SelectedItems collection. Can someone please assist with this issue?
Thank you in advance!!!
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace Jarloo
{
public class Customer : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
public CheckedListItem()
{
}
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<CheckedListItem<Customer>> Customers { get; set; }
public MainWindow()
{
InitializeComponent();
Customers = new ObservableCollection<CheckedListItem<Customer>>();
Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "Kelly Smith" }));
Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "Joe Brown" }));
Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "Herb Dean" }));
Customers.Add(new CheckedListItem<Customer>(new Customer() { Name = "John Paul" }));
DataContext = this;
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int index = listbox1.SelectedIndex;
string testName = ((CheckedListItem<Customer>)listbox1.SelectedValue).Item.Name;
}
}
}
<Window x:Class="Jarloo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen">
<Grid>
<ListBox Name="listbox1" ItemsSource="{Binding Customers}" SelectionChanged="ListBox_SelectionChanged"
SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
Content="{Binding Path=Item.Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
The way the control works when
SelectionMode
isMultiple
(orExtended
), is to set theSelectedIndex
only when the first item is selected (I.E. added toSelectedItems
). Additional items get added to the end of theSelectedItems
list, butSelectedIndex
remains unchanged.To trick this behavior into the desired results, we will take the newly added item(s) off the end of
SelectedItems
and essentially insert it(them) back into the front. But for this to work, we have to do more than just make the newest item the first item inSelectedItems
. We have to empty the list and re-add each entry so that the default behavior now recognizes the desired item as theSelectedValue
and will update theSelectedIndex
. So we'll use a temporary list to help.Additionally, we'll add a flag to indicate if we are currently busy correcting the
SelectedItems
order. This is needed since we will be modifying the list andListBox.SelectionChanged
will be recursively called.Sidenote: Instead of the
busy
flag and condition check, you could instead do the following:It will achieve the same purpose of preventing a recursive call and subsequent stack overflow. I just don't know whether unsubscribing and subscribing the event handler is less or more expensive than the boolean check (my guess is more but researching, there seems to be no preference, as shown in these three results).