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

MAUI中Shell.Current.GoToAsync跳转NotesApiPage触发空引用异常

MAUI登录后跳转NotesApiPage触发空引用异常的排查与解决

问题描述

在MAUI项目中通过ViewModel实现登录状态校验:无认证信息时跳转LoginPage功能正常,但跳转NotesApiPage时触发System.NullReferenceException: 'Object reference not set to an instance of an object.'异常。

相关代码片段

ViewModel代码

using Notas.Helpers;
using Notas.Model;
using Notas.View;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Notas.ViewModel
{
    public partial class LoadingPageViewModel : BaseViewModel
    {
        public LoadingPageViewModel()
        {
            CheckUserLoginDetails();
        }

        private async void CheckUserLoginDetails()
        {
            // retrieve token from internal storage
            var token = await SecureStorage.GetAsync("Token");
            if (string.IsNullOrEmpty(token))
                await GoToLoginPage();
            else
            {
                var jsonToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
                if (jsonToken.ValidTo < DateTime.UtcNow)
                {
                    SecureStorage.Remove("Token");
                    await GoToLoginPage();
                }
                else
                {
                    var role = jsonToken.Claims.FirstOrDefault(j => j.Type.Equals(ClaimTypes.Role))?.Value;
                    App.UserInfo = new UserInfo()
                    {
                        username = jsonToken.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Name))?.Value,
                        role = role
                    };
                    MenuBuilder.BuildMenu();
                    await GoToMainPage();
                }

            }
        }

        private async Task GoToLoginPage()
        {
            await Shell.Current.GoToAsync($"{nameof(LoginPage)}");
        }
        private async Task GoToMainPage()
        {
            await Shell.Current.GoToAsync($"{nameof(NotesApiPage)}"); //GETTING ERROR HERE...
        }
    }
}

NotesApiPage的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"
             x:Class="Notas.View.NotesApiPage"
             xmlns:model="clr-namespace:Notas.Model"
             xmlns:viewmodel="clr-namespace:Notas.ViewModel"
             x:DataType="viewmodel:NotesApiViewModel"
             Title="{Binding Title}">
    <VerticalStackLayout HorizontalOptions="Start" Padding="5" Spacing="10" Margin="5">
        <Label Text="My Notes" FontAttributes="Bold" FontSize="Subtitle"/>
        <RefreshView 
            IsRefreshing="{Binding Isrefreshing}"
            Command="{Binding GetNotesCommand}">
            <CollectionView ItemsSource="{Binding Notes}">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="model:Note">
                        <Grid Padding="3" ColumnSpacing="2" ColumnDefinitions="*,Auto">
                            <Frame Grid.Column="0">
                                <Frame.GestureRecognizers>
                                    <TapGestureRecognizer 
                                    Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:NotesViewModel}}, Path=GetNoteDetailsCommand}"
                                    CommandParameter="{Binding Id}"/>
                                </Frame.GestureRecognizers>
                                <Label Text="{Binding NoteDescription}"/>
                            </Frame>
                            <Button Grid.Column="1" Text="Delete"
                                Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:NotesViewModel}}, Path=DeleteNoteCommand}"
                                CommandParameter="{Binding Id}"/>
                        </Grid>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </RefreshView>
    </VerticalStackLayout>
</ContentPage>

AppShell代码

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="Notas.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:Notas.View"
    Shell.FlyoutBehavior="Flyout">
    
    <ShellContent
        Title="Home" Icon="icons_home.png"
        ContentTemplate="{DataTemplate local:LoadingPage}"
        Route="LoadingPage" />
  
</Shell>

AppShell.xaml.cs代码

using Notas.View;

namespace Notas;

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        Routing.RegisterRoute(nameof(NotesApiPage), typeof(NotesApiPage));
        Routing.RegisterRoute(nameof(NoteDetailPage), typeof(NoteDetailPage));
        Routing.RegisterRoute(nameof(LoginPage), typeof(LoginPage));
        Routing.RegisterRoute(nameof(MainPage), typeof(MainPage));
    }
}

原因分析

  1. Binding上下文匹配错误:NotesApiPage的x:DataType指定为NotesApiViewModel,但DataTemplate内部的TapGestureRecognizer和Button绑定的AncestorType是NotesViewModel,找不到对应的ViewModel实例,导致绑定的Command为null,触发空引用异常。
  2. ViewModel初始化缺失:如果NotesApiPage未正确设置BindingContext(比如未在后台代码或XAML中初始化NotesApiViewModel实例),页面绑定的Title、Isrefreshing等属性会因为找不到ViewModel而触发空引用。
  3. 异步方法调用时机问题:LoadingPageViewModel构造函数中直接调用async void方法CheckUserLoginDetails(),可能在Shell尚未完全初始化完成时执行导航操作,极端情况下会导致Shell.Current为null。

解决方法

  1. 修正DataTemplate绑定源:将NotesApiPage XAML中DataTemplate内的绑定AncestorType改为viewmodel:NotesApiViewModel,与页面的ViewModel类型保持一致:
<TapGestureRecognizer 
    Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:NotesApiViewModel}}, Path=GetNoteDetailsCommand}"
    CommandParameter="{Binding Id}"/>
<Button Grid.Column="1" Text="Delete"
    Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:NotesApiViewModel}}, Path=DeleteNoteCommand}"
    CommandParameter="{Binding Id}"/>
  1. 确保NotesApiPage的BindingContext初始化:在NotesApiPage的后台代码构造函数中添加ViewModel实例初始化:
public NotesApiPage()
{
    InitializeComponent();
    BindingContext = new NotesApiViewModel();
}
  1. 调整异步方法调用时机:将LoadingPageViewModel中的CheckUserLoginDetails()调用从构造函数移至页面的OnAppearing方法,避免Shell未初始化完成时执行导航:
  • 修改LoadingPage.xaml.cs:
protected override async void OnAppearing()
{
    base.OnAppearing();
    if (BindingContext is LoadingPageViewModel vm)
    {
        await vm.CheckUserLoginDetails();
    }
}
  • 同时将LoadingPageViewModel中的CheckUserLoginDetails()改为返回Task:
private async Task CheckUserLoginDetails()
{
    // 原有逻辑不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 20:50:29