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

MAUI Shell:如何通过导航字符串获取页面类型实现ViewModel模态导航

在MAUI Shell中实现ViewModel无UI引用的模态导航

你没法直接在ViewModel中通过导航字符串获取对应的页面类型——因为Shell的路由映射是在UI项目中注册的,ViewModel项目没有引用UI,无法访问这些类型信息。不过可以通过以下两种方案实现需求,同时保持VM与UI的解耦:

方案1:通过路由参数标记模态导航

在ViewModel调用GoToAsync时,给路由字符串添加一个modal查询参数,然后在UI项目中拦截导航事件,根据参数切换为模态推送:

ViewModel代码

// 添加?modal=true标记
await Shell.Current.GoToAsync("//person/edit/phonenumber?modal=true", 
    new Dictionary<string, object>{ { "phoneNumber", this }});

UI项目(AppShell)处理逻辑

在AppShell的构造函数中注册Navigating事件,拦截导航并判断参数:

public AppShell()
{
    InitializeComponent();
    Shell.Current.Navigating += OnNavigating;
}

private async void OnNavigating(object sender, ShellNavigatingEventArgs e)
{
    // 解析路由中的modal参数
    if (e.Target.Location.QueryParsed.TryGetValue("modal", out var modalValue) 
        && bool.TryParse(modalValue, out var isModal) 
        && isModal)
    {
        // 取消默认导航
        e.Cancel();
        // 移除modal参数,避免传递到页面
        var cleanRoute = e.Target.Location.ToString().Replace("?modal=true", "");
        // 获取页面类型并推送模态
        if (Shell.Current.CurrentItem.RouteTable.TryGetRoute(cleanRoute, out var routeMatch))
        {
            var page = (Page)Activator.CreateInstance(routeMatch.PageType);
            // 传递原参数
            if (e.Parameter is IDictionary<string, object> parameters && page is IQueryAttributable queryPage)
            {
                queryPage.ApplyQueryAttributes(new QueryPropertyDictionary(parameters));
            }
            await Shell.Current.Navigation.PushModalAsync(page);
        }
    }
}

方案2:抽象导航服务(推荐)

在共享核心层定义导航接口,ViewModel依赖该接口,UI项目实现接口并处理模态导航逻辑,彻底解耦VM与UI:

核心层(共享项目)定义接口

public interface INavigationService
{
    Task NavigateToAsync(string route, IDictionary<string, object> parameters);
    Task NavigateToModalAsync(string route, IDictionary<string, object> parameters);
}

ViewModel中使用接口

private readonly INavigationService _navigationService;

// 通过依赖注入获取导航服务
public PhoneNumberViewModel(INavigationService navigationService)
{
    _navigationService = navigationService;
}

// 调用模态导航方法
await _navigationService.NavigateToModalAsync("//person/edit/phonenumber", 
    new Dictionary<string, object>{ { "phoneNumber", this }});

UI项目实现导航服务

public class MauiNavigationService : INavigationService
{
    public async Task NavigateToAsync(string route, IDictionary<string, object> parameters)
    {
        await Shell.Current.GoToAsync(route, parameters);
    }

    public async Task NavigateToModalAsync(string route, IDictionary<string, object> parameters)
    {
        // 从Shell路由表中获取对应页面类型
        if (Shell.Current.CurrentItem.RouteTable.TryGetRoute(route, out var routeMatch) 
            && routeMatch.PageType != null)
        {
            var page = (Page)Activator.CreateInstance(routeMatch.PageType);
            // 传递参数给页面(如果页面实现了IQueryAttributable)
            if (parameters != null && page is IQueryAttributable queryPage)
            {
                queryPage.ApplyQueryAttributes(new QueryPropertyDictionary(parameters));
            }
            await Shell.Current.Navigation.PushModalAsync(page);
        }
        else
        {
            // 路由不存在时,回退到普通导航
            await NavigateToAsync(route, parameters);
        }
    }
}

注册服务

在UI项目的MauiProgram.cs中注册导航服务:

builder.Services.AddSingleton<INavigationService, MauiNavigationService>();

两种方案都能实现你的需求,方案2更符合依赖倒置原则,代码扩展性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:36:52