.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
相关产品推荐
相关产品推荐

