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

使用MAUI Shell导航传递对象时触发InvalidCastException异常

问题分析与解决方案

问题根源

Shell导航的GoToAsync(IDictionary<string, object>)重载并非设计用于传递复杂自定义对象,它底层会尝试将字典中的所有值转换为可序列化的URL查询参数类型(如字符串、数值等)。你的Training类未实现IConvertible接口,因此触发InvalidCastException。


解决方案

方案1:使用全局对象缓存传递复杂对象(推荐)

实现一个简单的参数管理器,通过唯一ID暂存对象,导航时传递ID,目标页面通过ID取回对象:

// 全局参数管理器
public static class NavigationParameterManager
{
    private static readonly Dictionary<string, object> _parameters = new();
    private static readonly Random _random = new();

    public static string StoreParameter(object param)
    {
        var key = _random.Next(10000, 99999).ToString();
        _parameters[key] = param;
        return key;
    }

    public static T? RetrieveParameter<T>(string key)
    {
        if (_parameters.TryGetValue(key, out var value) && value is T param)
        {
            _parameters.Remove(key); // 用完即删,避免内存泄漏
            return param;
        }
        return default;
    }
}

修改导航代码:

[RelayCommand]
async Task OpenEntry(Training training)
{
    var paramKey = NavigationParameterManager.StoreParameter(training);
    await Shell.Current.GoToAsync($"{nameof(DetailPage)}?ParamKey={paramKey}");
}

修改目标ViewModel:

[QueryProperty(nameof(ParamKey), "ParamKey")]
public partial class DetailViewModel : ObservableObject
{
    [ObservableProperty]
    string? paramKey;

    [ObservableProperty]
    Training? detailedTraining;

    partial void OnParamKeyChanged(string? oldValue, string? newValue)
    {
        if (!string.IsNullOrEmpty(newValue))
        {
            DetailedTraining = NavigationParameterManager.RetrieveParameter<Training>(newValue);
        }
    }
}

方案2:序列化为JSON字符串传递

将Training对象序列化为JSON字符串,作为普通字符串参数传递,目标页面反序列化:

导航代码:

[RelayCommand]
async Task OpenEntry(Training training)
{
    var json = JsonSerializer.Serialize(training);
    // 注意对URL进行编码
    var encodedJson = Uri.EscapeDataString(json);
    await Shell.Current.GoToAsync($"{nameof(DetailPage)}?TrainingJson={encodedJson}");
}

目标ViewModel:

[QueryProperty(nameof(TrainingJson), "TrainingJson")]
public partial class DetailViewModel : ObservableObject
{
    [ObservableProperty]
    string? trainingJson;

    [ObservableProperty]
    Training? detailedTraining;

    partial void OnTrainingJsonChanged(string? oldValue, string? newValue)
    {
        if (!string.IsNullOrEmpty(newValue))
        {
            var decodedJson = Uri.UnescapeDataString(newValue);
            DetailedTraining = JsonSerializer.Deserialize<Training>(decodedJson);
        }
    }
}

方案3:直接获取目标ViewModel赋值(适用于简单场景)

如果目标页面已注册到Shell,可以直接获取其ViewModel并赋值,再导航:

[RelayCommand]
async Task OpenEntry(Training training)
{
    // 获取目标页面实例
    var detailPage = (DetailPage)Shell.Current.CurrentPage.Navigation.NavigationStack.FirstOrDefault(p => p is DetailPage) 
                     ?? new DetailPage();
    // 直接赋值ViewModel属性
    ((DetailViewModel)detailPage.BindingContext).DetailedTraining = training;
    await Shell.Current.GoToAsync(nameof(DetailPage));
}

注意事项

  • 避免直接用IDictionary<string, object>传递复杂对象,Shell的该重载仅支持基础数据类型。
  • 使用全局缓存时,记得在取回对象后移除缓存,防止内存泄漏。
  • JSON序列化方案需要确保Training类的属性支持序列化(无循环引用、可访问的公共属性等)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:40:06