WPF - MVVM:如何选中/取消选中 ListView 中的所有项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17974535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:19:50  来源:igfitidea点击:

WPF - MVVM : How to Check/Uncheck all Items in a ListView

c#wpflistviewmvvm

提问by a praveen

I have the following requirements:

我有以下要求:

  1. Window will show a ListViewwith multiple items.
  2. User should be able to check (Checkbox) any item. a) If one item, all items should be unchecked and disabled. b) If checked item is unchecked, than all items should be enabled.
  1. 窗口将显示一个ListView包含多个项目。
  2. 用户应该能够检查(复选框)任何项目。a) 如果是一项,则所有项都应取消选中和禁用。b) 如果选中的项目未选中,则应启用所有项目。

As of now, I have the following incomplete code.

截至目前,我有以下不完整的代码。

MainWindow XAML:

主窗口 XAML:

<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="520.149" Width="732.463">
    <Window.Resources>
        <ResourceDictionary Source="MainWindowResource.xaml" />
    </Window.Resources>

    <Grid>
     <ListView x:Name="myListBox" ItemTemplate="{StaticResource OfferingTemplate}">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="3" VerticalAlignment="Top"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
        </ListView>
    </Grid>
</Window>

DataTemplete for ListView:

ListView 的数据模板:

<DataTemplate x:Key="OfferingTemplate">
    <StackPanel>
        <Grid IsEnabled="{Binding IsEnabled}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="8"></ColumnDefinition>
                <ColumnDefinition Width="120"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="40"></RowDefinition>
                <RowDefinition Height="50"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
            </Grid.RowDefinitions>
            <Rectangle Grid.Column="0" Grid.RowSpan="3" Fill="#F4CA16" />
            <Label
                Grid.Column="1"
                Grid.Row="0"
                Content="{Binding Title}"
                FontSize="18" FontWeight="Bold"
                Margin="0,0,0,0" />
            <TextBlock
                Grid.Column="1"
                Grid.Row="1"
                FontSize="10"
                Text="{Binding Description}"
                Foreground="Black"
                TextWrapping="WrapWithOverflow"
                Margin="5,0,0,0" />
            <CheckBox
                Grid.Column="1"
                Grid.Row="2"
                FontSize="14"
                IsChecked="{Binding IsSelected}"
                VerticalAlignment="Bottom"
                Margin="5,0,0,0">

                <TextBlock Text="Select" Margin="0,-2,0,0"/>
            </CheckBox>
        </Grid>
    </StackPanel>
</DataTemplate>

Model:

模型:

class MyModel
{
    public string Title { get; set; }
    public string Description { get; set; }
    public bool IsSelected { get; set; }
    public bool IsEnabled { get; set; }
}

ViewModel:

视图模型:

class MyViewModel : INotifyPropertyChanged
{
    private MyModel offering;

    public MyViewModel()
    {
        offering = new MyModel();
    }

    public int ID { get; set; }
    public string Title
    {
        get { return offering.Title; }
        set
        {
            offering.Title = value;
            RaisePropertyChanged("Title");
        }
    }
    public string Description
    {
        get { return offering.Description; }
        set
        {
            offering.Description = value;
            RaisePropertyChanged("Description");
        }
    }
    public bool IsSelected
    {
        get { return offering.IsSelected; }
        set
        {
            offering.IsSelected = value;
            RaisePropertyChanged("IsSelected");
        }
    }
    public bool IsEnabled
    {
        get { return offering.IsEnabled; }
        set
        {
            offering.IsEnabled = value;
            RaisePropertyChanged("IsEnabled");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

回答by Bill Zhang

This is an interesting question. Since the action you want applies to all items in the list, this logic should in list class level. Your MyViewModel class is fine. You need add some logic in your list class and XAML but thanks to Prism, it is quite easy.

这是个有趣的问题。由于您想要的操作适用于列表中的所有项目,因此此逻辑应在列表类级别。您的 MyViewModel 类很好。您需要在列表类和 XAML 中添加一些逻辑,但多亏了 Prism,这很容易。

The list class (not shown in your post) Contains:

列表类(未在您的帖子中显示)包含:

    public ObservableCollection<MyViewModel> MyItems { get; set; } //Binding to ItemsSource

    private ICommand _selectCommand;

    public ICommand SelectCommand
    {
        get { return _selectCommand ?? (_selectCommand = new DelegateCommand<MyViewModel>(DoSelect)); }
    }

    private void DoSelect(MyViewModel myViewModel)
    {
        foreach(var item in MyItems)
            if (item != myViewModel)
            {
                item.IsSelected = false;
                item.IsEnabled = false;
            }
    }

    private ICommand _unselectCommand;

    public ICommand UnselectCommand
    {
        get { return _unselectCommand ?? (_unselectCommand = new DelegateCommand<MyViewModel>(DoUnselect)); }
    }

    private void DoUnselect(MyViewModel myViewModel)
    {
        foreach (var item in MyItems)
            if (item != myViewModel)
            {
                item.IsEnabled = true;
            }
    }

There are two commands, one for selecting and the other for unselecting. The magic is on XAML:

有两个命令,一个用于选择,另一个用于取消选择。神奇之处在于 XAML:

      <ListView ItemsSource="{Binding Path=MyItems}" x:Name="listView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding Path=IsSelected}" IsEnabled="{Binding Path=IsEnabled}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="Checked">
                                <i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.SelectCommand}"
                                                       CommandParameter="{Binding}"/>
                            </i:EventTrigger>
                            <i:EventTrigger EventName="Unchecked">
                                <i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.UnselectCommand}"
                                                       CommandParameter="{Binding}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </CheckBox>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

Using Prism's triggers, you can map CheckBox's Checked and Unchecked event to your list view model's commands and passing the item view model as parameter.

使用 Prism 的触发器,您可以将 CheckBox 的 Checked 和 Unchecked 事件映射到列表视图模型的命令,并将项目视图模型作为参数传递。

It is working perfectly but one thing is annoying, that setting item's IsSelected is separate. When you check a CheckBox, the item behind is set to true through DataBinding but all others are set through parent view model. If your post is all your requirement, you can remove IsChecked binding and put the logic of setting one IsSelected inside list view model, which looks clenaer and easier to write test code.

它运行良好,但有一点很烦人,设置项的 IsSelected 是分开的。当您选中 CheckBox 时,后面的项目通过 DataBinding 设置为 true,但所有其他项目都通过父视图模型设置。如果你的帖子是你的全部需求,你可以去掉 IsChecked 绑定并将设置一个 IsSelected 的逻辑放在列表视图模型中,这样看起来更简洁,更容易编写测试代码。