列表视图项目选择更改在取消选择时不会触发,使用具有 MVVM 模式的 WPF

问题描述 投票:0回答:1

我有一个列表视图,它将 SelectionChanged 上的所选项目正确发送到我的视图模块。当选择一个或多个项目时,SelectionChanged 属性会发送选定的项目,但是当取消选择一个或多个项目时,它不会触发 SelectionChanged 属性。

这就是我所做的。

XAML

<ListView 
                    x:Name="myListView"
                    SelectionMode="Extended">
                       <i:Interaction.Triggers>
                           <i:EventTrigger EventName="SelectionChanged">
                                <i:InvokeCommandAction Command="{Binding GetSelectedItemsCommand}"  CommandParameter="{Binding ElementName=myListView, Path=SelectedItems}" />
                           </i:EventTrigger>
                       </i:Interaction.Triggers>
                      <ListView.View>
                         <GridView>
                            <GridViewColumn Header="Nombre" DisplayMemberBinding="{Binding FileName}" />
                            <GridViewColumn Header="Dirección" DisplayMemberBinding="{Binding FilePath}" />
                         </GridView>
                      </ListView.View>
               </ListView>

视图模型

 private ObservableCollection<FileItem> _selectedItems = new ObservableCollection<FileItem>();

 public ObservableCollection<FileItem> SelectedItems
 {
     get { return _selectedItems; }
     set { SetProperty(ref _selectedItems, value); }
 }
  private DelegateCommand<ObservableCollection<Object>> _getSelectedItems;

  public DelegateCommand<ObservableCollection<Object>> GetSelectedItemsCommand =>
      _getSelectedItems ?? (_getSelectedItems = new DelegateCommand<ObservableCollection<Object>>(ExecuteGetSelectedItems));

  private void ExecuteGetSelectedItems(ObservableCollection<Object> fileItems)
  {
      foreach (var file in fileItems)
      {
          FileItem fileItem = (FileItem)file;
          if (fileItem != null && !SelectedItems.Contains(file))
          {
              SelectedItems.Add(fileItem);
          }
      }
  }

我需要一种方法来获取未选择的项目,因为 SelectionChanged 属性在取消选择时不起作用。

c# .net wpf mvvm
1个回答
0
投票

您不应该在视图模型中处理 UI 事件数据。

Interaction
行为通常会导致代码异味,违反 MVVM 设计规则。它只是一个多余的帮手。
解决方案很简单:处理
SelectionChanged
事件并使用事件数据获取选定和未选定的项目。从这里你有多个好的解决方案:

  1. 通过调用API方法将数据直接传递给视图模型类实例(视图模型类型不再是匿名的)
  2. 调用绑定到当前元素的
    ICommand
    实现并将数据作为命令参数传递
  3. 更新绑定到数据源的本地依赖属性

以下示例使用附加属性来允许注册将在

ICommand
上调用的
Selector.SelectionChanged
:

SelectionData.cs
数据参数用于与视图模型交换数据。

class SelectionData<TItem>
{
  public SelectionData(IEnumerable<TItem> unselectedItems, IEnumerable<TItem> selectedItems)
  {
    this.UnselectedItems = unselectedItems;
    this.SelectedItems = selectedItems;
  }

  public IEnumerable<TItem> SelectedItems { get; }
  public IEnumerable<TItem> UnselectedItems { get; }
}

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
  public DelegateCommand<SelectionData<FileItem>> ProcessSelectedItemsCommand { get; }
    = new DelegateCommand<SelectionData<FileItem>>(ExecuteProcessSelectedItemsCommand));

  private void ExecuteProcessSelectedItemsCommand(SelectionData<FileItem> selectionData)
  {
    RemoveUnselectedFileItems(selectionData.UnselectedItems);
    AddSelectedFileItems(selectionData.SelectedItems);
  }

  private void ExecuteProcessSelectedItemsCommand(IEnumerable<FileItem> itemsToAdd)
  {
    var newItemsExcludingExistingItems = selectionData.SelectedItems
      .Except(this.SelectedItems);
    foreach (FileItem newFileItem in newItemsExcludingExistingItems)
    {
      this.SelectedItems.Add(newFileItem);
    }
  }

  private void RemoveUnselectedFileItems(IEnumerable<FileItem> itemsToRemove)
  {
    foreach (FileItem oldFileItem in itemsToRemove)
    {
      this.SelectedItems.Remove(oldFileItem);
    }
  }
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public static void SetFileItemsChangedCommande(UIElement attachingElement, ICommand value)
    => attachingElement.SetValue(FileItemsChangedCommandProperty, value);

  public static ICommand GetFileItemsChangedCommande(UIElement attachingElement)
    => return (ICommand)attachingElement.GetValue(FileItemsChangedCommandProperty);

  public static readonly DependencyProperty FileItemsChangedCommandProperty = DependencyProperty.RegisterAttached(
    "FileItemsChangedCommand",
    typeof(ICommand),
    typeof(MainWindow),
    new PropertyMetadata(default));

  public MainWindow()
  {
    InitializeComponent();

    this.DataContext = new ViewModel();
  }

  private void OnFileItemsSelectionChanged(object sender, SelectionChangedEventArgs e)
  {
    var sourceElement = (UIElement)sender;
    ICommand command = GetFileItemsChangedCommand(sourceElement);

    IEnumerable<FileItem> oldFileItems = e.RemovedItems.Cast<FileItem>();
    IEnumerable<FileItem> newFileItems = e.AddedItems.Cast<FileItem>();
    var commandArgument = new SelectionData<FileItem>(oldFileItems, newFileItems);
    if (command.CanExecute(commandArgument))
    {
      command. Execute(commandArgument);
    }
  }
}

MainWindow.xaml

<Window>
  <ListBox MainWindow.FileItemsChangedCommand="{Binding ProcessSelectedItemsCommand}"
           SelectionChanged="OnFileItemsSelectionChanged" />
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.