2

I have a list view with a grid view that has two columns. The first column contains checkboxes that are bound to the listviewitem selected property, the second column is text. In the header for the checkboxes column I have a check box that I want to function as a select / deselect all button. I have used datatriggers to do this but it only works when I remove the binding between the checkboxes and the selected property. Should I be able to use a data trigger to set a bound property?

Screenshot

codebehind

namespace ListviewWCheckboxes
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        List<string> listItems = new List<string>() { "foo", "bar", "blah" };
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            pdflistView.ItemsSource = listItems;
        }
    }
}

xaml

<Window x:Class="ListviewWCheckboxes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ListviewWCheckboxes"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        Loaded="Window_Loaded">
    <Grid>
        <ListView x:Name="pdflistView" HorizontalAlignment="Left" Height="300" Margin="5" VerticalAlignment="Top" Width="240"
                      SelectionMode="Extended"
                      >
            <ListView.View>
                <GridView>
                    <GridViewColumn>
                        <GridViewColumn.Header>
                            <CheckBox x:Name="ckbxSelectAll"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Path=IsSelected, 
                                                RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}, Mode=FindAncestor},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
                                    <CheckBox.Style>
                                        <Style TargetType="CheckBox">
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding ElementName=ckbxSelectAll, Path=IsChecked}" Value="True">
                                                    <Setter Property="IsChecked" Value="True" />
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding ElementName=ckbxSelectAll, Path=IsChecked}" Value="False">
                                                    <Setter Property="IsChecked" Value="False" />
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </CheckBox.Style>

                                </CheckBox>
                                <DataTemplate.Triggers>

                                </DataTemplate.Triggers>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>

                    </GridViewColumn>
                    <GridViewColumn Header="Pdf" DisplayMemberBinding="{Binding}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>
WIll Cruz
  • 73
  • 8

3 Answers3

4

Using AjS's suggestion I came up with this solution.

I was also able to come up with a solution that drilled down the visual tree of the listbox till I got the VirtualizingStackPanel that contains all of the ListviewItems and loop through them setting the IsSelected property but this seems cleaner.

xaml

 <Window x:Class="ListviewWCheckboxes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ListviewWCheckboxes"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListView x:Name="pdflistView" HorizontalAlignment="Left" Height="300" Margin="5" VerticalAlignment="Top" Width="240"
                      SelectionMode="Extended" ItemsSource="{Binding Path=listItems}">
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="IsSelected" Value="{Binding is_Selected}"/>
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.View>
                <GridView>
                    <GridViewColumn>
                        <GridViewColumn.Header>
                            <CheckBox x:Name="ckbxSelectAll" Checked="ckbxSelectAll_Checked" Unchecked="ckbxSelectAll_Checked"/>
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding is_Selected}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Pdf" DisplayMemberBinding="{Binding Path=text}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

code

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;

namespace ListviewWCheckboxes
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<someobjects> listItems { get; set; } = new ObservableCollection<someobjects>();

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            listItems.Add(new someobjects("foo"));
            listItems.Add(new someobjects("bar"));
            listItems.Add(new someobjects("blah"));
        }

        private void ckbxSelectAll_Checked(object sender, RoutedEventArgs e)
        {
            foreach (someobjects item in listItems)
            {
                item.is_Selected = (bool)((CheckBox)sender).IsChecked;
            }
        }

        public class someobjects : INotifyPropertyChanged
        {
            private string _text;

            public string text
            {
                get { return _text; }
                set { _text = value; OnPropertyChanged(); }
            }

            private bool _is_Selected;

            public bool is_Selected
            {
                get { return _is_Selected; }
                set { _is_Selected = value; OnPropertyChanged(); }
            }

            public someobjects(string t)
            {
                text = t;
                is_Selected = false;
            }

            public event PropertyChangedEventHandler PropertyChanged;

            protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                var handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
WIll Cruz
  • 73
  • 8
1

for anyone wondering about the 2nd solution I mentioned, this event will have to be attached to the checked and unchecked events of the selectall checkbox

private void ckbxSelectAll_Checked(object sender, RoutedEventArgs e)
    {
        DockPanel dp = FindVisualChild<DockPanel>(pdflistView, 0);
        ScrollContentPresenter scp = FindVisualChild<ScrollContentPresenter>(dp, 1);
        VirtualizingStackPanel vsp = FindVisualChild<VirtualizingStackPanel>(scp, 0);

        foreach (ListViewItem item in vsp.Children)
        {
            item.IsSelected = (bool)((CheckBox)sender).IsChecked;
        }
    }

    private static T FindVisualChild<T>(UIElement element, int childindex) where T : UIElement
    {
        UIElement child = element;
        while (child != null)
        {
            T correctlyTyped = child as T;
            if (correctlyTyped != null)
            {
                return correctlyTyped;
            }

            child = VisualTreeHelper.GetChild(child, childindex) as UIElement;
        }
        return null;
    }
WIll Cruz
  • 73
  • 8
0

I believe the best way to implement this is to create a property 'IsSelected' on your class and bound that to the checkbox (IsChecked) and listview (isSelected). Handle the checked event of the header checkbox and manually toggle the IsSelected property of the items in the collection so that checkbox and listviewitems are updated. Please let me know if you have any other questions

AjS
  • 341
  • 2
  • 13
  • This works as long as I dont use the checkboxes to select the row before using the selectall button. If I select item foo with the check box then unselect it and used the select all check box foo will not be selected but the other items will. each item breaks if they are manually selected. Any idea on how to fix this? – WIll Cruz Feb 15 '17 at 16:09
  • @WIllCruz yeah i see that it breaks the selection; sorry i replied too soon. I have updated the post. – AjS Feb 17 '17 at 09:17