Search code examples
wpfmvvmlistbox

WPF MVVM ListBox MultiSelect


I Have created a list box containing list of items and i need to bind them on selection changed(Select and deselect).

ABCD.xalm

<ListBox Grid.Column="2" Grid.ColumnSpan="9" Height="30" Margin="0 0 5 0" Foreground="{StaticResource AcresTheme}" SelectedItem="{Binding Path=UpdateSimulationItem,UpdateSourceTrigger=PropertyChanged}"                               
                            ItemsSource="{Binding SmulationTypes, NotifyOnSourceUpdated=True}" 
                            Background="{Binding }"
                            MinHeight="65" SelectionMode="Multiple">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <CheckBox  Foreground="{StaticResource AcresTheme}"
                                           Content="{Binding Item}" 
                                           IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"></CheckBox>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                        <ListBox.ItemContainerStyle>
                            <Style TargetType="{x:Type ListBoxItem}">
                                <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
                            </Style>
                        </ListBox.ItemContainerStyle>
                    </ListBox>

ABCD.cs (View Model)

public List<string> SimulationTypesList { get; set; } = new List<string>();
    private ObservableCollection<SimulationType> _simulationTypes = new ObservableCollection<SimulationType>();

    public ObservableCollection<Items> SimulationTypes
    {
        get
        {
            return _simulationTypes;
        }
        set
        {
            _simulationTypes = value;
            OnPropertyChanged("SimulationTypes");
        }
    }

    private Items _updateSimulationItem;
    public Items UpdateSimulationItem
    {
        get
        {
            return _updateSimulationItem;
        }
        set
        {
          //Logic for getting the selected item
            _updateSimulationItem = value;
            OnPropertyChanged("UpdateSimulationItem");
        }
    }
public ABCD()
{
        SimulationTypes.Add(new SimulationType() { Item = "Simulation 1", IsSelected = false });
        SimulationTypes.Add(new SimulationType() { Item = "Simulation 2", IsSelected = false });
        SimulationTypes.Add(new SimulationType() { Item = "Simulation 3", IsSelected = false });
}

Items.cs

public class Items: ViewModelBase
{
    private string item;
    public string Item
    {
        get { return item; }
        set
        {
            item = value;
            this.OnPropertyChanged("Item");
        }
    } 
    
    private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            isSelected = value;
            this.OnPropertyChanged("IsSelected");
        }
    }
}

I did try the solution given in https://stackoverflow.com/a/34632944/12020323 This worked fine for deleting a single item or selecting a single item.

When we select the second item it does not trigger the property change.


Solution

  • Thank you @EldHasp, for the time taken to respond to my question. Very greatfull to the solution provided.

    Currently I am intrested in having the complete code in ViewModel, I found the mistake that I had done in the ViewModel.

    Old Code:

    private ObservableCollection<SimulationType> _simulationTypes = new ObservableCollection<SimulationType>();
    
    public ObservableCollection<Items> SimulationTypes
    {
        get
        {
            return _simulationTypes;
        }
        set
        {
            _simulationTypes = value;
            OnPropertyChanged("SimulationTypes");
        }
    }
    
    private Items _updateSimulationItem;
    public Items UpdateSimulationItem
    {
        get
        {
            return _updateSimulationItem;
        }
        set
        {
            //Logic for getting the selected item
            _updateSimulationItem = value;
            OnPropertyChanged("UpdateSimulationItem");
        }
    }
    

    _updateSimulationItem = value; Binds the first selected item to UpdateSimulationItem and propertychange will trigger only when that perticular item is changed.

    For example:

    SimulationTypes.Add(new SimulationType() { Item = "Simulation 1", IsSelected = false }); SimulationTypes.Add(new SimulationType() { Item = "Simulation 2", IsSelected = false }); SimulationTypes.Add(new SimulationType() { Item = "Simulation 3", IsSelected = false });

    In this three items, if I select Simulation 1 then the UpdateSimulationItem will bind to Simulation 1 and the propertychange will narrow down to one item i.e. Simulation 1. Now if we click on Simulation 2 the peopertychange will not trigger as the UpdateSimulationItem is bound to only Simulation 1 item changes.

    The change that I Made.

    Updated code:

    private Items _updateSimulationItem;
    public Items UpdateSimulationItem
    {
        get
        {
            return _updateSimulationItem;
        }
        set
        {
          //Removed unnecessary code and the assignment of value to _updateSimulationItem
          OnPropertyChanged("UpdateSimulationItem");
        }
    }
    

    As we have binded the SimulationTypes to ItemSource in the ABC.XAML as shown below

    <ListBox Foreground="{StaticResource AcresTheme}" 
             SelectedItem="{Binding Path=UpdateSimulationItem,UpdateSourceTrigger=PropertyChanged}" 
             ItemsSource="{Binding SimulationTypes, NotifyOnSourceUpdated=True}" 
             MinHeight="65" SelectionMode="Multiple">
    

    when i click on the checkbox that is present in the view, it will automatically updat the SimulationTypes as i have bound the checkbox to IsSelected.

    <CheckBox  Foreground="{StaticResource AcresTheme}"
               Content="{Binding Item}"
               IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
    

    @EldHasp the code changes that we have to do in your code is to remove the assignment to _selectedItem in the setter property and just keep the OnPropertychange(nameOf(SelectedItem)).

    public Item? SelectedItem { get => _selectedItem; set => Set(ref _selectedItem, value); } The bold text was making the SelectedItem to bind to one item, which was restricting the trigger when other item was selected.

    Once Again Thank you @EldHasp for taking out your time on this.