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

在ContentView中调用Navigation.PushAsync无效?求正确实现方式

问题描述

我给一个ContentView添加了TapGestureRecognizer,期望点击时跳转页面,代码如下:

var tapRecognizer = new TapGestureRecognizer();
tapRecognizer.Tapped += async (_, _) => await Navigation.PushAsync(new GalleryPage());
GestureRecognizers.Add(tapRecognizer);

但实际没有达到预期效果。我的应用采用Shell架构,Shell风格导航正常,在Page中调用Navigation.PushAsync也有效。请问是不是无法在ContentView中调用Navigation.PushAsync?正确的实现方式是什么?

附相关代码:

ContentView代码

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="....GalleryThumbnailView"
             BindingContext="{Binding Source={RelativeSource Self}, Path=Thumbnail}">
    <Frame CornerRadius="6">
        <HorizontalStackLayout>
            <Image Aspect="AspectFit" HeightRequest="256" WidthRequest="256" Source="{Binding CoverUrl}"/>
            <VerticalStackLayout>
                <Label Text="{Binding Category}" />
                <Label Text="{Binding Title}"/>
                <Label Text="{Binding PublishedTime}"/>
            </VerticalStackLayout>
        </HorizontalStackLayout>
    </Frame>
</ContentView>

承载视图的页面代码

<?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"
             xmlns:views="clr-namespace:...Views"
             xmlns:models="clr-namespace:...Models"
             x:Class="...Pages.HomePage"
             Title="..." Loaded="OnLoaded">
    <ScrollView>
        <VerticalStackLayout>
            <ListView x:Name="list" ItemsSource="{Binding Latest}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <views:GalleryThumbnailView Thumbnail="{Binding Source={x:RelativeSource AncestorType={Type models:GalleryThumbnail}}}" />
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

目标跳转页面代码

<?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="...Pages.GalleryPage"
             Title="GalleryPage">
    <VerticalStackLayout>
        <Label 
            Text="{Binding GalleryThumbnail.Title}"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />
        <HorizontalStackLayout>
            <Image HeightRequest="256" WidthRequest="256" Source="{Binding GalleryThumbnail.CoverUrl}"/>
            <VerticalStackLayout>
            </VerticalStackLayout>
        </HorizontalStackLayout>
    </VerticalStackLayout>
</ContentPage>
原因分析
  • ContentView属于基础视图组件,本身没有内置的Navigation属性,直接调用Navigation.PushAsync会因Navigation对象为null而无法执行导航逻辑。
  • 只有继承自Page的类(如ContentPage)才拥有内置的Navigation实例,可直接调用导航方法。
正确实现方式

方法一:给ContentView添加Navigation绑定属性

通过给ContentView定义INavigation类型的绑定属性,从宿主页面传入有效的Navigation实例:

1. 修改GalleryThumbnailView后台代码

public partial class GalleryThumbnailView : ContentView
{
    // 定义Navigation绑定属性
    public static readonly BindableProperty NavigationProperty =
        BindableProperty.Create(nameof(Navigation), typeof(INavigation), typeof(GalleryThumbnailView));

    public INavigation Navigation
    {
        get => (INavigation)GetValue(NavigationProperty);
        set => SetValue(NavigationProperty, value);
    }

    // 原有的Thumbnail属性
    public static readonly BindableProperty ThumbnailProperty =
        BindableProperty.Create(nameof(Thumbnail), typeof(GalleryThumbnail), typeof(GalleryThumbnailView));

    public GalleryThumbnail Thumbnail
    {
        get => (GalleryThumbnail)GetValue(ThumbnailProperty);
        set => SetValue(ThumbnailProperty, value);
    }

    public GalleryThumbnailView()
    {
        InitializeComponent();
        var tapRecognizer = new TapGestureRecognizer();
        tapRecognizer.Tapped += async (_, _) => 
        {
            if (Navigation != null)
                await Navigation.PushAsync(new GalleryPage());
        };
        GestureRecognizers.Add(tapRecognizer);
    }
}

2. 修改宿主HomePage的XAML,传递Navigation

<DataTemplate>
    <ViewCell>
        <views:GalleryThumbnailView 
            Thumbnail="{Binding}"
            Navigation="{Binding Source={RelativeSource AncestorType={Type ContentPage}}, Path=Navigation}" />
    </ViewCell>
</DataTemplate>

方法二:利用ListView的ItemTapped事件(更简洁)

由于ContentView嵌套在ListView的ViewCell中,可直接在页面层处理ListView的ItemTapped事件,无需给ContentView单独添加手势:

1. 修改HomePage的XAML,添加ItemTapped事件

<ListView x:Name="list" ItemsSource="{Binding Latest}" ItemTapped="OnItemTapped">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <views:GalleryThumbnailView Thumbnail="{Binding}" />
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

2. 在HomePage后台实现事件处理

private async void OnItemTapped(object sender, ItemTappedEventArgs e)
{
    if (e.Item is GalleryThumbnail thumbnail)
    {
        await Navigation.PushAsync(new GalleryPage { BindingContext = new { GalleryThumbnail = thumbnail } });
    }
    // 取消选中状态
    ((ListView)sender).SelectedItem = null;
}

方法三:使用WeakReferenceMessenger(解耦方式)

通过消息传递实现组件间解耦,ContentView发送导航消息,宿主页面订阅并执行导航:

1. 定义消息类

public class NavigateToGalleryMessage
{
    public GalleryThumbnail Thumbnail { get; set; }
}

2. 在ContentView中发送消息

public GalleryThumbnailView()
{
    InitializeComponent();
    var tapRecognizer = new TapGestureRecognizer();
    tapRecognizer.Tapped += (_, _) => 
    {
        WeakReferenceMessenger.Default.Send(new NavigateToGalleryMessage { Thumbnail = Thumbnail });
    };
    GestureRecognizers.Add(tapRecognizer);
}

3. 在HomePage中订阅消息

protected override void OnAppearing()
{
    base.OnAppearing();
    WeakReferenceMessenger.Default.Register<NavigateToGalleryMessage>(this, async (r, m) =>
    {
        await Navigation.PushAsync(new GalleryPage { BindingContext = new { GalleryThumbnail = m.Thumbnail } });
    });
}

protected override void OnDisappearing()
{
    base.OnDisappearing();
    WeakReferenceMessenger.Default.Unregister<NavigateToGalleryMessage>(this);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:47:49