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
相关产品推荐
相关产品推荐

