您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  要资料 文章 文库 Lib 视频 Code iProcess 课程 认证 咨询 工具 火云堂 讲座吧   成长之路  
会员   
 
   
 
  
每天15篇文章
不仅获得谋生技能
更可以追随信仰
 
 
     
   
 订阅
  捐助
Windows Phone开发(四)-- 导航事件和传值
 
作者:cc_net 来源:CSDN 发布于 2015-04-01
810 次浏览     评价:      
 

从第一篇开始我们就看到了页面的导航切换,上一篇文章则介绍了框架实现导航的原理和过程。真正的导航功能是NavigationService类来实现的。而Frame是Page的载体,是负责导航,历史记录等功能的,相当于一个指挥官。这一篇就主要介绍一下导航的操作和相关的一些方法。

一 导航时发生错误

默认的我们建立一个Windows Phone程序,使用导航功能是不会出现这个问题的。我们先看一个列子:

private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)  
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
//RootVisual = RootFrame;
RootVisual = new MainPage();
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}

我们新建一个项目后,不把Frame设置到RootVsual,而是用MainPage。我们从Mainpage导航到Page1. 使用两种方法:

private void Button_Click(object sender, RoutedEventArgs e)  
{
方法1:this.NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
方法2:(Application.Current as App).RootFrame.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
}

第一种方法是使用Page页面的NavigationSevice属性来导航,第二种方法是使用Frame对象来导航。从上一篇我们知道,两种方法是相同的。结果是使用第一种方法发生了NullReferenceException错误,为什么NavigationService对象是空?我们不是在App构造函数中就创建过NavigationServie对象的实例吗,并且在加载了XAML文件后设置到了依赖属性中。

obj2.SetValue(NavigationServiceProperty, this);  

上一篇文章介绍过,Page的NavigationService属性是通过依赖属性获得的。其实这里我要了解依赖属性的特点,虽然是一个static字段来维护,但是内部是有Hash表来存放不同对象的属性值。我们在构造函数的Load方法中,加载了配置文件中指定的MainPage,并生成了他的实例,然后设置了NavigationServiceProperty。而这里我们使用,MainPage的是一个新的实例,他的NavigationServiceProperty为Null。所以这里当然会报错了。 而且NavigationServiceProperty是internal属性,说以我们不能手动设置了。

从这个异常我们能发现,每次导航的新的页面,在加载完成Page之后,都会设置Page的NavigationServiceProperty的属性。所以并不是创建了Frame,我们就能使用NavigationService来导航。而是需要Frame来吧Page和NavigationService关联起来。而这里,我们new的Mainpage没有通过Frame加载显示的,所以无法导航。

接下来看看第二种方法。这里使用的是我们创建的RootFrame对象,因为此时Frame已经完全构造好了,NavigationSevice也创建好了。所以此时调用Navigate方法是不会报错的,而且返回的是true。因为这里不存在延迟导航,所以说明是成果了,但是为什么没显示呢?我们点击【<-】退出程序,但是点了2下才退出,这说明导航了吗?我们在做个试验,我们在Page1中加入以下的代码

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)  
{
base.OnNavigatedTo(e);
if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
{
bool ret = (Application.Current as App).RootFrame.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
}
}

我们在从MainPage用方法2导航到Page1后,在点【<-】退出程序,现在要点3下了。我们也可以设置断点来查看,实际完成了MainPage->Page1->Page2的导航。当时为什么没有显示呢?很简单了,因为Page要通过RootVisual来显示,导航的话Page需要Frame来加载的,当我们把Frame设置到RootVisual时,就能显示,而这里我们设置的MainPage,虽然Frame导航了,但没有被显示出来。但是RootVisual只能设置一次,所以这里没有办法让他显示出来了。

好了,到这里我们导航的原理就研究到这里,通过2个错误我们进步不理解了Frame和Page的关系。也明确了Frame导航和加载Page的这一功能。这也说明为什么必须有Frame才能导航。

二 导航的过程

在上面的列子中,我们看到了OnNavigatedTo方法,当导航到Page1时自动导航到了Page2。我们知道一个Page导航到另一个Page实际是一个卸载和装载的过程。在这个过程中会触发一系列的事件和方法,下面我们就来看下这些方法。

public class Page : UserControl  
{
// Methods
internal Page();
internal void InternalOnFragmentNavigation(FragmentNavigationEventArgs e);
internal virtual void InternalOnNavigatedFrom(NavigationEventArgs e);
internal virtual void InternalOnNavigatedTo(NavigationEventArgs e);
internal void InternalOnNavigatingFrom(NavigatingCancelEventArgs e);
protected virtual void OnFragmentNavigation(FragmentNavigationEventArgs e);
protected virtual void OnNavigatedFrom(NavigationEventArgs e);
protected virtual void OnNavigatedTo(NavigationEventArgs e);
protected virtual void OnNavigatingFrom(NavigatingCancelEventArgs e);
}

在Page类中,我们看到了和导航相关的4个protected虚方法。对应有4个internal的方法,他们是在事件发生的时候触发的(系统已经给我们绑定了),他们实现很简单,就是调用这里的虚方法。我们可以通过重写这些虚方法来控制。而在PhoneApplicationPage类中没有重写这些方法,也没有加入其它相关的方法。

在我们Demo3的PhoneApp3中我们在MainPage和Page1重写这几个方法:

protected override void OnFragmentNavigation(System.Windows.Navigation.FragmentNavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnFragmentNavigation", this.ToString());
base.OnFragmentNavigation(e);
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("{0}:OnNavigatedTo", this.ToString());
base.OnNavigatedTo(e);
}

protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
Debug.WriteLine("{0}:OnNavigatingFrom", this.ToString());
base.OnNavigatingFrom(e);
}

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("{0}:OnNavigatedFrom", this.ToString());
base.OnNavigatedFrom(e);
}

启动程序,从MainPage导航到Page1: 输出如下:

PhoneApp3.MainPage:OnNavigatedTo  
PhoneApp3.MainPage:OnNavigatingFrom
PhoneApp3.MainPage:OnNavigatedFrom
PhoneApp3.Page1:OnNavigatedTo

点击【<-】按钮,输出如下:

PhoneApp3.Page1:OnNavigatingFrom  
PhoneApp3.Page1:OnNavigatedFrom
PhoneApp3.MainPage:OnNavigatedTo

在点击【<-】按钮退出程序,输出如下:

PhoneApp3.MainPage:OnNavigatingFrom  
PhoneApp3.MainPage:OnNavigatedFrom

好吧,这些方法的执行顺序很清楚了,我就不用什么介绍了。不清楚可以看MSDN。不过这里命名很容易让人产生误解。我开始就弄反了。这里To和From相对的对象都是当前的页面。OnFragmentNavigation 方法,在导航到包括片断的统一资源标识符 (URI) 时会发生。一个片断是片断分隔符 (#) 后的值。这个后面在介绍。

三 页面传值

和Web一样,我们在导航过程中可能需要传递值,对于WinForm程序来说,我们可以通过Form的构造函数传值,可以定义定义全局变量等多种方法,对于Web,我们可以有QueryString,Cookies,Session等方法,在这里我们可以用类似的方法传递,这里我们结合导航到过程来进行值的传递。这里我们导航时不能通过构造函数传递,也无法使用Cookies和Session。

我在Demo3的PhoneApp4中,MainPage和Page1放置了5个TextBox,每个对应一种传递方式:

1 全局变量传递

这个方法在WinForm很常见,我们在App类中定义一个变量,App是一个全局对象,所以可以定义在这里。

public partial class App : Application  
{
public string StaticVar { get; set; } //全局变量

public PhoneApplicationFrame RootFrame { get; private set; }

public App()
{
}

在Mainpage的OnNavigtedFrom方法中,把s1的值传递给全局变量,s1的值来源于TextBox1。当然你可以在导航前任意时刻获取TextBox的值,这里只是演示导航方法。

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedFrom", this.ToString());
base.OnNavigatedFrom(e);
s1 = textBox1.Text;
//全局变量传递
(Application.Current as App).StaticVar = s1;
}

在Page1的OnNavigatedTo方法中接受并显示。同样,你可以在导航到Page1后的任意时刻来获取并显示,这里也是为了掩饰导航方法。

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedTo", this.ToString());
base.OnNavigatedTo(e);
//从全局变量接受
s1 = (Application.Current as App).StaticVar;
textBox1.Text = s1;
}

使用全局变量很方便,但是占用内存空间,并且存在丢失的可能。这个以后文件会介绍。

2 页面QueryString传值

因为这里导航和Web相似,所以我们也可以用Uri后加上QueryString的传值方式。我们这里使用TextBox2.

首先修改导航按钮代码,我们必须在导航前,构造好导航的Uri,并且要获取TextBox2的值。QueryString格式为 Name1=Value1&Name2=Value2,多个参数用&间隔开。

private void button1_Click(object sender, RoutedEventArgs e)  
{
//构造QueryString
s2 = textBox2.Text;
string uri = "/Page1.xaml?s2=" + s2;

//导航
this.NavigationService.Navigate(new Uri(uri, UriKind.Relative));
}

而在Page1页面,我们通过NavigationContext属性的QueryString获得传递的值。这里是返回的是IDictionary类型,在使用前,必须检查Name对应的变量是否存在。

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedTo", this.ToString());
base.OnNavigatedTo(e);
//从Uri接受
IDictionary<string, string> queryString = this.NavigationContext.QueryString;
if (queryString.ContainsKey("s2"))
{
s2 = queryString["s2"];
}

//显示到UI
textBox2.Text = s2;
}

使用QueryString可以传递少量数据,因为Uri长度是有限制的,并且只能传递简单类型,如果要传递自定义类对象,就不适用了。另外传的是string类型,所以可能需要进行类型转化。在我们导航的时候,会建立一个JournalEntry对象,当新的xaml页面加载完后,会去解析URI中的QueryString数据存入到其中。

JournalEntry.SetNavigationContext(obj2, new NavigationContext(UriParsingHelper.InternalUriParseQueryStringToDictionary

3 PhoneApplicationService类

如果我们需要传递自定义类型数据时,就需要用到这个对象。其实这个对象我们并不是很陌生,在前面我们就介绍过他实现了IApplicationService就接口,为Silverlight程序供了扩展功能,比如前面App中启动时Application_Launching,Application_Activated等四个方法。在这里他在内部提供了一个字典来维护一些全局的数据:

public IDictionary<string, object> State { get; }  

所以我们不需要自己去定义全局变量,而可以直接使用他,通过PhoneApplicationService.Current.State就能访问到这个字典。我们这里使用TextBox3来演示。在MainPage中把TextBox3获得的值,设置到字典中,注意,这里的Key需要是唯一的。另外我们使用时要引入以下命名空间。

using Microsoft.Phone.Shell;  

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedFrom", this.ToString());
base.OnNavigatedFrom(e);
s3 = textBox3.Text;
//使用PhoneApplicationService传递
PhoneApplicationService.Current.State["s3"] = s3;
}

我们在Page1中,我们取得s3的值2,这里我们采用了TryGetValue来获得值,当然也能使用QueryString中的方法。两者都能实现。TryGetValue性能更高。

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedTo", this.ToString());
base.OnNavigatedTo(e);
//从PhoneApplicationService获得数据
object objS3;
PhoneApplicationService.Current.State.TryGetValue("s3", out objS3);
if (objS3 != null)
{
s3 = Convert.ToString(objS3);
}

//显示到UI
textBox3.Text = s3;
}

这里我们要注意,这里是object类型,所以在读取数据时,存在类型转换。另外,这里传递的对象,必须是可以序列化的对象。

4 独立存储传递

使用独立存储IsolatedStorageSettings类时,需要引用以下命名空间。我们在MainPage中使用她来保存TextBox4的值。

using System.IO.IsolatedStorage;  

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedFrom", this.ToString());
base.OnNavigatedFrom(e);
s4 = textBox4.Text;
//使用独立存储
IsolatedStorageSettings.ApplicationSettings["s4"] = s4;
}

在Page1页面,我们获取S4的值,这里我们采用TryGetValue<>,这是一个泛型方法,从安全和性能上都比较好。

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)  
{
Debug.WriteLine("{0}:OnNavigatedTo", this.ToString());
base.OnNavigatedTo(e);
//从独立存储获得数据
IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("s4", out s4);
textBox4.Text = s4;
}

看起来也没有什么特别。我们在MainPage的OnNavigatedTo也加入上面Page1中的代码,我们从MainPage导航到Page,让后在关闭程序,在重新启动。嘿嘿。竟然在Mainpage中就显示了TextBox4之前的值。这是因为独立存储把值存到了本地,所以即便程序关闭也不会丢失,而前面则存在丢失的问题。对于全局变量和QueryString,即便程序不完全关闭,也是存在丢失的可能。所以一般来说,我们使用IsolatedStorageSettings来存储程序的一些配置信息。当然我们还能选择File和Database等方式来传递数据,当然对于页面传值来说就有点不太适合。

以上是各种方式传值的结果。

四 总结

这一篇文章首先继续谈了Frame和Page的关系, 然后介绍了在导航时会触发的事件方法,我们可以通过重写这些方法来控制页面的显示,比如进行值的传递。也详细介绍了值传递的四种方法和使用的环境。在下一篇文章将继续介绍导航的其他常用方法以及回退键,还有特殊页面的回退处理。

   
 订阅
  捐助
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
 

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...   
 
 
 
 
 
每天2个文档/视频
扫描微信二维码订阅
订阅技术月刊
获得每月300个技术资源
 
 

关于我们 | 联系我们 | 京ICP备10020922号 京公海网安备110108001071号