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

MAUI Picker绑定SQLite与ViewModel时显示异常问题排查

.NET MAUI赛事管理应用Picker绑定问题

我开发的.NET MAUI赛事管理应用对接多个SQLite数据库,分别存储队伍(Squad)和参赛者(Participant)数据。在创建/编辑参赛者详情时,通过Picker选择对应队伍,但打开参赛者详情时Picker显示为空。调试发现保存/加载参赛者时SelectedSquad已正确赋值,但SelectedItem绑定后Picker仍无显示。


参赛者ViewModel代码

using MatchTracker.Pages;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Web;

namespace MatchTracker.Models.ViewModels
{
    public partial class ParticipantDetailsVM : BaseViewModel, IQueryAttributable
    {

        [ObservableProperty]
        Participant currentParticipant;

        [ObservableProperty]
        Squad selectedSquad;

        public ObservableCollection<Squad> SquadsList { get; }

        [ObservableProperty]
        public string saveButtonText;

        public ParticipantDetailsVM()
        {
            Title = "Participant Details";
            SquadsList = new ObservableCollection<Squad>();
        }

        public async void ApplyQueryAttributes(IDictionary<string, object> query)
        {
            if (int.TryParse(HttpUtility.UrlDecode(query["id"].ToString()), out int Id))
                if (Id == 0)
                {
                    SaveButtonText = "Add Participant";
                    CurrentParticipant = new Participant { Id = 0 };
                }
                else
                {
                    SaveButtonText = "Update participant";
                    CurrentParticipant = await App._ParticipantsDb.GetAsync(Id);
                }
        }

        async Task LoadSquads()
        {
            try
            {
                SquadsList.Clear();

                var all = await App._SquadsDb.GetAllAsync();
                foreach (var item in all)
                {
                    SquadsList.Add(item);
                }
            
                SelectedSquad = await App._SquadsDb.GetAsync(CurrentParticipant.SquadId);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                RefreshNeeded = false;
            }
        }

        [RelayCommand]
        async Task SaveParticipant()
        {
            try
            {
                CurrentParticipant.SquadId = SelectedSquad.Id;
                await App._ParticipantsDb.AddAsync(CurrentParticipant);
                await Shell.Current.GoToAsync(nameof(ParticipantsListView));

            } catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }

        public async void OnAppearing()
        {
            await LoadSquads();
        }
    }
}

参赛者视图XAML文件

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MatchTracker.Pages.ParticipantDetailsView"
             xmlns:vm="clr-namespace:MatchTracker.Models.ViewModels"
             x:DataType="vm:ParticipantDetailsVM"
             Title="{Binding Title}">
    <VerticalStackLayout>
        <TableView Intent="Data">
            <TableRoot>
                <TableSection Title="Basic Information">
                    <EntryCell x:Name="ParticipantNameField" Label="Name" Text="{Binding CurrentParticipant.Name, Mode=TwoWay}" />
                </TableSection>
            </TableRoot>
        </TableView>
        <Picker x:Name="ParticipantSquadPicker" Title="Squad" ItemsSource="{Binding SquadsList}" SelectedItem="{Binding SelectedSquad, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}"/>
        <Label x:Name="Debug" Text="{Binding CurrentParticipant.SquadId}"/>
        <Button x:Name="SaveButton" Text="{Binding SaveButtonText}" Command="{Binding SaveParticipantCommand}"/>
    </VerticalStackLayout>
</ContentPage>

视图代码隐藏文件

using MatchTracker.Models.ViewModels;

namespace MatchTracker.Pages;

public partial class ParticipantDetailsView : ContentPage
{
    public ParticipantDetailsView(ParticipantDetailsVM vm)
    {
        InitializeComponent();
        BindingContext = vm;
    }
    protected override void OnAppearing()
    {
        base.OnAppearing();
        ((ParticipantDetailsVM)BindingContext)?.OnAppearing();
    }
}

问题原因及解决方案

原因

Picker的SelectedItem匹配依赖对象引用相等性。你通过await App._SquadsDb.GetAsync(CurrentParticipant.SquadId)获取的SelectedSquad是数据库返回的新实例,而SquadsList中的队伍是从GetAllAsync获取的另一批实例,两者引用不同,导致Picker无法找到匹配项,因此显示为空。

解决方法

修改LoadSquads方法,从已加载到SquadsList的队伍中查找对应ID的实例,保证引用一致:

async Task LoadSquads()
{
    try
    {
        SquadsList.Clear();

        var all = await App._SquadsDb.GetAllAsync();
        foreach (var item in all)
        {
            SquadsList.Add(item);
        }
    
        // 从已加载的列表中匹配队伍,确保引用一致
        SelectedSquad = SquadsList.FirstOrDefault(s => s.Id == CurrentParticipant.SquadId);
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
    }
    finally
    {
        RefreshNeeded = false;
    }
}

额外优化建议

  • 可以给Squad类实现IEquatable<Squad>接口,重写Equals和GetHashCode方法,基于Id判断相等性,这样即使引用不同,只要ID匹配也能被Picker识别。
  • 若使用SQLite ORM,可开启实体跟踪功能,避免重复创建实例,但上述方法更直接解决当前问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 07:35:31