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

如何在Uno Platform的TabViewItem内使用NavigateViewAsync导航页面?

问题:Uno Platform中NavigationView点击项无法在TabViewItem内导航,而是覆盖整个窗口

我有一个带动态树形项的NavigationView,点击项时需要打开新标签页,但调用Navigator().NavigateViewAsync<...>()时,会覆盖整个窗口内容,而非在目标TabViewItem的内容区域内导航,且多次触发ArgumentNullException,多数错误指向Navigator的IDispatcher实例为空。


相关XAML代码

<Grid utu:SafeArea.Insets="All">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <utu:NavigationBar Grid.Row="0" Content="{Binding Title}" />
    <TabView uen:Region.Name="tabview"  uen:Region.Attached="True" Grid.Column="1" TabCloseRequested="TV_TabClose" x:Name="projectTabs" IsAddTabButtonVisible="False" />
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <NavigationView Grid.Column="0" x:Name="sidebar" IsBackButtonVisible="Collapsed" IsSettingsVisible="False">
            <NavigationView.PaneCustomContent>
                <TreeView ItemInvoked="TreeItemClicked" CanDragItems="False" CanReorderItems="False" x:Name="ProjectTree" ItemTemplateSelector="{StaticResource TplSelector}" ItemsSource="{Binding wi}"/>
            </NavigationView.PaneCustomContent>
        </NavigationView>
    </Grid>
</Grid>

最初尝试的代码

public async void NavToProject(WorkspaceProject? p)
{
    if (p != null)
    {
        var newTab = new TabViewItem();
        newTab.Content = new Frame();
        await this.NavigateViewAsync<Project>(this, data: p.Project);
    }
}

自定义Navigator/FrameNavigator的尝试

public class MainModel {
    ILogger<Navigator> log1 { get; set; }
    ILogger<FrameNavigator> log2 { get; set; }

    IDispatcher dispatcher { get; set; }
    public MainModel(
        IStringLocalizer localizer,
        INavigator navigator, IConfiguration config, 
        IRouteResolver routeresolver, 
        IDispatcher idispatcher, ILogger<Navigator> log1, ILogger<FrameNavigator> log2)
    {
        viewmapresolver = routeresolver;
        this.log1 = log1;
        this.log2 = log2;
        this.dispatcher = idispatcher;
        this.config = config;
        config.GetSection("RecentProjects").Bind(appopts);
        _navigator = navigator;
    }
    public async Task<TabViewItem?> NavToProject(WorkspaceProject? p)
    {
        if (p != null)
        {
            var newTab = new TabViewItem();
            newTab.Content = new Frame();
            var nb = new Navigator(log1, dispatcher, new NavigationRegion(view: newTab.Content as FrameworkElement), views);
            var fn = new FrameNavigator(null, dispatcher, new NavigationRegion(view: newTab.Content as FrameworkElement), views, new RegionControlProvider()
            {
                RegionControl = newTab.Content
            }) ;
            // 此处抛出异常
            await fn.NavigateViewAsync<Project>(fn, data: p.Project);
            // 此处也抛出异常
            await nb.NavigateViewAsync<Project>(nb, data: p.Project);
            return newTab;
        }
        return null;
    }
}

两种尝试均触发Uno内部代码抛出的ArgumentNullException。


当前实现代码

App.cs路由配置

private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
{
    views.Register(
        new ViewMap(ViewModel: typeof(ShellModel)),
        new ViewMap<MainPage, MainModel>(),
        new DataViewMap<ListPage, ListPageModel, G.Project>(),
        new DataViewMap<turquoise.Presentation.Project, ProjectModel, G.Project>(),
        new DataViewMap<TaskTree, TreeModel, G.Section>()
    );

    routes.Register(
        new RouteMap("", View: views.FindByViewModel<ShellModel>(),
            Nested: new RouteMap[]
            {
                new RouteMap("Main", View: views.FindByViewModel<MainModel>()),
                new RouteMap("/Project/List", View: views.FindByViewModel<ListPageModel>()),
                new RouteMap("/Project", View: views.FindByViewModel<ProjectModel>()),
            }
        )
    );
}

MainModel.cs代码

public partial class MainModel
{
    private INavigator _navigator;
    private IConfiguration config;
    public static G.Project CurrentProject;

    public IDispatcher dispatcher { get; set; }
    public IRouteResolver viewmapresolver { get; set; }

    public MainModel(
        INavigator navigator, IConfiguration config,
        IRouteResolver routeresolver,
        IDispatcher idispatcher)
    {
        dispatcher = idispatcher;
        viewmapresolver = routeresolver;
        this.config = config;
        config.GetSection("RecentProjects").Bind(appopts);
        _navigator = navigator;
    }

    public async Task<TabViewItem?> NavToProject(WorkspaceProject? p)
    {
        if (p != null)
        {
            var newTab = new TabViewItem();
            newTab.Header = p.Project.Name;

            var cc = new Frame();

            Region.SetParent(cc, newTab);
            Region.SetName(cc, "/Project");
            Region.SetAttached(cc, true);
            cc.SetValue(Region.ParentProperty, this);
            cc.SetValue(Region.NameProperty, "./Project");
            cc.SetValue(Region.AttachedProperty, true);

            var nnr = new NavigationRegion(cc, Ioc.Default.GetRequiredService<IServiceProvider>());

            var nav = new FrameNavigator(
                Ioc.Default.GetRequiredService<ILogger<FrameNavigator>>(),
                dispatcher,
                nnr,
                Ioc.Default.GetRequiredService<IRouteResolver>(),
                new RegionControlProvider()
                {
                    RegionControl = cc
                }
            );
            cc.SetNavigatorInstance(nav);
            cc.SetValue(Region.NavigatorProperty, "/Project");
            nnr.ReassignParent();

            NavigationResponse? resp = await nav?.NavigateViewAsync<Project>(cc, data: p.Project);
            
            newTab.Content = cc;
            return newTab;
        }
        return null;
    }
}

目前仍多次触发ArgumentNullException,多数错误指向Navigator的IDispatcher实例为空。


解决方案

核心问题分析

  1. 手动实例化FrameNavigator时,部分依赖项未正确注入,尤其是IDispatcher可能未关联到UI线程;
  2. 未将Tab内的Frame注册为独立导航区域,导致导航操作仍作用于全局根区域;
  3. 原XAML中TabView的Grid列配置错误,导致布局异常。

修正后的实现步骤

1. 简化Tab创建与区域注册

无需手动实例化Navigator,利用Uno的区域导航机制为每个Tab的Frame分配独立区域:

public async Task<TabViewItem?> NavToProject(WorkspaceProject? p)
{
    if (p == null) return null;

    var newTab = new TabViewItem
    {
        Header = p.Project.Name,
        Content = new Frame()
    };

    // 为Frame设置唯一导航区域标识
    var frame = newTab.Content as Frame;
    Region.SetAttached(frame, true);
    Region.SetName(frame, $"ProjectTab_{p.Project.Id}");

    // 指定导航到该Frame的独立区域
    var navigationResult = await _navigator.NavigateViewAsync<Project>(
        this,
        data: p.Project,
        region: Region.GetName(frame)
    );

    return navigationResult.Success ? newTab : null;
}

2. 确保Dispatcher正确注入

直接使用DI容器注入的UI线程Dispatcher,无需手动传递给新Navigator:

public MainModel(
    INavigator navigator, 
    IConfiguration config,
    IRouteResolver routeresolver,
    IDispatcher dispatcher)
{
    this.dispatcher = dispatcher;
    this.viewmapresolver = routeresolver;
    this.config = config;
    config.GetSection("RecentProjects").Bind(appopts);
    this._navigator = navigator;
}

3. 修正XAML布局

调整TabView的Grid列配置,确保其处于正确的布局位置:

<Grid Grid.Row="1">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="3*"/>
    </Grid.ColumnDefinitions>
    <NavigationView Grid.Column="0" x:Name="sidebar" IsBackButtonVisible="Collapsed" IsSettingsVisible="False">
        <NavigationView.PaneCustomContent>
            <TreeView ItemInvoked="TreeItemClicked" CanDragItems="False" CanReorderItems="False" x:Name="ProjectTree" ItemTemplateSelector="{StaticResource TplSelector}" ItemsSource="{Binding wi}"/>
        </NavigationView.PaneCustomContent>
    </NavigationView>
    <TabView uen:Region.Name="tabview" uen:Region.Attached="True" Grid.Column="1" TabCloseRequested="TV_TabClose" x:Name="projectTabs" IsAddTabButtonVisible="False" />
</Grid>

4. 关键注意事项

  • 每个Tab的Frame必须设置唯一的Region.Name,避免导航冲突;
  • 导航时必须指定region参数,明确目标区域;
  • 不要手动实例化FrameNavigator,依赖DI注入的INavigator即可;
  • 确保所有UI操作在UI线程执行,DI注入的IDispatcher已关联当前线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 19:17:02