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

