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

.NET MAUI Picker SelectedIndex绑定初始不显示选中项问题

.NET MAUI Picker初始选中项不显示问题解决

问题描述

在.NET MAUI项目中使用Picker控件时,遇到初始加载无法展示绑定选中项的问题:点击Picker时,ItemSource、ItemDisplayBinding和SelectedItem均能正常工作,但视图首次显示时,Picker并未展示绑定列表中的对应选中对象。

项目代码

视图MainPage.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"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:models="clr-namespace:MauiPicker;assembly=MauiPicker"
             xmlns:viewModels="clr-namespace:MauiPicker"
             x:Class="MauiPicker.MainPage"
             x:DataType="viewModels:MainViewModel">

    <Grid
        ColumnDefinitions="*"
        RowDefinitions="*,*">
        <CollectionView
            Grid.Row="0"
            Grid.Column="0"
            ItemsSource="{Binding PartAResultLists}"
            SelectionMode="None">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="models:PartAResultList">
                    <Grid Padding="5">
                        <Border>
                            <Grid Padding="10"
                                  ColumnDefinitions="Auto,*"
                                  RowDefinitions="Auto"
                                  RowSpacing="7">
                                <Label Text="Outlet Type:" 
                                       Grid.Column="0" Grid.Row="0"
                                       HorizontalOptions="End"
                                       VerticalOptions="Center"
                                       Margin="0,0,0,0"/>
                                <Border
                                    Grid.Column="1"
                                    Grid.Row="0"
                                    Grid.ColumnSpan="2">
                                    <Picker
                                        Title="Select an Outlet"
                                        ItemsSource="{Binding Source={RelativeSource AncestorType={x:Type viewModels:MainViewModel}}, Path=Outlets}"
                                        ItemDisplayBinding="{Binding Name}"
                                        SelectedIndex="{Binding OutletIndex}"
                                        SelectedItem="{Binding OutletName}">
                                    </Picker>
                                </Border>
                            </Grid>
                        </Border>
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
        <Button
            Grid.Row="1"
            Grid.Column="0"
            Text="Reload List"
            HorizontalOptions="Center"
            VerticalOptions="Center"
            Command="{Binding Source={RelativeSource AncestorType={x:Type viewModels:MainViewModel}}, Path=LoadResultsCommand}">
        </Button>
    </Grid>

</ContentPage>

代码后置文件MainPage.xaml.cs

namespace MauiPicker;

public partial class MainPage : ContentPage
{
    public MainPage(MainViewModel vm)
    {
        InitializeComponent();
        BindingContext = vm;
    }
}

视图模型MainViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MvvmHelpers;

namespace MauiPicker
{
    public partial class MainViewModel : ObservableObject
    {
        public MainViewModel()
        {
            LoadResults();
        }

        [RelayCommand]
        async Task LoadResults()
        {
            Outlets = new ObservableRangeCollection<Outlet>
            {
                new Outlet(){Name="Outlet0"},
                new Outlet(){Name="Outlet1"},
                new Outlet(){Name="Outlet2"},
            };

            PartAResultLists = new ObservableRangeCollection<PartAResultList>
            {
                new PartAResultList(){OutletIndex = 0, OutletName= new Outlet(){Name="Outlet0" } },
                new PartAResultList(){OutletIndex=1, OutletName= new Outlet(){Name="Outlet1" }},
                new PartAResultList(){OutletIndex = 2, OutletName= new Outlet(){Name="Outlet2" }},
                new PartAResultList(){OutletIndex = 0, OutletName= new Outlet(){Name="Outlet0" }},
                new PartAResultList(){OutletIndex = 2, OutletName= new Outlet(){Name="Outlet2" }}
            };
        }

        [ObservableProperty]
        ObservableRangeCollection<Outlet> outlets;

        [ObservableProperty]
        ObservableRangeCollection<PartAResultList> partAResultLists;
    }
}

模型类

Outlet.cs

using CommunityToolkit.Mvvm.ComponentModel;

namespace MauiPicker
{
    public partial class Outlet : ObservableObject
    {
        [ObservableProperty]
        public string name;
    }
}

PartAResultList.cs

using CommunityToolkit.Mvvm.ComponentModel;

namespace MauiPicker
{
    public partial class PartAResultList : ObservableObject
    {
        [ObservableProperty]
        public Outlet outletName;

        [ObservableProperty]
        public int outletIndex;
    }
}

MauiProgram.cs

using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;

namespace MauiPicker;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseMauiCommunityToolkit()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        builder.Services.AddSingleton<MainPage>();
        builder.Services.AddSingleton<MainViewModel>();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

问题根源及解决方案

1. 对象实例不匹配

Picker的SelectedItem通过引用相等匹配ItemsSource中的对象。原代码中PartAResultList的OutletName是新建的Outlet实例,和Outlets集合中的实例并非同一对象,导致Picker无法识别选中项,初始加载时不显示。

2. 同时绑定SelectedIndex和SelectedItem

同时绑定两个属性可能引发冲突,Picker初始化时会优先处理其中一个,导致状态不一致。

修改方案

步骤1:复用Outlets集合中的实例

修改MainViewModel的LoadResults方法,让PartAResultList的OutletName直接引用Outlets中的对象:

[RelayCommand]
async Task LoadResults()
{
    Outlets = new ObservableRangeCollection<Outlet>
    {
        new Outlet(){Name="Outlet0"},
        new Outlet(){Name="Outlet1"},
        new Outlet(){Name="Outlet2"},
    };

    PartAResultLists = new ObservableRangeCollection<PartAResultList>
    {
        new PartAResultList(){OutletIndex = 0, OutletName= Outlets[0] },
        new PartAResultList(){OutletIndex=1, OutletName= Outlets[1] },
        new PartAResultList(){OutletIndex = 2, OutletName= Outlets[2] },
        new PartAResultList(){OutletIndex = 0, OutletName= Outlets[0] },
        new PartAResultList(){OutletIndex = 2, OutletName= Outlets[2] }
    };
}

步骤2:移除冗余绑定(可选)

建议只保留SelectedItem绑定避免冲突,修改Picker的XAML:

<Picker
    Title="Select an Outlet"
    ItemsSource="{Binding Source={RelativeSource AncestorType={x:Type viewModels:MainViewModel}}, Path=Outlets}"
    ItemDisplayBinding="{Binding Name}"
    SelectedItem="{Binding OutletName}">
</Picker>

若需保留SelectedIndex绑定,需确保OutletIndex的值和OutletName在Outlets中的索引一致,但优先使用SelectedItem更直观可靠。

补充说明

若无法复用实例(如数据来自数据库),需重写Outlet类的Equals和GetHashCode方法,让Picker通过对象内容而非引用判断相等性:

public partial class Outlet : ObservableObject
{
    [ObservableProperty]
    public string name;

    public override bool Equals(object obj)
    {
        return obj is Outlet outlet && name == outlet.name;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(name);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 22:40:22