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

WinUI3+Microsoft.Toolkit.Mvvm获取ListView SelectedItems为空问题

解决WinUI3中ListView SelectedItems通过CommandParameter传递为null的问题

问题根源

WinUI3 的 ListView.SelectedItems 并非依赖属性(Dependency Property),直接通过 CommandParameter 绑定该属性时,绑定引擎无法跟踪其动态变化,且在命令触发时往往只能传递初始的 null 值,而非当前实际选中的项集合。

解决方案

方法1:使用行为库传递选中项

借助 Microsoft.Xaml.Behaviors.WinUI.Managed 行为库,监听 SelectionChanged 事件并直接传递选中项集合:

  1. 安装NuGet包:Microsoft.Xaml.Behaviors.WinUI.Managed
  2. 在XAML中添加命名空间:
xmlns:behaviors="using:Microsoft.Xaml.Behaviors"
  1. 为目标ListView配置行为:
<ListView x:Name="FruitListView" ItemsSource="{Binding Fruits}">
    <behaviors:Interaction.Behaviors>
        <behaviors:EventTriggerBehavior EventName="SelectionChanged">
            <behaviors:InvokeCommandAction 
                Command="{Binding UpdateSelectedFruitsCommand}"
                CommandParameter="{Binding ElementName=FruitListView, Path=SelectedItems}"/>
        </behaviors:EventTriggerBehavior>
    </behaviors:Interaction.Behaviors>
    <!-- ItemTemplate 等其他配置 -->
</ListView>
  1. 在ViewModel中实现命令:
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
using System.Collections;
using System.Collections.ObjectModel;

public partial class MainViewModel : ObservableObject
{
    public ObservableCollection<string> Fruits { get; } = new() { "苹果", "香蕉", "橙子", "草莓" };
    public ObservableCollection<string> SelectedFruits { get; } = new();

    [ObservableProperty]
    private int _selectedCount;

    [RelayCommand]
    private void UpdateSelectedFruits(IList selectedItems)
    {
        SelectedFruits.Clear();
        foreach (var item in selectedItems)
        {
            if (item is string fruitName)
            {
                SelectedFruits.Add(fruitName);
            }
        }
        SelectedCount = SelectedFruits.Count;
    }
}
  1. 绑定第二个ListView和TextBox:
<ListView ItemsSource="{Binding SelectedFruits}"/>
<TextBox Text="{Binding SelectedCount, Mode=OneWay}" IsReadOnly="True"/>

方法2:自定义附加属性实现双向绑定

通过自定义附加属性,将 ListView.SelectedItems 与ViewModel中的集合双向绑定,无需依赖命令传参:

  1. 创建附加属性类:
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections;

public static class ListViewExtensions
{
    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.RegisterAttached(
            "SelectedItems",
            typeof(IList),
            typeof(ListViewExtensions),
            new PropertyMetadata(null, 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 -= ListView_SelectionChanged;
            if (e.NewValue is IList newList)
            {
                listView.SelectionChanged += ListView_SelectionChanged;
                // 初始化选中项
                foreach (var item in newList)
                {
                    listView.SelectedItems.Add(item);
                }
            }
        }
    }

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

            foreach (var item in e.RemovedItems)
            {
                boundList.Remove(item);
            }
            foreach (var item in e.AddedItems)
            {
                boundList.Add(item);
            }
        }
    }
}
  1. 在XAML中使用附加属性:
<ListView x:Name="FruitListView"
          ItemsSource="{Binding Fruits}"
          local:ListViewExtensions.SelectedItems="{Binding SelectedFruits, Mode=TwoWay}">
    <!-- ItemTemplate 等其他配置 -->
</ListView>
  1. 在ViewModel中监听集合变化:
using Microsoft.Toolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;

public partial class MainViewModel : ObservableObject
{
    public ObservableCollection<string> Fruits { get; } = new() { "苹果", "香蕉", "橙子", "草莓" };
    public ObservableCollection<string> SelectedFruits { get; } = new();

    [ObservableProperty]
    private int _selectedCount;

    public MainViewModel()
    {
        SelectedFruits.CollectionChanged += (s, e) =>
        {
            SelectedCount = SelectedFruits.Count;
        };
    }
}
  1. 绑定第二个ListView和TextBox:
<ListView ItemsSource="{Binding SelectedFruits}"/>
<TextBox Text="{Binding SelectedCount, Mode=OneWay}" IsReadOnly="True"/>

方法3:代码后台直接处理(适合简单场景)

如果项目复杂度较低,可直接在页面代码后台处理选中事件:

  1. 在XAML中添加事件绑定:
<ListView x:Name="FruitListView"
          ItemsSource="{Binding Fruits}"
          SelectionChanged="FruitListView_SelectionChanged">
    <!-- ItemTemplate 等其他配置 -->
</ListView>
  1. 在代码后台实现事件逻辑:
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;

public sealed partial class MainPage : Page
{
    private MainViewModel ViewModel => (MainViewModel)DataContext;

    public MainPage()
    {
        this.InitializeComponent();
        DataContext = new MainViewModel();
    }

    private void FruitListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (sender is ListView listView)
        {
            ViewModel.SelectedFruits.Clear();
            foreach (var item in listView.SelectedItems)
            {
                if (item is string fruitName)
                {
                    ViewModel.SelectedFruits.Add(fruitName);
                }
            }
            ViewModel.SelectedCount = ViewModel.SelectedFruits.Count;
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:05:23