0

I am trying to select/unselect all the listview items when checkbox in listview header is clicked.

View (xaml):

            <ListView Margin="10" Name="MyLv" ItemsSource="Binding Path=lstData}" SelectionMode="Extended">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.View>
                    <GridView>
                        <!-- Checkbox column -->
                        <GridViewColumn>
                            <GridViewColumn.Header>
                                <CheckBox x:Name="CheckAll" Command="{Binding CheckAllCommand}" 
                                          CommandParameter="{Binding IsChecked, ElementName=CheckAll}" />
                            </GridViewColumn.Header>
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <CheckBox IsChecked="{Binding IsSelected}" />
                                    </StackPanel>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="ID" Width="120" DisplayMemberBinding="{Binding ID}" />
                        <GridViewColumn Header="Desc" Width="50" DisplayMemberBinding="{Binding Desc}" />
                    </GridView>
                </ListView.View>
            </ListView>

Code-Behind Constructor (xaml.cs):

    public MyView()           
    {
        DataContext = new MyViewModel();
        InitializeComponent();
    }

ViewModel:

    public  ObservableCollection<DataModel> lstData = null;
    public MyViewModel()
    {          
        this.lstData = this.LoadData();  // this connects to a database an extract info to be loaded in listview
    }

    private RelayCommand checkAllCommand;
    public ICommand CheckAllCommand
    {
        get
        {
            return checkAllCommand ??
                (checkAllCommand = new RelayCommand(param => this.SelectUnselectAll(Convert.ToBoolean(param.ToString()))));
        }
    }

    private void SelectUnselectAll(bool isSelected)
    {
        for (int i = 0; i < this.lstData.Count; i++)
        {
            this.lstData[i].IsSelected = isSelected;
        }
    }

Data Model:

public class DataModel
{
    public bool IsSelected { get; set; }
    public string ID { get; set; }
    public string Desc { get; set; }     
}

RelayCommand Class:

public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Creates a new command that can always execute.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members
}

My problem is the following: When I check/uncheck the checkbox in listview header, IsSelected column in listview for each listviewitem is not updated. I want the following behaviour:

  • If I check the checkbox in the listview header, all listview items will be checked.
  • If I uncheck the checkbox in the listview header, all listview items will be unchecked.
Willy
  • 9,848
  • 22
  • 141
  • 284
  • Your DataModel must fire a PropertyChanged event for the IsSelected property, i.e. implement INotifyPropertyChanged. – Clemens Jun 20 '17 at 15:23
  • @Clemens Yes, I have implemented it, I have specified here. And when you say I must fire PropertyChanged event, where? in method SelectUnselectAll for each iteration, or once at the end of the method? – Willy Jun 20 '17 at 15:27
  • You only have `public bool IsSelected { get; set; }`. That doesn't fire the PropertyChanged event. – Clemens Jun 20 '17 at 15:27
  • @Clemens I have done this within viewmodel. – Willy Jun 20 '17 at 15:28
  • `class DataModel` must *also* implement INotifyPropertyChanged – Clemens Jun 20 '17 at 15:28
  • @Clemens also using ObservableCollection I thought that when changes are made to the collection (lstData), the list in the UI will be automatically updated so is necessary INotifyPropertyChanged? – Willy Jun 20 '17 at 15:29
  • 1
    You're not making a change to the collection when you change a property of an object in the collection. – Clemens Jun 20 '17 at 15:32

1 Answers1

0

Your class DataModel must implement INotifyPropertyChanged, and fire the PropertyChanged event when the IsSelected property changes. Otherwise, the Binding of the ListViewItem's IsSelected property isn't notified.

public class DataModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            isSelected = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(IsSelected)));
        }
    }

    public string ID { get; set; }
    public string Desc { get; set; }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Ok, it works but is there any way to only do INotifyPropertyChanged in viewModel and not in DataModel? I would like to not implement INotifyPropertyChanged in DataModel. – Willy Jun 20 '17 at 15:48
  • No, you have to do it this way. You may however derive all your view model classes from a common base class that implements the interface. – Clemens Jun 20 '17 at 15:55
  • The problem now is that I have properties repeated in viewmodel and datamodel, I mean, some days ago I had another problem see here: https://stackoverflow.com/questions/44571986/wpf-bind-property-to-textblock-not-working and he told me to not implement INotifyPropertyChanged in DataModel, only in ViewModel. So now I have repeated properties in two places, how to avoid this? – Willy Jun 20 '17 at 16:00
  • No idea what these duplicate properties are, and why someone told you not to implement INotifyPropertyChanged in your DataClass (and why you accepted that answer). From what you've shown in this question, there is no way around, and it doesn't hurt. DataClass must notify about a changed IsSelected property. – Clemens Jun 20 '17 at 16:04
  • I accepted the answer because some days ago I did not implement this functionality to iterate over all the listviewitems and check/uncheck them. Today I was implementing it, and a new problem appeared. – Willy Jun 20 '17 at 16:07
  • For all your support I have accepted this answer as an approach but this is not what I am looking for, I would like to implement it in view model not int data model. – Willy Jun 21 '17 at 13:59
  • "I would like to implement it in view model not in data model". As already explained several times, this is not possible. Every view model class with properties that are bound to view properties and that can change from the view model side must implement a change notification mechanism. – Clemens Jun 21 '17 at 14:03