You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF MVVM模式下ListView取消选中时SelectionChanged未触发问题

问题分析与解决方案

首先明确:SelectionChanged事件在取消选中项时是会触发的,你的问题出在参数传递和处理逻辑上——之前的实现只能拿到当前选中的项集合,无法获取被取消选中的项,且未处理移除逻辑。

方案一:双向绑定SelectedItems(MVVM推荐方案)

WPF原生ListView.SelectedItems是只读集合,无法直接双向绑定,我们可以通过附加属性实现双向同步:

1. 创建附加属性类

public class ListViewSelectedItemsBehavior
{
    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.RegisterAttached(
            "SelectedItems",
            typeof(IList),
            typeof(ListViewSelectedItemsBehavior),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChanged));

    public static IList GetSelectedItems(DependencyObject obj)
    {
        return (IList)obj.GetValue(SelectedItemsProperty);
    }

    public static void SetSelectedItems(DependencyObject obj, IList value)
    {
        obj.SetValue(SelectedItemsProperty, value);
    }

    private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ListView listView)
        {
            listView.SelectionChanged -= OnListViewSelectionChanged;
            if (e.NewValue is IList newSelectedItems)
            {
                listView.SelectionChanged += OnListViewSelectionChanged;
                SyncSelectedItems(listView, newSelectedItems);
            }
        }
    }

    private static void OnListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (sender is ListView listView)
        {
            var boundSelectedItems = GetSelectedItems(listView);
            if (boundSelectedItems == null) return;

            // 移除取消选中的项
            foreach (var item in e.RemovedItems)
            {
                if (boundSelectedItems.Contains(item))
                {
                    boundSelectedItems.Remove(item);
                }
            }

            // 添加新选中的项
            foreach (var item in e.AddedItems)
            {
                if (!boundSelectedItems.Contains(item))
                {
                    boundSelectedItems.Add(item);
                }
            }
        }
    }

    private static void SyncSelectedItems(ListView listView, IList selectedItems)
    {
        listView.SelectedItems.Clear();
        foreach (var item in selectedItems)
        {
            listView.SelectedItems.Add(item);
        }
    }
}

2. 修改XAML绑定

<ListView 
  x:Name="myListView"
  SelectionMode="Extended"
  local:ListViewSelectedItemsBehavior.SelectedItems="{Binding SelectedItems}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Nombre" DisplayMemberBinding="{Binding FileName}" />
            <GridViewColumn Header="Dirección" DisplayMemberBinding="{Binding FilePath}" />
        </GridView>
    </ListView.View>
</ListView>

3. 简化ViewModel

无需额外命令,直接维护SelectedItems即可:

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

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

方案二:修改事件绑定,获取取消选中项

如果想继续使用Interaction.Triggers,可以传递事件参数直接拿到RemovedItems:

1. 修改XAML

<ListView 
  x:Name="myListView"
  SelectionMode="Extended">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" PassEventArgsToCommand="True" />
        </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>

2. 修改ViewModel命令

private DelegateCommand<SelectionChangedEventArgs> _selectionChangedCommand;

public DelegateCommand<SelectionChangedEventArgs> SelectionChangedCommand =>
    _selectionChangedCommand ?? (_selectionChangedCommand = new DelegateCommand<SelectionChangedEventArgs>(ExecuteSelectionChanged));

private void ExecuteSelectionChanged(SelectionChangedEventArgs e)
{
    // 处理取消选中的项
    foreach (var item in e.RemovedItems.Cast<FileItem>())
    {
        if (SelectedItems.Contains(item))
        {
            SelectedItems.Remove(item);
        }
    }

    // 处理新选中的项
    foreach (var item in e.AddedItems.Cast<FileItem>())
    {
        if (!SelectedItems.Contains(item))
        {
            SelectedItems.Add(item);
        }
    }
}

问题根源总结

  1. 之前的CommandParameter传递的是SelectedItems,这是更新后的选中集合,无法获取被取消的项;
  2. 原ExecuteGetSelectedItems方法只做添加操作,未处理移除逻辑,导致ViewModel选中集合与实际状态脱节。

内容的提问来源于stack exchange,提问作者c3n74ur0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:33:16