C# 带有分组项目的ListView-通过组标题选中所有组成员复选框

C# 带有分组项目的ListView-通过组标题选中所有组成员复选框,c#,wpf,listview,checkbox,selected,C#,Wpf,Listview,Checkbox,Selected,我使用的是C#和WPF,我有一个列表视图,其中包含第一列中带有复选框的项。ListView的ItemsSource是在代码中设置的(不是通过绑定),它包含具有属性“Name”、“Type”和“Selected”的类“Item”的实例 public class Item : INotifyPropertyChanged { private string _name; private bool _selected; private string _type; pub

我使用的是C#和WPF,我有一个列表视图,其中包含第一列中带有复选框的项。ListView的ItemsSource是在代码中设置的(不是通过绑定),它包含具有属性“Name”、“Type”和“Selected”的类“Item”的实例

public class Item : INotifyPropertyChanged
{
    private string _name;
    private bool _selected;
    private string _type;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            this.OnPropertyChanged();
        }
    }

    public bool Selected
    {
        get { return _selected; }
        set
        {
            _selected = value;
            this.OnPropertyChanged();
        }
    }

    public string Type
    {
        get { return _type; }
        set
        {
            _type = value;
            this.OnPropertyChanged();
        }
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string property = "")
    {
        if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
ListView的视图设置为GridView,第一列有一个绑定到选定属性的复选框-例如,复选框表示“已选定”

我正在将分组添加到此ListView(按“类型”分组),GroupStyle也包含一个复选框

        var lst = new List<Item>();
        lst.Add(new Item { Name = "A", Type = "1" });
        lst.Add(new Item { Name = "B", Type = "1" });
        lst.Add(new Item { Name = "C", Type = "1" });
        lst.Add(new Item { Name = "A", Type = "2" });
        lst.Add(new Item { Name = "B", Type = "2" });
        lst.Add(new Item { Name = "C", Type = "2" });

        listview.ItemsSource = lst;

        var view = CollectionViewSource.GetDefaultView(lst);
        view.GroupDescriptions.Add(new PropertyGroupDescription("Type"));
var lst=newlist();
添加(新项目{Name=“A”,Type=“1”});
添加(新项目{Name=“B”,Type=“1”});
添加(新项目{Name=“C”,Type=“1”});
添加(新项目{Name=“A”,Type=“2”});
添加(新项目{Name=“B”,Type=“2”});
添加(新项目{Name=“C”,Type=“2”});
listview.ItemsSource=lst;
var view=CollectionViewSource.GetDefaultView(lst);
view.groupdescription.Add(新属性groupdescription(“类型”);
ListView的XAML包含GridView和GroupStyle:

    <ListView x:Name="listview">

        <!-- View -->
        <ListView.View>
            <GridView>
                <GridViewColumn Width="50">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate DataType="cls:Item">
                            <CheckBox IsChecked="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn Width="300" Header="Name" DisplayMemberBinding="{Binding Name, UpdateSourceTrigger=PropertyChanged}"></GridViewColumn>

                <GridViewColumn Width="100" Header="Type" DisplayMemberBinding="{Binding Type}"></GridViewColumn>
            </GridView>
        </ListView.View>

        <!-- Group style -->
        <ListView.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <Expander IsExpanded="True">
                                        <Expander.Header>
                                            <StackPanel Orientation="Horizontal">
                                                <CheckBox></CheckBox>
                                                <TextBlock Text="{Binding Name}" />
                                                <TextBlock Text="{Binding ItemCount, StringFormat='- {0} item(s)'}" />
                                            </StackPanel>
                                        </Expander.Header>
                                        <ItemsPresenter />
                                    </Expander>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </ListView.GroupStyle>
    </ListView>

最后是我的问题:我希望能够使用组标题中的复选框选择该特定组中的所有项目或不选择任何项目。例如:

单击组标题复选框应选择该特定组中的所有项目(如果尚未选择)。再次单击它应取消选择(取消选中)该组中的所有项目。如果用户手动选择或取消选择组中的某些项目,则组标题复选框最好显示不确定状态,但不选中也很好

我不知道从哪里开始。我假设我需要绑定group header复选框的IsChecked属性,但我不知道绑定到什么,因为datacontext是一种GroupDescriptor,它不包含关于组的任何信息,也不知道该组中有哪些项(右??)

我没有严格遵循MVVM,所以我不担心在绑定和viewmodel中执行所有操作,我可以听复选框的Checked事件,并以某种方式在代码中确定应该检查哪些项。例如如果我能听到选中的事件,并以某种方式提取组的类型,我将大部分被设置(我可以浏览整个列表并选择所有匹配组的类型)。但我甚至没有看到一种方法可以做到这一点;我可以在Checked事件(发送方)中获得复选框,我可以循环到所有父控件,但我看不到提取有关我分组的属性信息的方法


任何帮助都会很好

您可以在选中组标题时进行处理,然后循环查看组中的项目,并将IsChecked设置为true。

我找到了答案,只需要复选框的数据上下文。这不是最漂亮的解决方案(当然不是MVVM),但它似乎工作正常

只需将选中和未选中的事件处理程序添加到组样式中的复选框中,即可将DataContext强制转换为包含项的CollectionViewGroup

在嵌套分组的情况下,Items集合包含CollectionViewGroup的另一个实例,因此当您找到另一个(嵌套)组时,需要递归地循环这些项:


现在,我仍然需要弄清楚如何响应检查项目和更改相应组中的复选框。

但如何获取组中的项目?我需要知道我正在分组的属性的值,或者以其他方式获取组项,但我找不到任何方法来获取它们。你有没有想出解决方案?
    private void OnGroupChecked(object sender, RoutedEventArgs e)
    {
        this.HandleGroupCheck((CheckBox)sender, true);
    }

    private void OnGroupUnchecked(object sender, RoutedEventArgs e)
    {
        this.HandleGroupCheck((CheckBox)sender, false);
    }

    private void HandleGroupCheck(CheckBox sender, bool check)
    {
        var group = (CollectionViewGroup) sender.DataContext;
        this.HandleGroupCheckRecursive(group, check);
    }

    private void HandleGroupCheckRecursive(CollectionViewGroup group, bool check)
    {
        foreach (var itemOrGroup in group.Items)
        {
            if (itemOrGroup is CollectionViewGroup)
            {
                // Found a nested group - recursively run this method again
                this.HandleGroupCheckRecursive(itemOrGroup as CollectionViewGroup, check);
            }
            else if (itemOrGroup is Item)
            {
                var item = (Item)itemOrGroup;
                item.Selected = check;
            }
        }
    }