MAUI Shell导航:首次跳转后Dictionary传递对象为null的问题
MAUI页面二次跳转时QueryProperty传递对象为null问题
问题现象
首次点击按钮调用OpenSpecialInstructionsCommand可正常跳转至SpecialInstructions页面并加载内容,但返回主页面后再次跳转,目标页面的LineItem对象变为null,导致页面因LineItem.StockNumber为空无法执行加载逻辑,陷入无限加载状态。
相关代码
主ViewModel导航代码
[RelayCommand] async Task OpenSpecialInstructions() { if (LineItem == null) return; this.popupPage.Close(); await Shell.Current.GoToAsync(nameof(SpecialInstructions), true, new Dictionary<string, object> { {"LineItem", LineItem } }); }
注:每次调用该方法时,LineItem均持有有效数据
目标ViewModel代码
[QueryProperty(nameof(LineItem), "LineItem")] public partial class SpecialInstructionsViewModel : BaseViewModel { [ObservableProperty] LineItem lineItem; [RelayCommand] async Task Instructions_Get() { IsBusy = true; if (App.SpecialInstructions.Count() == 0) { await PrefillSpecialInstructions(); } SpecialInstructions = App.SpecialInstructions; foreach (SpecialInstruction instruction in App.DbConnection.SpecialInstructions_Get(LineItem.StockNumber)) { try { SpecialInstructions.FirstOrDefault(x => x.MessageCode == instruction.MessageCode).IsSelected = true; } catch { } } IsBusy = false; } }
注:二次跳转时,LineItem在执行SpecialInstructions = App.SpecialInstructions;前始终为null
原因分析
- Shell页面缓存机制:MAUI Shell默认会缓存页面实例,首次跳转创建的
SpecialInstructionsViewModel在返回后不会被销毁,二次跳转时复用该实例,但QueryProperty的赋值时机晚于Instructions_Get的执行,导致加载逻辑触发时LineItem尚未被赋值。 - 加载逻辑触发时机错误:如果
Instructions_Get是在页面初始化时自动执行(如绑定到页面Loaded事件),二次进入时缓存的ViewModel中LineItem未被重新赋值,直接执行加载就会出现null。 - 对象序列化问题:
LineItem类若不具备可序列化特性,Shell导航二次传递对象时可能失败,导致LineItem为null。
解决办法
1. 禁用页面缓存,强制创建新实例
修改导航代码,添加清除缓存参数:
await Shell.Current.GoToAsync(nameof(SpecialInstructions), true, new Dictionary<string, object> { {"LineItem", LineItem }, {"clearCache", true} });
或者在路由注册时配置禁用缓存:
Routing.RegisterRoute(nameof(SpecialInstructions), typeof(SpecialInstructions), new Dictionary<string, object>{{nameof(NavigationCacheMode), NavigationCacheMode.Disabled}});
2. 监听LineItem变更再执行加载
利用ObservableProperty的变更回调,确保只有当LineItem被正确赋值后才触发加载逻辑:
[ObservableProperty] LineItem lineItem; // 自动生成的属性变更回调(需确保启用CommunityToolkit.Mvvm的源生成) partial void OnLineItemChanged(LineItem value) { if (value != null) { _ = Instructions_Get(); } }
同时移除页面初始化时自动调用Instructions_Get的逻辑,避免提前执行加载。
3. 手动清理导航栈中的缓存页面
在主页面的返回逻辑中,移除已缓存的SpecialInstructions页面:
var targetPage = Shell.Current.Navigation.NavigationStack.FirstOrDefault(p => p.GetType() == typeof(SpecialInstructions)); if (targetPage != null) { Shell.Current.Navigation.RemovePage(targetPage); }
4. 确保LineItem可序列化
为LineItem类添加可序列化标记,并保证所有属性为公共可读写:
[Serializable] public class LineItem { public string StockNumber { get; set; } // 其他属性... }
内容的提问来源于stack exchange,提问作者jakewags01
相关产品推荐
相关产品推荐

