首页 > 其他分享 >MAUI新生6.5-导航路由Navigation

MAUI新生6.5-导航路由Navigation

时间:2023-01-01 00:00:36浏览次数:49  
标签:Shell Navigation args Current 6.5 MAUI 导航 await 路由

MAUI的Shell导航框架,也是以路由方式进行导航,并提供了两套导航方式,一是如前面章节所述的视觉层次结构,会自动建立导航路由,可以进行不同层次页面的导航切换;二是为页面手动注册路由,并执行代码导航。

 

一、注册路由

1、视觉层次结构页面的路由注册

在视觉层次结构中(Shell>FlyoutItem或TabBar>Tab>ShellContent),框架自动建立导航路由,可以直接进行导航切换。如果要通过代码导航到视觉层次页面,可以在定义视觉层次结构时,显示的定义路由,如:

<!--通过Route属性,显示注册路由-->
<Shell
    ......>
    <FlyoutItem Route="animals">
        <Tab Title="Domestic" Route="domestic">
            <ShellContent
                Title="Cats"
                ContentTemplate="{DataTemplate view:Cats}"
                Route="cats" />
            <ShellContent
                Title="Dogs"
                ContentTemplate="{DataTemplate view:Dogs}"
                Route="dogs" />
        </Tab>
        <ShellContent
            Title="Monkeys"
            ContentTemplate="{DataTemplate view:Monkeys}"
            Route="monkeys" />
        <ShellContent
            Title="Bears"
            ContentTemplate="{DataTemplate view:Bears}"
            Route="bears" />
        <ShellContent
            Title="Elephants"
            ContentTemplate="{DataTemplate view:Elephants}"
            Route="elephants" />
    </FlyoutItem>
    <ShellContent
        Title="About"
        ContentTemplate="{DataTemplate view:About}"
        Route="about" />
</Shell>

<!--上例的导航路由结构为,如要导航到dogs,则可以通过路由//animals/domestic/dogs;如要导航到about,则可以通过路由//about-->
animals
    domestic
        cats
        dogs
    monkeys
    bears
    elephants
about

 

2、非视觉层次结构页面的路由注册

MAUI中还有很多页面,没有在视觉层次结构中定义,这些页面需要进行手动注册路由,一般在AppShell后台代码的构造函数中进行。

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        //注册非视觉层次页面路由,所有路由名称需保持唯一,它们是全局的
        //当导航到视觉层次结构中的路由时,并不会创建导航堆栈;但导航到非视觉层次页面路由时,创建导航堆
        Routing.RegisterRoute("dogdetail",typeof(DogDetail));
        Routing.RegisterRoute("monkeydetail", typeof(MonkeyDetail));

        //也可以将路由注册到不同的视觉层次结构上,可以实现跟踪当前路由层次,导航到不同的页面
        //如下例中,都是导航到detail,但在dogs路由层次结构中时,将导航到DogDetail;反之,将导航到MonkeyDetail
        Routing.RegisterRoute("dogs/detail", typeof(DogDetail));
        Routing.RegisterRoute("monkeys/detail", typeof(MonkeyDetail));
    }
}

 

 

 

二、执行导航

1、在视觉层次结构中,可以直接执行导航跳转。注:这些跳转页面,不会进入到导航堆栈中。

 

2、非视觉层次结构的页面,需要通过编程式导航来实现。 

//下例为Monkeys页面,通过按钮点击事件进行导航
public partial class Monkeys : ContentPage
{
    ......private async void Button_Clicked(object sender, EventArgs e)
    {
        //Shell.Curren获得对当前Shell对象的引用,等效于((Shell)App.Current.MainPage)
        //Shell对象包括了GoToAsync导航方法,以及CurrentItem、CurrentPage、CurrentState、BackButtonBehavior等属性
        await Shell.Current.GoToAsync("dogdetail");
        await ((Shell)App.Current.MainPage).GoToAsync("dogdetail");
        await DisplayAlert("显示当前路由", Shell.Current.CurrentState.Location.ToString(), "OK");
    }
}

 

3、绝对路由和相对路由

  • 视觉层次结构的页面,可以通过绝对路径进行导航,如【Shell.Current.GoToAsync("//animals/domestic/dogs")】
  • 非视觉层次结构的页面,可以通过相对路径进行导航,如【Shell.Current.GoToAsync("dogdetail")】
  • 上下文导航。注册非视觉层次结构页面时,将路由注册到结构层次页面上,如Routing.RegisterRoute("monkeys/detail"...)。当在Monkeys页面中,导航到detail时,会匹配monkeys/detail。
  • 向后导航。向后导航,【await Shell.Current.GoToAsync("..")】;向后导航与路由导航结合,【await Shell.Current.GoToAsync("../route")】;可以多次向后导航,【await Shell.Current.GoToAsync("../../route")】
  • 关于路由导航,还有一些概念比较模糊,建议暂时按以上两种方式来导航

 

 

 

三、导航传参

1、传递参数。

1)方法一:查询字符串

//传递字符串:直接通过查询字符串传递参数
//传递一个字符串参数
await Shell.Current.GoToAsync("monkeydetail?name=sun");
//传递多个字符串参数
await Shell.Current.GoToAsync("monkeydetail?name=sun&sex=male");
//也可以使用内插值变量
string name = "sun"
await Shell.Current.GoToAsync($"monkeydetail?name={name}");

2)方法一:字典类型参数

//传递对象:通过GoToAsync方法的第二个参数,IDictionary<string,object>字典类型对象传参
//传递一个对象
var p1 = new Person{Name="zs",Age=18};
var navigationParam = new Dictionary<string, object>
{
    { "person1", p1 }
};
await Shell.Current.GoToAsync($"persondetail", navigationParame);
//传递多个对象
var p1 = new Person{Name="zs",Age=18};
var p2 = new Person{Name="ls",Age=28};
var navigationParam = new Dictionary<string, object>
{
    { "person1", p1 }
    { "person2", p2 }
};
await Shell.Current.GoToAsync($"persondetail", navigationParame);

 

2、接收参数。

1)方法一:通过QueryProperty特性,接收路由参数

//QueryProperty特性
//QueryProperty特性的第一个参数为接收路由参数的属性,第一个参数为路由参数的键名
//通过查询参数传递的路由参数,会自动转为键值对形式
//注意接收路由参数的属性,必须是可观察属性(MVVM),触发PropertyChanged事件
[QueryProperty(nameof(Name), "name")]
[QueryProperty(nameof(Sex), "sex")]
public partial class MonkeyDetail : ContentPage
{
    public MonkeyDetail()
    {
        InitializeComponent();
        BindingContext = this; //将BindingContext设置为当前对象
    }

    //定义可观察属性Name来接收键名为name的路由参数
    private string name;
    public string Name
    {
        get { return name; }
        set 
        {
            name = value;
            OnPropertyChanged();
        }
    }
    //定义可观察属性Sex来接收键名为sex的路由参数
    private string sex;
    public string Sex
    {
        get { return sex; }
        set
        {
            sex = value;
            OnPropertyChanged();
        }
    }
}
//XAML文件中使用路由参数
<ContentPage
    ......>
    <VerticalStackLayout>
        <Label
            HorizontalOptions="Center"
            Text="{Binding Name}"
            VerticalOptions="Center" />
        <Label
            HorizontalOptions="Center"
            Text="{Binding Sex}"
            VerticalOptions="Center" />
    </VerticalStackLayout>
</ContentPage>

2)方法二:通过实现IQueryAttributable接口,接收路由参数(在ViewModel中使用)

//接收字符串路由参数
public class PersonDetailViewModel : IQueryAttributable, ObservableObject
{
    [ObservableProperty]
    private string name;

    [ObservableProperty]
    private string sex;

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        name = HttpUtility.UrlDecode(query["name"].ToString());
        sex = HttpUtility.UrlDecode(query["sex"].ToString());
    }
}

//接收对象类型参数
public class PersonDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    [ObservableProperty]
    private Person person;

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        person = query["p1"] as Person;
    }
    ...
}

 

 

 

四、路由守卫

1、全局守卫:类似于生命周期函数,Shell类提供OnNavigating(导航发生前)和OnNavigated(导航发生后)可重写方法,通过重写这两个方法,可以对导航进行控制,实现路由守卫的功能。OnNavigating和OnNavigated方法,在AppShell.xaml.cs中定义。

//OnNavigating和OnNavigated方法的常用属性和方法
public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        ......
    }
    protected override void OnNavigating(ShellNavigatingEventArgs args)
    {
        base.OnNavigating(args);
        ShellNavigationState current = args.Current;//当前页,可通过Location属性获取URL
        ShellNavigationState target = args.Target; //目标页,可通过Location属性获取URL
        ShellNavigationSource source = args.Source; //导航类型,枚举,包括Unknown/Push/Pop/PopToRoot/Insert/Remove/ShellItemChanged/ShellSectionChanged/ShellContentChanged
        bool canCancle = args.CanCancel; //是否可以取消
        bool canclled = args.Cancelled; //是否取消
        args.Cancel(); //取消导航
        //获取导航令牌,并完成导航
        ShellNavigatingDeferral token = args.GetDeferral();
        token.Complete();
    }

    protected override void OnNavigated(ShellNavigatedEventArgs args)
    {
        base.OnNavigated(args);
        ShellNavigationState current = args.Current; //当前页
        ShellNavigationState previous = args.Previous; //上一页
        ShellNavigationSource source = args.Source; //导航类型
    }
}

//案例一:取消各后导航
protected override void OnNavigating(ShellNavigatingEventArgs args)
{
    base.OnNavigating(args);
    if (args.Source == ShellNavigationSource.Pop)
    {
        args.Cancel();
    }
}

//案例二、跟进弹出框的选择按钮,确定是否继续导航
protected override async void OnNavigating(ShellNavigatingEventArgs args)
{
    base.OnNavigating(args);
    ShellNavigatingDeferral token = args.GetDeferral();
    var result = await DisplayActionSheet("Navigate?", "Cancel", "Yes", "No");
    if (result != "Yes")
    {
        args.Cancel();
    }
    token.Complete();
}

 

2、局部守卫:Shell层次结构中的Tab,提供了一系列的重写方法。我们可以自定义Tab的派生类,并重新定义这些重写方法。在视觉层次结构中使用自定义的Tab派生类,就可以控制这个Tab下的路由行为,从而实现局部守卫的功能。

public class MyTab:Tab
{
    protected override void OnInsertPageBefore(Page page, Page before)
    {
        base.OnInsertPageBefore(page, before);
    }
    protected override void OnRemovePage(Page page)
    {
        base.OnRemovePage(page);
    }
    protected override Task<Page> OnPopAsync(bool animated)
    {
        return base.OnPopAsync(animated);
    }
    protected override Task OnPushAsync(Page page, bool animated)
    {
        return base.OnPushAsync(page, animated);
    }
    protected override Task OnPopToRootAsync(bool animated)
    {
        return base.OnPopToRootAsync(animated);
    }
}

 

 

五、后退按钮

当导航到非结构层次页面时,会创建导航堆栈,并显示后退按钮,可以重新定义后退按钮的样式和行为

 

<!--Command在按下后退按钮时执行-->
<!--其它样式和行为见名知意-->
<ContentPage
    ......>
    <Shell.BackButtonBehavior>
        <BackButtonBehavior
            Command="{Binding BackCommand}"
            CommandParameter="命令参数"
            IconOverride="back.png"
            IsEnabled="True"
            IsVisible="True"
            TextOverride="后退" />
    </Shell.BackButtonBehavior>
    ......
</ContentPage>

 

 

 

 

六、Navigation导航

Navigation导航类,在Shell框架中仍然可以使用,但不建议混合使用。Navigation会创建导航堆栈,并显示后退按钮。如:

//导航和后退
await Navigation.PushAsync(new DetailsPage());
await Navigation.PopAsync();
await Navigation.PushModalAsync(new DetailsPage());
await Navigation.PopModalAsync();

//导航传参
Contact contact = new Contact
{
    Name = "Jane Doe",
    Age = 30,
    Occupation = "Developer",
    Country = "USA"
};
...
await Navigation.PushModalAsync(new DetailsPage(contact));

//通过BindingContext导航传参
Contact contact = new Contact
{
    Name = "Jane Doe",
    Age = 30,
    Occupation = "Developer",
    Country = "USA"
};

await Navigation.PushAsync(new DetailsPage
{
    BindingContext = contact  
});

 

标签:Shell,Navigation,args,Current,6.5,MAUI,导航,await,路由
From: https://www.cnblogs.com/functionMC/p/17016612.html

相关文章

  • MAUI新生6.4-内容页ShellContent
    ShellContent是Shell视觉层次结构中,最终承载内容页的构件,Shell>FlyoutItem或TabBar>Tab>ShellContent-View。 一、按需加载内容页和应用启动时加载内容页1、按需加载内......
  • VMware ESXI 6.5安装
    一、硬件准备准备软件:推荐使用VMware,我用的是VMware 14镜像:VMware-VMvisor-Installer-6.5.0-5310538.x86_64-DellEMC_Customized-A04.iso硬件:因为是在宿主机上运行虚拟化软......
  • Visual Studio 2022 MAUI NU1105(NETSDK1005) 处理记录
    故障说明MAUI项目是日常使用的项目,一直都好好的某一天修改了几行代码后,突然项目无法编译了,提示NU1105错误从Git重新拉取一份之前的代码编译也是同样的错误,经过半天......
  • MAUI新生6.3-底部Tab栏导航TabBar/Tab/ShellContent
    一、TabBar的基本使用:如果只需要底部Tab栏导航,则应使用TabBar。TabBar的使用,和FlyoutItem基本相同,如下图所示:    二、底部Tab栏的样式设置Shell.TabBarBackgr......
  • 学习.NET MAUI Blazor(三)、创建.NET MAUI Blazor应用并使用AntDesignBlazor
    大致了解了Blazor和MAUI之后,尝试创建一个.NETMAUIBlazor应用。需要注意的是:虽然都叫MAUI,但.NETMAUI与.NETMAUIBlazor并不相同,MAUI还是以xaml为主,而MAUIBlazor则是......
  • MASA MAUI Plugin (七)应用通知角标(小红点)Android+iOS
    背景MAUI的出现,赋予了广大Net开发者开发多平台应用的能力,MAUI是Xamarin.Forms演变而来,但是相比Xamarin性能更好,可扩展性更强,结构更简单。但是MAUI对于平台相关的实现并不......
  • MAUI新生6.2-浮出控件导航Flyout-FlyoutItem/MenuItem/Header/Footer
    如前章所述,Shell导航框架,在UI层面,有两种导航方式,一是浮出控件导航,二是底部Tab栏导航,本章节将深入学习浮出控件导航。浮出控件提供了非常丰富的定制功能,组成部分如下图所示:......
  • MAUI新生5.6-Form表单类控件难点
     一、表单类控件目录Entry,单行输入框Edit,多行输入框CheckBox,复选框RadioButton,单选框Picker,下拉单选框Switch,开关Slider,滑块Stepper,步进器DataPicker,日期选择框......
  • MAUI-FilePicker选择文件_选择头像
    一、代码:privateasyncvoidBtnPickAndShow_Clicked(objectsender,EventArgse){try{varresult=awaitFilePicker.PickAs......
  • Uploader上传 控件升级6.5,增加简单图片压缩上传
    一、 Uploader上传控件升级6.5,增加简单图片压缩上传升级了2个前端功能1.前端压缩,图片上传(imgsingle):不改变图片的比例,在指定范围内等比例缩放,最小(minWidth*minHeight);......