我有一个
ListView
,它可以正确地将 SelectionChanged
上选定的项目发送到我的 ViewModel
。当选择一个或多个项目时, 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
属性在取消选择时不起作用。
您不应该在视图模型中处理 UI 事件数据。
Interaction
行为通常会导致代码异味,违反 MVVM 设计规则。它只是一个多余的帮手。SelectionChanged
事件并使用事件数据获取选定和未选定的项目。从这里你有多个好的解决方案:
ICommand
实现并将数据作为命令参数传递以下示例使用附加属性来允许注册将在
ICommand
上调用的 Selector.SelectionChanged
。SelctionChanged
源元素能够定义自己的 ICommand
,而无需为每个元素实现新的依赖属性:
DataChanges.cs
数据参数用于与视图模型交换数据。这是可选的。它只会让数据处理更加方便。
class DataChanges<TItem>
{
public DataChanges(TItem oldValue, TItem newValue)
{
this.OldValue = oldValue;
this.NewValue = newItems;
}
public TItem NewValue { get; }
public TItem OldValue { get; }
}
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
public DelegateCommand<DataChanges<IEnumerable<FileItem>>> ProcessSelectedItemsCommand { get; }
= new DelegateCommand<DataChanges<IEnumerable<FileItem>>>(ExecuteProcessSelectedItemsCommand));
private void ExecuteProcessSelectedItemsCommand(DataChanges<IEnumerable<FileItem>> dataChanges)
{
RemoveUnselectedFileItems(dataChanges.OldValue);
AddSelectedFileItems(dataChanges.NewValue);
}
private void AddSelectedFileItems(IEnumerable<FileItem> itemsToAdd)
{
// Use LINQ to filter duplicates
var newItemsExcludingExistingItems = itemsToAdd.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 DataChanges<IEnumerable<FileItem>>(oldFileItems, newFileItems);
if (command?.CanExecute(commandArgument) ?? false)
{
command. Execute(commandArgument);
}
}
}
MainWindow.xaml
<Window>
<ListBox MainWindow.FileItemsChangedCommand="{Binding ProcessSelectedItemsCommand}"
SelectionChanged="OnFileItemsSelectionChanged" />
</Window>