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 事件并直接传递选中项集合:
- 安装NuGet包:
Microsoft.Xaml.Behaviors.WinUI.Managed - 在XAML中添加命名空间:
xmlns:behaviors="using:Microsoft.Xaml.Behaviors"
- 为目标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>
- 在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; } }
- 绑定第二个ListView和TextBox:
<ListView ItemsSource="{Binding SelectedFruits}"/> <TextBox Text="{Binding SelectedCount, Mode=OneWay}" IsReadOnly="True"/>
方法2:自定义附加属性实现双向绑定
通过自定义附加属性,将 ListView.SelectedItems 与ViewModel中的集合双向绑定,无需依赖命令传参:
- 创建附加属性类:
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); } } } }
- 在XAML中使用附加属性:
<ListView x:Name="FruitListView" ItemsSource="{Binding Fruits}" local:ListViewExtensions.SelectedItems="{Binding SelectedFruits, Mode=TwoWay}"> <!-- ItemTemplate 等其他配置 --> </ListView>
- 在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; }; } }
- 绑定第二个ListView和TextBox:
<ListView ItemsSource="{Binding SelectedFruits}"/> <TextBox Text="{Binding SelectedCount, Mode=OneWay}" IsReadOnly="True"/>
方法3:代码后台直接处理(适合简单场景)
如果项目复杂度较低,可直接在页面代码后台处理选中事件:
- 在XAML中添加事件绑定:
<ListView x:Name="FruitListView" ItemsSource="{Binding Fruits}" SelectionChanged="FruitListView_SelectionChanged"> <!-- ItemTemplate 等其他配置 --> </ListView>
- 在代码后台实现事件逻辑:
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
相关产品推荐
相关产品推荐

