Set ListBox SelectedItems Based on Another Collection

I have a ListBox associated with a MyCollection type IEnumerable<string> .

  <ListBox x:Name="ListBox" ItemsSource="{Binding MyCollection, Mode=OneWay}" SelectionMode="Multiple"/> 

I have another List<string> SubCollection that contains a subset of MyCollection

Whenever a SubCollection changes, I would like to highlight SelectedItems according to the SubCollection

Is there a Binding , Behaviour or any other way to accomplish this?

Edit:

Say I have a ListBox associated with MyCollection { "Orange", "Mango", "Stawberry", "Pineapple" }

Suppose I click a button to load data from a database, and the result is "Orange", "Mango", which is then placed in the SubCollection . ListBox should now have Orange, Mango as SelectedItems .

+4
source share
6 answers

You can create a List AttachedProperty for a ListBox that adds items to the ListBox.SelectedItems when the list changes.

AttachedProperty solution keeps WPF clean and your MVVM template, also makes this function multiple for all your projects :)

Here is an example:

AttachedProperty:

 public static class ListBoxExtensions { // Using a DependencyProperty as the backing store for SearchValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty SelectedItemListProperty = DependencyProperty.RegisterAttached("SelectedItemList", typeof(IList), typeof(ListBoxExtensions), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemListChanged))); public static IList GetSelectedItemList(DependencyObject obj) { return (IList)obj.GetValue(SelectedItemListProperty); } public static void SetSelectedItemList(DependencyObject obj, IList value) { obj.SetValue(SelectedItemListProperty, value); } private static void OnSelectedItemListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var listbox = d as ListBox; if (listbox != null) { listbox.SelectedItems.Clear(); var selectedItems = e.NewValue as IList; if (selectedItems != null) { foreach (var item in selectedItems) { listbox.SelectedItems.Add(item); } } } } } 

Using Xaml:

 <ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple" local:ListBoxExtensions.SelectedItemList="{Binding SelectedItems}" /> 


Demo:

Working example if you want to test:

Xaml:

 <Window x:Class="WpfApplication17.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication17" Title="MainWindow" Height="227" Width="170" Name="UI"> <Grid DataContext="{Binding ElementName=UI}"> <ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple" local:ListBoxExtensions.SelectedItemList="{Binding SelectedItems}" Margin="0,0,0,37" > <ListBox.Resources> <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{x:Static SystemColors.HighlightColor}" /> <Style TargetType="ListBoxItem"> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/> </Trigger> </Style.Triggers> </Style> </ListBox.Resources> </ListBox> <Button Content="Populate SelectedItemList" Click="Button_Click" Height="32" Margin="2,0,1,2" VerticalAlignment="Bottom"/> </Grid> </Window> 

code:

 namespace WpfApplication17 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window, INotifyPropertyChanged { private List<string> _selectedItems = new List<string>(); private ObservableCollection<string> _items = new ObservableCollection<string> { "Orange", "Mango", "Stawberry", "Pineapple", "Apple", "Grape", "Banana" }; public MainWindow() { InitializeComponent(); } public ObservableCollection<string> Items { get { return _items; } set { _items = value; } } public List<string> SelectedItems { get { return _selectedItems; } set { _selectedItems = value; OnPropertyChanged("SelectedItems"); } } private void Button_Click(object sender, RoutedEventArgs e) { SelectedItems = new List<string> { "Orange", "Pineapple", "Apple" }; } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string e) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(e)); } } public static class ListBoxExtensions { // Using a DependencyProperty as the backing store for SearchValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty SelectedItemListProperty = DependencyProperty.RegisterAttached("SelectedItemList", typeof(IList), typeof(ListBoxExtensions), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemListChanged))); public static IList GetSelectedItemList(DependencyObject obj) { return (IList)obj.GetValue(SelectedItemListProperty); } public static void SetSelectedItemList(DependencyObject obj, IList value) { obj.SetValue(SelectedItemListProperty, value); } private static void OnSelectedItemListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var listbox = d as ListBox; if (listbox != null) { listbox.SelectedItems.Clear(); var selectedItems = e.NewValue as IList; if (selectedItems != null) { foreach (var item in selectedItems) { listbox.SelectedItems.Add(item); } } } } } } 

Result:

enter image description here

+4
source

I have a suggestion for you.

You can bind the " IsSelected " property to a ListBoxItem .

To do this, you need to use collection of objects (let say MyListBoxItem ) instead of a set of strings.

 public class MyListBoxItem { public string Description { get; set; } public bool IsSelected { get; set; } } 

Use the IsSelected property of the IsSelected class to bind the << 20> ListBoxItem to the ListBoxItem .

Example: in a view model,

 this.MyCollection = new ObservableCollection<MyListBoxItem>(); MyListBoxItem item1 = new MyListBoxItem() item1.Description = "Mango"; item1.IsSelected = true; MyCollection .add(item1); MyListBoxItem item2 = new MyListBoxItem() item2 .Description = "Orange"; item2 .IsSelected = false; MyCollection .add(item2 ); 

XAML (inside ListBox )

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

Standard data binding does not work because the SelectedItems property is read-only.

One simple approach is to manually iterate over MyCollection and set the IsSelected property for each element based on the elements in the SubCollection. To do this, the elements in the MyCollection list must contain objects inherited from ListBoxItem that display the IsSelected property.

Here is an example WPF application to demonstrate it:

Clicking the button will update the selected items based on the items in the SubCollection list. So far, I have hardcode values ​​for the SubCollection list.

According to your implementation, you can update your SubCollection and intercept the code inside the click button for any other events accordingly (for example, make the SubCollection list as an ObservableCollection and connect to the ObservableCollection.CollectionChange)

MainWindow.xaml

 <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="250" Width="255"> <Grid> <StackPanel Orientation="Vertical"> <ListBox x:Name="ListBox" ItemsSource="{Binding MyCollection}" SelectionMode="Extended" Margin="10,10"/> <Button Content="UpdateSelection" Click="Button_Click" Margin="10,10"/> </StackPanel> </Grid> </Window> 

MainWindow.xaml.cs

 namespace WpfApplication1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); _myCollection = new ObservableCollection<MyListBoxItem>() { new MyListBoxItem("Orange"), new MyListBoxItem("Mango"), new MyListBoxItem("Stawberry"), new MyListBoxItem("Pineapple") }; //Items to be selected on this.MyCollection ListBox this.SubCollection = new List<string>() { "Pineapple", "Mango" }; this.DataContext = this; } private ObservableCollection<MyListBoxItem> _myCollection; public ObservableCollection<MyListBoxItem> MyCollection { get { return _myCollection; } set { this._myCollection = value; } } public IList<string> SubCollection { get; set; } private void Button_Click(object sender, RoutedEventArgs e) { // Clear all the selected items in the ListBox this.MyCollection.ToList().ForEach(item => item.IsSelected = false); // SELECT only the items in MySubCollection into this.MyCollection ListBox this.MyCollection.Where(item => this.SubCollection.Contains(item.Content.ToString())).ToList().ForEach(item => item.IsSelected= true); } } public class MyListBoxItem : ListBoxItem { public MyListBoxItem(string displayName) { this.Content = displayName; } } } 
+2
source

Check out this blogpost: how-to-databind-to-selecteditems . There is a demo project with an attached code.

+1
source

Use the " SelectedValue " property in the ListBox. (Because you use string collection to bind the list).

  • Define a row type property in your view model (say MySelectedValue ).
  • Then bind it to the " SelectedValue " property of both lists. (Remember to set Mode = TwoWay to your main list)
  • Assume that the method will execute when you select an item in the subnet list box. (You must raise the SelectionChanged event in the SubBox ListBox)
  • What is it.

So. You need to do the following:

Use the same property to bind the "SelectedValue" property to the BOTH Box List.

Say if you want to associate a collection of objects with a list box. Then, use the SelectedItem property to accomplish this task.

+1
source

if you know the index number you want to select, you can use

 int[] index = {1, 2, 5}; for( int i in index) { listBox.SetSelected( i, true ); } 
0
source

All Articles