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

如何在ViewModel中获取CollectionView的SelectedItems?

解决方案:MAUI CollectionView 选中项双向绑定到 ViewModel

问题根源

CollectionView 的 SelectedItems 属性类型为 IList<object>,双向绑定时:

  1. 直接绑定 ObservableCollection<User> 会因类型不匹配报错
  2. 原转换器的 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:02:08