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

.NET MAUI Shell中OnShellNavigating触发时BindingContext为空的解决求助

.NET MAUI Shell标签页切换时ViewModel BindingContext为空的解决方法

问题背景

开发.NET MAUI Shell日志应用时,侧边栏Flyout包含多个标签页,需要根据选中的标签页(如“全部条目”“过去24小时条目”)过滤日志集合。尝试通过OnShellNavigating事件触发MainPageViewModel的TabSelectedCommand,但事件触发时BindingContext始终为空,导致命令无法执行。

相关代码

AppShell.xaml

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:sys="clr-namespace:System;assembly=netstandard"
    xmlns:views="clr-namespace:Logbook.Views"
    xmlns:viewModel="clr-namespace:Logbook.ViewModels"
    xmlns:behaviors="clr-namespace:Logbook.Behaviours"
    x:Class="Logbook.AppShell"
    x:Name="shell">

    <FlyoutItem Title="Logbook" >
        <FlyoutItem.Icon>
            <FontImageSource FontFamily="FASolid"
                             Color="{AppThemeBinding 
                                    Dark ={StaticResource White}, 
                                    Light={StaticResource Gray500}}"
                             Glyph="{StaticResource fa_Book}"/>
        </FlyoutItem.Icon>
        <Tab Title="Past 24 Hours">
            <Tab.Icon>
                <FontImageSource FontFamily="FASolid"
                                 Color="{AppThemeBinding 
                                        Dark ={StaticResource White}, 
                                        Light={StaticResource Gray100}}"
                                 Glyph="{StaticResource fa_Clock}"/>
            </Tab.Icon>
            <ShellContent ContentTemplate="{DataTemplate views:MainPage}"
                          Route="MainPage\Past24Hours">
            </ShellContent>
        </Tab>
        <Tab Title="All Entries">
            <Tab.Icon>
                <FontImageSource FontFamily="FASolid"
                                 Color="{AppThemeBinding 
                                        Dark ={StaticResource White}, 
                                        Light={StaticResource Gray100}}"
                                 Glyph="{StaticResource fa_BookOpen}"/>
            </Tab.Icon>
            <ShellContent ContentTemplate="{DataTemplate views:MainPage}" 
                          Route="MainPage">
            </ShellContent>
        </Tab>
    </FlyoutItem>
</Shell>

MainPageViewModel.cs

public partial class MainPageViewModel : BaseViewModel
{
    // ...

    [RelayCommand]
    async Task TabSelectedAsync(string route)
    {
        // 根据路由过滤日志条目
        // ...
    }

    // ...
}

TabSelectedBehavior.cs

namespace Logbook.Behaviours;
public class TabSelectedBehavior : Behavior<Shell>
{
    protected override void OnAttachedTo(Shell shell)
    {
        base.OnAttachedTo(shell);
        shell.Navigating += OnShellNavigating;
    }

    protected override void OnDetachingFrom(Shell shell)
    {
        shell.Navigating -= OnShellNavigating;
        base.OnDetachingFrom(shell);
    }

    private void OnShellNavigating(object sender, ShellNavigatingEventArgs e)
    {
        if (e.Target?.Location.OriginalString != null)
        {
            var route = e.Target.Location.OriginalString;
            if (BindingContext is MainPageViewModel viewModel && viewModel.TabSelectedCommand.CanExecute(route))
            {
                viewModel.TabSelectedCommand.Execute(route);
            }
        }
    }
}

解决方案

1. 切换到ShellNavigated事件触发命令

OnShellNavigating在导航开始前触发,此时页面的BindingContext尚未完成初始化。改用ShellNavigated事件(导航完成后触发),确保页面和ViewModel已绑定完成:

namespace Logbook.Behaviours;
public class TabSelectedBehavior : Behavior<Shell>
{
    protected override void OnAttachedTo(Shell shell)
    {
        base.OnAttachedTo(shell);
        // 替换为Navigated事件
        shell.Navigated += OnShellNavigated;
    }

    protected override void OnDetachingFrom(Shell shell)
    {
        shell.Navigated -= OnShellNavigated;
        base.OnDetachingFrom(shell);
    }

    private void OnShellNavigated(object sender, ShellNavigatedEventArgs e)
    {
        if (e.Current?.Location.OriginalString != null)
        {
            var route = e.Current.Location.OriginalString;
            // 获取当前活跃页面的BindingContext
            var currentPage = Shell.Current.CurrentPage;
            if (currentPage?.BindingContext is MainPageViewModel viewModel && viewModel.TabSelectedCommand.CanExecute(route))
            {
                viewModel.TabSelectedCommand.Execute(route);
            }
        }
    }
}

2. 确保MainPage的BindingContext正确设置

检查MainPage.xaml是否正确绑定ViewModel:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewModel="clr-namespace:Logbook.ViewModels"
             x:Class="Logbook.Views.MainPage"
             BindingContext="{StaticResource MainPageViewModel}">
    <!-- 页面内容 -->
</ContentPage>

或者在代码后台初始化:

public MainPage()
{
    InitializeComponent();
    BindingContext = new MainPageViewModel();
}

3. 避免重复创建ViewModel(优化)

使用ContentTemplate时,每次切换标签页可能会生成新的MainPage实例。可以直接在ShellContent上绑定ViewModel,确保实例稳定:

<ShellContent ContentTemplate="{DataTemplate views:MainPage}"
              Route="MainPage">
    <ShellContent.BindingContext>
        <viewModel:MainPageViewModel />
    </ShellContent.BindingContext>
</ShellContent>

4. 通过Shell层级获取ViewModel

在ShellNavigated事件中,通过Shell的层级结构直接找到选中标签页对应的ViewModel:

private void OnShellNavigated(object sender, ShellNavigatedEventArgs e)
{
    if (sender is Shell shell && shell.CurrentItem is FlyoutItem flyoutItem)
    {
        var selectedTab = flyoutItem.CurrentItem as Tab;
        if (selectedTab?.CurrentItem is ShellContent shellContent)
        {
            var page = shellContent.Content as MainPage;
            if (page?.BindingContext is MainPageViewModel viewModel)
            {
                var route = e.Current.Location.OriginalString;
                viewModel.TabSelectedCommand.Execute(route);
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 20:24:54