How to set CheckBox.IsChecked based on the existence of a linked object in a list

There are 2 lists - AvailableItems and SelectedItems.

Available items are displayed in the ListBox, and each ListBoxItem contains a CheckBox. The CheckBox is supposed to be checked if the related item is in SelectedItems.

Can I achieve this without checking Checked and Unchecked in the code and without adding the IsSelected property to the item class?

Here is the XAML:

<ListBox Name="ListBox1" ItemsSource="{Binding Path=AvailableItems}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel></StackPanel> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <DockPanel> <CheckBox Name="cb1"></CheckBox> <TextBlock Text="{Binding}"></TextBlock> </DockPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ListBox> 

and code:

  public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); _availableItems.Add(Colors.Red); _availableItems.Add(Colors.Green); _availableItems.Add(Colors.Blue); _selectedItems.Add(Colors.Green); this.DataContext = this; } ObservableCollection<Color> _selectedItems = new ObservableCollection<Color>(); public ObservableCollection<Color> SelectedItems { get { return _selectedItems; } set { _selectedItems = value; } } ObservableCollection<Color> _availableItems = new ObservableCollection<Color>(); public ObservableCollection<Color> AvailableItems { get { return _availableItems; } set { _availableItems = value; } } } 

The above xaml / code can be copied directly to a new WPF project for testing.

+4
source share
2 answers

You define a converter of several values ​​that takes an element and a collection and returns a boolean:

 public class CollectionContainsConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var item = values[0]; var collection = values[1] as IList; return collection.Contains(item); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } 

and then you use it like this:

  <ListBox Name="ListBox1" ItemsSource="{Binding Path=AvailableItems}"> <ListBox.Resources> <local:CollectionContainsConverter x:Key="contains"/> </ListBox.Resources> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel></StackPanel> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <DockPanel> <CheckBox Name="cb1"> <CheckBox.IsChecked> <MultiBinding Converter="{StaticResource contains}" Mode="OneWay"> <Binding Mode="OneWay"/> <Binding ElementName="ListBox1" Path="DataContext.SelectedItems" Mode="OneWay"/> </MultiBinding> </CheckBox.IsChecked> </CheckBox> <TextBlock Text="{Binding}"></TextBlock> </DockPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ListBox> 

Inverse transformation processing remains as an exercise.

+2
source

Rough layout using MVVM

create a class to represent all the data needed for each item, for example, Checked and Description

 class CustomItem : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private bool _isSelected; private string _customColour; public bool IsSelected { get { return _isSelected; } set { _isSelected = value; if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsSelected")); } } } public string CustomColour { get { return _customColour; } set { _customColour = value; if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs("CustomColour")); } } } } 

create a class to represent the collection of elements you want to display

  class CustomItems : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection<CustomItem> _listOfCustomItems; public CustomItems() { ListOfCustomItems = new ObservableCollection<CustomItem>(); var c = new CustomItem { IsSelected = false, CustomColour = "Red" }; ListOfCustomItems.Add(c); c = new CustomItem { IsSelected = true, CustomColour = "Green" }; ListOfCustomItems.Add(c); c = new CustomItem { IsSelected = false, CustomColour = "Blue" }; ListOfCustomItems.Add(c); } public ObservableCollection<CustomItem> ListOfCustomItems { get { return _listOfCustomItems; } set { _listOfCustomItems = value; if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs("ListOfCustomItems")); } } } public List<CustomItem> SelectedItems { get { return (from customItem in ListOfCustomItems where customItem.IsSelected select customItem).ToList(); } } } 

Add to window

  <Window.Resources> <WpfApplication1:CustomItems x:Key="CI"/> </Window.Resources> <Grid DataContext="{StaticResource CI}"> <ListBox ItemsSource="{Binding Path=ListOfCustomItems}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel></StackPanel> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <DockPanel> <CheckBox IsChecked="{Binding Path=IsSelected}"/> <TextBlock Text="{Binding Path=CustomColour}"/> </DockPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ListBox> </Grid> 

You save one list of items and provide a property for retrieving selects that select a simple LINQ. Easy. Change data types as needed.

-1
source

Source: https://habr.com/ru/post/1338207/


All Articles