一个常规的App是由多个Page组成的,出现多个Page就会涉及页面跳转问题。Xamarin.Forms页面之间的跳转通过Navigation Stack管理Page,如页面A跳转到页面B时,会将B压入栈定,此时页面B成为活动页面,执行Back操作时,页面B从栈定推出使页面A重新变为活动页面。
每个应用程序都有一个特殊页作为应用程序的入口(main page, or the home page, or the start page),Xamarin.Forms中由App的MainPage属性设置。
INavigation介绍
Xamarin.Forms中页面分为Modal pages(模态页面) 和 modeless pages(非模态页面),跳转到一个新的页面可以通过PushAsync和PushModalAsync两个方法实现,两个方法均传入一个Page对象作为参数。同样提供了PopAsync和PopModalAsync两个方法返回前一个页面。PushAsync和PopAsync是对非模态页面的操作,PushModalAsync和PopModalAsync操作的是模态页面。PopToRootAsync方法表示回到导航栈低页面。RemovePage方法从导航栈中删除一个页面。InserPageBefore方法将一个page插入到指定page之前。通过这些方法操作Navigation Stack可以在不同Page间跳转。
关于PopXXX和Push方法定义返回Task类型介绍参考(涉及的OnDisappearing和OnAppearing不在本文不再说明):
http://www.cnblogs.com/qiandi/p/5598742.html
当我们调用PushAsync方法时必须修改App的MainPage属性为NavigationPage对象,否则报如下异常,使用NavigationPage时不必在为iOS单独设置Page的Padding属性解决页面和状态栏遮盖问题。
PopXXX和PushXXX提供的bool类型参数重载方法设置为true增加页面跳转动画。
INavigation还定义了两个IReadOnlyList
如果用MainPage = new NavigationPage(new Page0());初始化App的MainPage属性运行应用程序,此时IOS平台中ModalStack为空,Android和Windows平台ModalStack包含一个元素(MainPage对应NavigationPage实例)。三个平台中的NavigationStack集合包均含一个Page0的实例。
这些方法均定义在INavigation中,VisualElement提供了一个INavigation类型只读属性Navigation,可以通过调用Navigation的方法实现跳转。
页面中调用代码Navigation.PushAsync(new ModelessPage(), true);
Programmers familiar with Android architecture are sometimes curious how Xamarin.Forms page navigation integrates with the aspect of Android application architecture known as the activity. A Xamarin.Forms application running on an Android device comprises only one activity, and the page navigation is built on top of that. A ContentPage is a Xamarin.Forms object; it is not an Android activity, or a fragment of an activity.
前面提到的参考链接有说明,什么意思自己看看吧!!!
NavigationPage介绍
NavigationPage的作用就是导航管理页面。
NavigationPage提供的属性
- BarBackgroundColor - 获取、设置NavigationPage顶部导航栏的背景色(iOS、Android、Windows 平台有效)。
- BarTextColor - 获取、设置NavigationPage顶部导航栏显示文本颜色(iOS、Windows平台有效)。
不同平台效果:
- CurrentPage - 获取Navigation Stack最顶部页面,不管当前显示页面是模态页面还是非模态页面,CurrentPage都表示NavigationStack集合中的最后一项。
when a modal page is active, CurrentPage continues to indicate the last modeless page that was active before navigation to a modal page
NavigationPage提供的BindableProperty
- HasBackButtonProperty - 是否显示非模态页面导航栏的返回按钮(iOS、Android起作用)
C#设置NavigationPage.SetHasBackButton(this, false);
XAML设置NavigationPage.HasBackButton="False" - HasNavigationBarProperty - 是否显示导航栏
- BackButtonTitleProperty - 设置返回按钮的文本(仅iOS平台)
- TitleIconProperty - 设置导航栏上显示图标(iOS平台Icon代替Title,Android修改左侧图标显示,Windows 无效)
屏蔽Android、Windows Mobile 返回按键
Android和Windows Phone会包含一个返回键,返回键提供类似PopAsync或PopModalAsync的工能,程序中要屏蔽返回键的功能需要重写Page的OnBackButtonPressed方法。OnBackButtonPressed方法返回true值,程序不处理返回按键。
protected override bool OnBackButtonPressed() { //return base.OnBackButtonPressed(); return true; }
Page还定义了SendBackButtonPressed方法,用来模拟返回按钮被按下。调用该方法与Android按下返回键功能类似。
官方电子书中提到“Although this works on iOS and Android, it currently does not work on the Windows Runtime platforms.”这个方法只对iOS和Android有效,Windows Runtime platforms不能正常工作。 实际测试iOS无效,Android正常,Windows 无法测试。
页面间传值
一个应用程序会包含多个页面,把数据从一个页面传到另一个页面该怎么做?
构造函数传值
页面跳转的PushXXX方法传入Page页面实例作为参数,可以通过Page页面的构造函数传入数据,如Navigation.PushModalAsync(new DataPage(label.Text));,在DataPage重载的构造函数中在处理接收的数据。
属性方法传值
根据页面的属性或调用方法传值。如:
构造函数传值是单向的不能传递返回数据,通过属性传值可以从当前页面向前一个页面传值。应从NavigationStack集合中取得前一个页面实例。
var stack = (App.Current.MainPage as NavigationPage).Navigation.NavigationStack; int lastIndex = stack.Count - 1; navigationPage homePage = stack[lastIndex] as navigationPage; if (homePage == null) { homePage = stack[lastIndex - 1] as navigationPage; } homePage.SetText(Value + "页面返回数据了");
从模态页面跳转到非模态页面和模态页面跳转到模态页面两种情况,跳转前的页面在NavigationStack中有不同的位置。如果是非模态页面跳转到非模态页面?又要从ModalStack中获取前一个页面实例,所以通过属性和方法传值不推荐!!!
Messaging Center传值
MessagingCenter是一个静态类,提供了Subscribe、Unsubscribe和Send三个公开的方法。Subscribe订阅接收的消息信息,参数、发送者类型等。Unsubscribe取消订阅,不在接收消息。Send发送指定类型消息。
Messaging Center传值示例:
数据接收页面增加消息订阅代码:
MessagingCenter.Subscribe(this, "Value", (arg1, arg2) => { label.Text = arg2; });
Subscribe包含两个范型参数,第一个为消息发送者类型,第二个为传递数据类型。Subscribe包含三个参数,第一个为消息接收者,第二个参数为字符串类型消息标示,第三个参数是一个lambda表达式,表示接收到消息后的动作。
数据传递页面增加发送消息代码:
MessagingCenter.Sendstring>(this, "Value", label.Text);
Send两个范型参数与Subscribe方法相同。Send包含三个参数,第一个参数为消息发送者,第二个参数为消息标示,第三个参数为传递的数据对象。
本示例Send方法应在PushAsync后执行。PushAsync执行前消息没有订阅,Send发送消息此时没有接收者。
不再接收消息时应该调用Unsubscribe取消指定消息的订阅,对资源回收释放。本示例取消订阅代码添加在Subscribe方法的lambda中。
MessagingCenter.Unsubscribestring>(this, "Value");
Strictly speaking, however, unsubscribing shouldn’t be necessary because the MessagingCenter maintains WeakReference objects for subscribers。
MessagingCenter定义了一个Dictionary, List >>>类型对象通过WeakReference管理subscribers,所以Unsubscribe方法不是必须的。
事件传值
WinForm中常用的传值形式,事件传值。推荐数据回传时使用。
Page1 跳转到Page2时,Page2添加事件定义:
public EventHandler<string> Value;
Page2 返回Page1 时,触发事件:
EventHandler handler = Value; if (handler != null) { handler(this, "增加返回字符串"); }
Page1 跳转到Page2时,订阅Page2的事件:
var page = new Page2(); page.Value += (sender, e) => { label.Text = e; }; Navigation.PushAsync(page);
传递复杂数据继承EventArgs实现传值
App Class传值
Xamarin.Forms应用程序会提供一个App类(继承Application)作为程序的入口,该对象在应用程序运行期间一直存在,可以借助App类实现不同页面之间数据传递。
修改App代码定义属性保存、获取数据。如:
获取App类实例可以通过(App)Application.Current)获取。或着直接定义static 属性,直接通过App.Value 传值。