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

MVVM模式下Picker绑定SelectedItem时的选中项传递问题

MVVM模式下Picker控件更新数据匹配问题

我采用MVVM模式开发,新增与展示功能运行正常,但实现更新功能时碰到Picker控件的问题:SelectedItem已绑定新数据,不清楚如何将展示页面的Picker选中项传递到编辑页面。已通过QueryProperty将选中的Dungeon对象传递至UpdateDungeonPage,但Picker无法正确匹配原有数据;试过SelectedIndex方法,仅支持int类型,并非最优方案。


相关代码实现

DisplayViewModel中的UpdateDungeon方法

public async void UpdateDungeon()
{
    Dictionary<string, object> newParm = new Dictionary<string, object>();
    newParm.Add("DungeonDetails", selectedDungeon);
    await AppShell.Current.GoToAsync(nameof(UpdateDungeonPage), newParm);
}

展示页面ListView(dungeondisplay.xaml)

<ListView ItemsSource="{Binding dungeonsCollection}"
          IsRefreshing="True"
          ItemSelected="OnItemSelected">
    <ListView.Header>
        <HorizontalStackLayout Spacing="10">
            <Label Text="Name" />
            <Label Text="Level" />
            <Label Text="Enchant" />
            <Label Text="Zone" />
            <Label Text="Type" />
        </HorizontalStackLayout>
    </ListView.Header>

    <ListView.ItemTemplate>
        <DataTemplate x:DataType="models:Dungeon">
            <ViewCell>
                <StackLayout Spacing="15">
                <HorizontalStackLayout Spacing="10">
                    <Label Text="{Binding name}" />
                    <Label Text="{Binding level}" />
                    <Label Text="{Binding enchant}" />
                    <Label Text="{Binding zone}" />
                    <Label Text="{Binding type}" />
                </HorizontalStackLayout>
            </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

ItemSelected事件(dungeondisplaypage.xaml.cs)

public void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
    if (args.SelectedItem == null)
    {
        return;
    }

    _viewModel.selectedDungeon = args.SelectedItem as Dungeon;
}

编辑页面Picker(UpdateDungeonPage.xaml)

<Picker ItemsSource="{Binding TierList}"
        ItemDisplayBinding="{Binding Tier}"
        SelectedItem="{Binding SelectedTier}"
 />

UpdateDungeonViewModel.cs

[QueryProperty(nameof(DungeonDetails), "DungeonDetails")]
public partial class UpdateDungeonPageViewModel : ObservableObject
{
    [ObservableProperty]
    private Dungeon _dungeonDetails = new Dungeon();

    private readonly IDungeonService _service;

    public List<DungeonTier> TierList { get; set; }

    public List<DungeonEnchant> EnchantList { get; set; }

    public UpdateDungeonPageViewModel(IDungeonService dungeonService)
    {
        _service = dungeonService;

        TierList = new List<DungeonTier>();
        TierList.Add(new DungeonTier() { Id = 0, Tier = "4" });
        TierList.Add(new DungeonTier() { Id = 1, Tier = "5" });
        TierList.Add(new DungeonTier() { Id = 2, Tier = "6" });
        TierList.Add(new DungeonTier() { Id = 3, Tier = "7" });
        TierList.Add(new DungeonTier() { Id = 4, Tier = "8" });

        EnchantList = new List<DungeonEnchant>();
        EnchantList.Add(new DungeonEnchant() { Id = 0, Enchant = 0 });
        EnchantList.Add(new DungeonEnchant() { Id = 1, Enchant = 1 });
        EnchantList.Add(new DungeonEnchant() { Id = 2, Enchant = 2 });
        EnchantList.Add(new DungeonEnchant() { Id = 3, Enchant = 3 });
    }

    public event PropertyChangingEventHandler PropertyChanged;

    void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged == null)
        {
            return;
        }

        PropertyChanged.Invoke(this, new PropertyChangingEventArgs(propertyName));
    }

    [ObservableProperty]
    private DungeonTier _level;

    public DungeonTier SelectedTier
    {
        get { return Level; }
        set
        {
            Level = value;
            OnPropertyChanged();
        }
    }

    [ObservableProperty]
    private DungeonEnchant _enchant;

    public DungeonEnchant SelectedEnchant
    {
        get { return Enchant; }
        set
        {
            Enchant = value;
            OnPropertyChanged();
        }
    }

    [RelayCommand]
    public async void UpdateDungeon()
    {
        DungeonDetails.enchant = Enchant.Enchant;

        DungeonDetails.level = Level.Tier;

        int respone = -1;
        if (DungeonDetails.id > 0)
        {
            respone = await _service.UpdateItemAsync(DungeonDetails);
        }
        await Shell.Current.Navigation.PopAsync();
    }
}

DungeonDisplay.xaml.cs完整代码

protected DungeonDisplayViewModel _viewModel;

public DungeonDisplay(DungeonDisplayViewModel viewModel)
{
    InitializeComponent();
    _viewModel = viewModel;
    this.BindingContext = viewModel;
}

public void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
    if (args.SelectedItem == null)
    {
        return;
    }

    _viewModel.selectedDungeon = args.SelectedItem as Dungeon;
}

protected override void OnAppearing()
{
    base.OnAppearing();
    _viewModel.GetDungeonList();
}

DungeonDisplayViewModel.cs完整代码

public ObservableCollection<Dungeon> dungeonsCollection { get; set; } = new ObservableCollection<Dungeon>();

private readonly IDungeonService _service;

public Dungeon selectedDungeon { get; set; } = new Dungeon();

public DungeonDisplayViewModel(IDungeonService dungeonService) 
{
    _service = dungeonService;
}

[RelayCommand]
public async void GetDungeonList()
{
    var dungeonList = await _service.GetAllAsync();

    if (dungeonList?.Count > 0 )
    {
        dungeonsCollection.Clear();

        foreach( var dungeon in dungeonList )
        {
            dungeonsCollection.Add(dungeon);
        }
    }
}

[RelayCommand]
public async void AddDungeon()
{
    await AppShell.Current.GoToAsync(nameof(AddDungeonPage));
}

[RelayCommand]
public async void UpdateDungeon()
{
    Dictionary<string, object> newParm = new Dictionary<string, object>();
    newParm.Add("DungeonDetails", selectedDungeon);
    await AppShell.Current.GoToAsync(nameof(UpdateDungeonPage),newParm);
}

解决方案

Picker无法匹配原有数据的核心原因是:SelectedItem的匹配依赖对象引用相等,传递过来的DungeonDetails中的level(字符串)和TierList中的DungeonTier对象不是同一个引用,所以Picker找不到对应的选中项。

步骤1:修正ObservableObject的PropertyChanged实现

ViewModel继承了ObservableObject,但自定义的PropertyChanged事件和OnPropertyChanged方法会导致CommunityToolkit的自动属性通知失效,直接删除以下代码:

public event PropertyChangingEventHandler PropertyChanged;

void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
    if (PropertyChanged == null)
    {
        return;
    }

    PropertyChanged.Invoke(this, new PropertyChangingEventArgs(propertyName));
}

步骤2:在DungeonDetails赋值后匹配Picker选项

利用CommunityToolkit的[ObservableProperty]回调,在DungeonDetails被赋值后,从列表中找到对应的数据对象,赋值给选中项属性:

[ObservableProperty]
private Dungeon _dungeonDetails = new Dungeon();

// 当DungeonDetails属性变化时触发的回调
partial void OnDungeonDetailsChanged(Dungeon value)
{
    // 匹配Tier选项
    SelectedTier = TierList.FirstOrDefault(t => t.Tier == value.level);
    // 匹配Enchant选项
    SelectedEnchant = EnchantList.FirstOrDefault(e => e.Enchant == value.enchant);
}

// 直接用ObservableProperty生成选中项属性,无需手动包装
[ObservableProperty]
private DungeonTier _selectedTier;

[ObservableProperty]
private DungeonEnchant _selectedEnchant;

同时修改Xaml绑定,直接绑定到自动生成的属性:

<Picker ItemsSource="{Binding TierList}"
        ItemDisplayBinding="{Binding Tier}"
        SelectedItem="{Binding SelectedTier}"
 />
<Picker ItemsSource="{Binding EnchantList}"
        ItemDisplayBinding="{Binding Enchant}"
        SelectedItem="{Binding SelectedEnchant}"
 />

步骤3:修正UpdateDungeon方法中的赋值

直接使用自动生成的选中项属性更新数据:

[RelayCommand]
public async void UpdateDungeon()
{
    if (SelectedEnchant != null)
        DungeonDetails.enchant = SelectedEnchant.Enchant;

    if (SelectedTier != null)
        DungeonDetails.level = SelectedTier.Tier;

    int response = -1;
    if (DungeonDetails.id > 0)
    {
        response = await _service.UpdateItemAsync(DungeonDetails);
    }
    await Shell.Current.Navigation.PopAsync();
}

额外优化:避免传递空对象

在DungeonDisplayViewModel的UpdateDungeon方法中,先判断选中对象是否为空:

[RelayCommand]
public async void UpdateDungeon()
{
    if(selectedDungeon == null) return;
    
    Dictionary<string, object> newParm = new Dictionary<string, object>();
    newParm.Add("DungeonDetails", selectedDungeon);
    await AppShell.Current.GoToAsync(nameof(UpdateDungeonPage), newParm);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:17:56