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

a p*_*een 4 c# wpf listview mvvm

我有以下要求:

  1. 窗口将显示一个ListView包含多个项目。
  2. 用户应该能够检查(复选框)任何项目。a) 如果是一个项目,则所有项目都应取消选中和禁用。b) 如果选中的项目未选中,则应启用所有项目。

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

主窗口 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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

模型:

class MyModel
{
    public string Title { get; set; }
    public string Description { get; set; }
    public bool IsSelected { get; set; }
    public bool IsEnabled { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

视图模型:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 5

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

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

    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;
            }
    }
Run Code Online (Sandbox Code Playgroud)

有两个命令,一个用于选择,另一个用于取消选择。神奇之处在于 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>
Run Code Online (Sandbox Code Playgroud)

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

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