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

MAUI MVVM模式下点击按钮跳转页面失败的问题

在MAUI MVVM模式下实现跨ViewModel页面导航的正确方式

问题根源

你当前的实现有两个核心问题:

  1. 手动实例化页面导致依赖注入失效:WifiPageView的构造函数需要传入已注册到DI容器的WifiPageViewModel,直接用new WifiPageView()无法自动获取ViewModel实例,会触发参数缺失的异常。
  2. ViewModel直接依赖导航违反MVVM原则:ViewModel中直接调用Navigation会导致视图逻辑与业务逻辑耦合,不利于单元测试和代码维护。

解决方案

下面提供两种主流的实现方式,按需选择:

方式一:基于导航服务+DI容器的传统导航

1. 定义导航服务接口

创建抽象的导航服务,解耦ViewModel与具体导航实现:

public interface INavigationService
{
    Task PushAsync(Page page);
    Task PopAsync();
    // 可根据需求添加其他导航方法(如模态导航)
}

2. 实现导航服务

基于MAUI的NavigationPage实现导航逻辑:

public class NavigationService : INavigationService
{
    private readonly IServiceProvider _serviceProvider;

    public NavigationService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task PushAsync(Page page)
    {
        var mainNavigation = Application.Current.MainPage as NavigationPage;
        if (mainNavigation != null)
        {
            await mainNavigation.Navigation.PushAsync(page);
        }
    }

    public async Task PopAsync()
    {
        var mainNavigation = Application.Current.MainPage as NavigationPage;
        if (mainNavigation != null)
        {
            await mainNavigation.Navigation.PopAsync();
        }
    }
}

3. 修正DI容器注册

将视图和ViewModel分开注册,同时注册导航服务:

// 注册视图与ViewModel(根据业务需求选择Singleton/Transient)
builder.Services.AddSingleton<SettingsPageView>();
builder.Services.AddSingleton<SettingsPageViewModel>();
builder.Services.AddSingleton<WifiPageView>();
builder.Services.AddSingleton<WifiPageViewModel>();

// 注册导航服务
builder.Services.AddSingleton<INavigationService, NavigationService>();

4. 修改SettingsPageViewModel的导航逻辑

通过DI注入导航服务和服务提供者,从容器中获取页面实例并导航:

public partial class SettingsPageViewModel : ObservableObject
{
    private readonly INavigationService _navigationService;
    private readonly IServiceProvider _serviceProvider;

    public SettingsPageViewModel(INavigationService navigationService, IServiceProvider serviceProvider)
    {
        _navigationService = navigationService;
        _serviceProvider = serviceProvider;
    }

    [RelayCommand]
    public async Task ClickedWifiMenu()
    {
        // 从DI容器获取已注入ViewModel的WifiPageView实例
        var wifiPage = _serviceProvider.GetRequiredService<WifiPageView>();
        await _navigationService.PushAsync(wifiPage);
    }
}

方式二:基于MAUI Shell的路由导航(推荐)

如果你的项目使用MAUI Shell,这种方式更简洁,无需直接操作页面实例:

1. 注册Shell路由

在AppShell.xaml中添加页面路由:

<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:local="clr-namespace:YourAppNamespace"
       x:Class="YourAppNamespace.AppShell">

    <!-- 注册WifiPage路由 -->
    <ShellContent Route="WifiPage" ContentTemplate="{DataTemplate local:WifiPageView}" />

</Shell>

2. 扩展导航服务支持路由

修改导航服务接口和实现:

public interface INavigationService
{
    Task GoToAsync(string route);
    Task GoBackAsync();
}

public class NavigationService : INavigationService
{
    public async Task GoToAsync(string route)
    {
        await Shell.Current.GoToAsync(route);
    }

    public async Task GoBackAsync()
    {
        await Shell.Current.GoToAsync("..");
    }
}

3. 修改SettingsPageViewModel的导航逻辑

直接通过路由导航,无需依赖具体页面类型:

public partial class SettingsPageViewModel : ObservableObject
{
    private readonly INavigationService _navigationService;

    public SettingsPageViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;
    }

    [RelayCommand]
    public async Task ClickedWifiMenu()
    {
        // 通过路由跳转到WifiPage
        await _navigationService.GoToAsync("//WifiPage");
    }
}

关键注意事项

  • 确保MAUI应用的主页面是NavigationPage(方式一)或AppShell(方式二),否则导航逻辑无法正常工作。
  • 选择合适的DI生命周期(Singleton/Transient/Scoped):如果页面需要每次打开都是新实例,使用AddTransient而非AddSingleton。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 05:40:40