.NET MAUI传递QueryParameter时获取值为null的问题
MAUI QueryProperty传值后ViewModel内属性为null的解决方法
问题场景
我有两个使用CollectionView展示列表的页面:主页面ExploreCountriesPage和子页面ExploreRadiosPage,需要把主页面的SelectedItem作为子页面的SelectedCountry,用来填充子页面的数据。
目前遇到的问题:UI能正常显示传递过来的国家名称,但在ViewModel里调用InitializeData()加载数据时,SelectedCountry却是null。已经实现了INotifyPropertyChanged接口,但没解决问题。
主页面ViewModel代码
public class ExploreCountriesViewModel : INotifyPropertyChanged { private NameAndCount selectedItem; public NameAndCount SelectedItem { get => selectedItem; set { selectedItem = value; RaisePropertyChanged(); } } // ... 其他代码 public ICommand NavigateTo => new Command(() => { var navParam = new Dictionary<string, object> { {"SelectedCountry", SelectedItem } }; Shell.Current.GoToAsync($"{nameof(ExploreRadiosPage)}", navParam); }); // ... 其他代码 }
子页面ViewModel代码
[QueryProperty(nameof(SelectedCountry), "SelectedCountry")] public class ExploreRadiosViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); private readonly IRadioBrowserClient radioBrowserClient; private NameAndCount selectedCountry; private List<StationInfo> stations; public List<StationInfo> Stations { get => stations; set { stations = value; RaisePropertyChanged(); } } public NameAndCount SelectedCountry // 从QueryParameter传值后仍为null { get => selectedCountry; set { selectedCountry = value; RaisePropertyChanged(); } } public ExploreRadiosViewModel(IRadioBrowserClient radioBrowserClient) { this.radioBrowserClient = radioBrowserClient; InitializeData(); } private async Task InitializeData() { try { Stations = await radioBrowserClient.Search.AdvancedAsync(new AdvancedSearchOptions { Country = SelectedCountry.Name // SelectedCountry为null! }); foreach (var result in Stations) { Debug.WriteLine(result.Name); } } catch (Exception e) { Debug.WriteLine(e); } } }
子页面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:viewmodel="clr-namespace:Rad.io.Client.MAUI.ViewModels" x:Class="Rad.io.Client.MAUI.Pages.ExploreRadiosPage" x:DataType="viewmodel:ExploreRadiosViewModel" xmlns:radios="clr-namespace:RadioBrowser.Models;assembly=RadioBrowser" Title="{Binding SelectedCountry.Name}"> <!-- 此处能正确显示国家名称 --> <Grid RowDefinitions="Auto, Auto"> <Entry Grid.Row="0" Placeholder="Search"/> <CollectionView x:Name="RadiosList" Grid.Row="1" SelectionMode="Single" ItemsSource="{Binding Stations}"> <!-- ... 其他布局代码 --> </CollectionView> </Grid> </ContentPage>
问题原因
核心逻辑顺序错误:MAUI导航时,会先实例化ViewModel(执行构造函数),之后才会通过QueryProperty机制给SelectedCountry赋值。你在构造函数里直接调用InitializeData(),这时候SelectedCountry还没被赋值,自然是null。而UI能正常显示,是因为后续SelectedCountry赋值后触发了PropertyChanged,UI绑定自动更新了。
解决方法
方法1:在SelectedCountry的setter中触发数据加载
修改子页面ViewModel的SelectedCountry属性,在赋值完成后调用数据初始化方法:
public NameAndCount SelectedCountry { get => selectedCountry; set { selectedCountry = value; RaisePropertyChanged(); // 赋值完成后再加载数据,使用_避免异步方法警告 _ = InitializeData(); } } // 构造函数中移除InitializeData的调用 public ExploreRadiosViewModel(IRadioBrowserClient radioBrowserClient) { this.radioBrowserClient = radioBrowserClient; // 删掉 InitializeData(); }
方法2:利用页面生命周期方法(推荐)
在子页面的代码后台重写OnNavigatedTo方法,确保参数传递完成后再通知ViewModel加载数据:
- 子页面代码后台(ExploreRadiosPage.xaml.cs):
protected override void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); // 确认BindingContext是目标ViewModel if (BindingContext is ExploreRadiosViewModel vm) { _ = vm.InitializeData(); } }
- 同时修改ViewModel的构造函数,移除
InitializeData()的调用:
public ExploreRadiosViewModel(IRadioBrowserClient radioBrowserClient) { this.radioBrowserClient = radioBrowserClient; // 移除 InitializeData(); }
注意事项
- 异步方法调用时用
_ = InitializeData()或者在异步生命周期方法中用await,避免阻塞UI线程。 - 如果使用方法1,要考虑重复赋值的情况(比如页面返回后再次导航),可以加个判断避免重复加载数据。
内容的提问来源于stack exchange,提问作者Sisimośki
相关产品推荐
相关产品推荐

