如何在ViewModel中获取CollectionView的SelectedItems?
解决方案:MAUI CollectionView 选中项双向绑定到 ViewModel
问题根源
CollectionView 的 SelectedItems 属性类型为 IList<object>,双向绑定时:
- 直接绑定
ObservableCollection<User>会因类型不匹配报错 - 原转换器的
ConvertBackTo逻辑错误:CollectionView 返回的选中项集合并非ObservableCollection<User>,而是内部私有集合,value as ObservableCollection<User>会返回null,导致 ViewModel 的SelectedMembers始终为空
方案一:修正类型转换器(推荐程度:★★★)
重写转换器,正确处理 IList<object> 和 ObservableCollection<User> 之间的双向转换:
using CommunityToolkit.Maui.Converters; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; namespace Portal.ValueConverters { internal class SelectedItemsConverter : BaseConverter<ObservableCollection<User>, IList<object>> { public override IList<object> DefaultConvertReturnValue => []; public override ObservableCollection<User> DefaultConvertBackReturnValue => []; // ViewModel → View:将User集合转为object列表 public override IList<object> ConvertFrom(ObservableCollection<User> value, CultureInfo culture) { return value?.Cast<object>().ToList() ?? []; } // View → ViewModel:将选中的object列表转为User集合 public override ObservableCollection<User> ConvertBackTo(IList<object> value, CultureInfo culture) { var selectedUsers = new ObservableCollection<User>(); if (value != null) { foreach (var item in value.OfType<User>()) { selectedUsers.Add(item); } } return selectedUsers; } } }
XAML 中保持原绑定逻辑,确保转换器已在资源字典注册:
<ResourceDictionary> <valueConverters:SelectedItemsConverter x:Key="UserObservableCollectionToIListOfObjectsConverter" /> </ResourceDictionary>
方案二:直接使用 IList<object> 并封装转换属性(推荐程度:★★★★★)
无需转换器,直接绑定 IList<object>,在 ViewModel 中封装一个只读属性获取强类型的选中用户集合:
public partial class SelectGroupMembersViewModel:ObservableObject { private readonly IUserFriendshipService _userFriendshipService; public Command GetUserFriendsCommand { get; } public Command GetSelectedMembersCommand { get; } // 直接绑定CollectionView的SelectedItems public IList<object> SelectedMembers { get; set; } = []; // 封装强类型的选中用户集合 public IEnumerable<User> SelectedUsers => SelectedMembers.OfType<User>(); public ObservableCollection<User> Friends { get; } public SelectGroupMembersViewModel(IUserFriendshipService userFriendshipService) { _userFriendshipService = userFriendshipService; GetUserFriendsCommand = new(async () => await GetUserFriendsAsync()); GetSelectedMembersCommand = new(async () => await GetSelectedMembersAsync()); Friends = []; } public async Task GetUserFriendsAsync() { IList<User> userFriends = await _userFriendshipService.GetCurrentUserFriendsAsync(); Friends.Clear(); foreach (User friend in userFriends) Friends.Add(friend); } public async Task GetSelectedMembersAsync() { // 使用强类型集合处理选中项 var selectedUsers = SelectedUsers.ToList(); // 后续业务逻辑... } }
XAML 简化绑定(移除转换器):
<CollectionView SelectedItems="{Binding SelectedMembers, Mode=TwoWay}" ItemsSource="{Binding Friends}" SelectionMode="Multiple" BackgroundColor="#222"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="models:User"> <HorizontalStackLayout Padding="20"> <ImageButton Source="{Binding ImageSource}" CornerRadius="25" WidthRequest="50" HeightRequest="50" HorizontalOptions="Center"/> <Label Text="{Binding Name}" FontSize="Title" Margin="5" VerticalOptions="Center"/> </HorizontalStackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView>
方案三:使用 Behavior 监听选中事件(推荐程度:★★★)
通过自定义 Behavior 监听 SelectionChanged 事件,直接更新 ViewModel 的 ObservableCollection<User>:
using Microsoft.Maui.Controls; using System.Collections.ObjectModel; namespace Portal.Behaviors { public class CollectionViewSelectionBehavior : Behavior<CollectionView> { public static readonly BindableProperty SelectedItemsProperty = BindableProperty.Create(nameof(SelectedItems), typeof(ObservableCollection<User>), typeof(CollectionViewSelectionBehavior), new ObservableCollection<User>()); public ObservableCollection<User> SelectedItems { get => (ObservableCollection<User>)GetValue(SelectedItemsProperty); set => SetValue(SelectedItemsProperty, value); } protected override void OnAttachedTo(CollectionView bindable) { base.OnAttachedTo(bindable); bindable.SelectionChanged += OnSelectionChanged; } protected override void OnDetachingFrom(CollectionView bindable) { base.OnDetachingFrom(bindable); bindable.SelectionChanged -= OnSelectionChanged; } private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { SelectedItems.Clear(); foreach (var item in e.CurrentSelection.OfType<User>()) { SelectedItems.Add(item); } } } }
XAML 中绑定 Behavior:
<CollectionView ItemsSource="{Binding Friends}" SelectionMode="Multiple" BackgroundColor="#222"> <CollectionView.Behaviors> <behaviors:CollectionViewSelectionBehavior SelectedItems="{Binding SelectedMembers, Mode=TwoWay}" /> </CollectionView.Behaviors> <CollectionView.ItemTemplate> <!-- 模板内容不变 --> </CollectionView.ItemTemplate> </CollectionView>
ViewModel 保持原 ObservableCollection<User> SelectedMembers 定义即可。
内容的提问来源于stack exchange,提问作者Hossam Fares
相关产品推荐
相关产品推荐

