Windows Phone开发(五)– 导航控制

前面几乎每篇文章都会涉及到页面的切换,页面导航,从程序启动开始,到结束。上一篇文章介绍了页面导航时会发生的几个事件以及页面中传值的方法。这一篇文章将介绍对导航的一些控制。

 

 

一 导航栈

 

 

从Windows Phone 手机上我们就可以看到,手机有Back键,但是没有Forward按键。而我们在前面例子中也基本都是使用GoBack()方法。看下MSDN,关于Forward的描述如下:

Navigates to the most recent entry in the forward navigation history, or throws an exception if no entry exists in forward navigation. For Windows Phone this method will always throw an exception because there is no forward navigation stack. 

上面告诉我们,在Windows Phone 中没有Forward Stack,而只有Back Stack,所以我们使用GoBack后没有办法在使用GoForward前进到上一个页面。而当我们进入到一个新的页面时,上一个页面就会被存放到Back Stack中,当我们点击Back按钮,或使用GoBack方法时就会销毁当前的页面,而从Back Stack中弹出上一页面。如果Back Stack中没有页面,此时点击Back按钮程序就会退出,而是用GoBack方法就会抛出异常,所以我们在使用时最先对CanGoForward属性进行判断。

在WP7.1版本中我们注意到,在NavigationService类下面多了一个BackStack的属性。

public IEnumerable<JournalEntry> BackStack
{
    get
    {
        return this._journal.BackStack;
    }
}

还记得在前面文章介绍Navigate方法的时候,见到到一下代码:

JournalEntry journalEntry = new JournalEntry(uri.OriginalString, uri);
this.Journal.AddHistoryPoint(journalEntry);


JournalEntry也是在WP7.1中公开的一个类,它表示后退或前进导航历史记录中的一个条目。这个类在silverlight和WPF中都是公开存在的,但是和WPF相比,这个类只有公开了Uri属性。这里我们不详细研究这个类,只看看它的构造函数,也是很简单。

internal JournalEntry(string name, Uri uri)
{
    Guard.ArgumentNotNull(uri, "uri");
    this.Name = name;
    this._source = uri;
    this.PageInstance = null;
    this.PausedPage = null;
}

Journal类是用来管理这些条目的,上面的AddHistoryPoint方法并不是新的要进的页面加入到BackStack中。在前面文章也看过这个的源码,在内部创建了一个ShellPage对象,并注册了相关的事件。

internal void AddHistoryPoint(JournalEntry journalEntry)
{
    Guard.ArgumentNotNull(journalEntry, "journalEntry");
    this._shellPagePending = this._shellPageManager.CreatePage(journalEntry.Source.ToString());
    if (this._shellPagePending == null)
    {
        throw new InvalidOperationException("Unable to create ShellPage");
    }
    this._shellPagePending.ShellPageCallback.OnNavigateAwayEventHandler += new EventHandler<NavigateAwayEventArgs>(this.ShellPage_NavigatedAway);
    this._shellPagePending.ShellPageCallback.OnNavigateToEventHandler += new EventHandler<NavigateToEventArgs>(this.ShellPage_NavigatedTo);
    this._shellPagePending.ShellPageCallback.OnRemoveEventHandler += new EventHandler<RemoveEventArgs>(this.ShellPage_RemovedPage);
    this._shellPagePending.ShellPageCallback.OnBackKeyPressInternalEventHandler += new EventHandler<BackKeyPressEventArgs>(this.ShellPage_BackKeyPressed);
    this._shellPageManager.NavigateTo(this._shellPagePending);
    this.IsBusy = true;
}

这些事件绑定的方法都是实现在Journal类中,在这些方法中可以找到对BackStack的操作。所以在NavigationService的BackStack属性中就是返回了_journal.BackStack。这里具体内容就不讨论。 我们只要知道在Windows Phone 7中存在一个BackStack来存放之前访问的页面,一边我们可以回退。

 

 

二 Navigate和GoBack

 

 

看起来好像没有太大关系,一个是导航到一个页面,一个是从返回到BackStack的一个页面,但是我们在GoBack的地方改用Navigate回如何呢?前一个页面怎么处理呢?在Demo4这个程序中,我们有MainPage,Page1,Page2三个页面。导航方式为MainPage<->Page1<->Page2。2个页面间可以选择Navigate或GoBack来导航。

现在我们进行以下操作:从MainPage导航到Page2,然后从Page2调用GoBack到MainPage,以下是输出的结果,我们打印了BackStack的数量

Entry the MainPage
JournalEntry number is 0
MainPage Navigate to Page1
Leave the MainPage
JournalEntry number is 1

Entry the Page1
JournalEntry number is 1
Page1 Navigate to Page2
Leave the Page1
JournalEntry number is 2

Entry the Page2
JournalEntry number is 2
Page2 Call GoBack
Leave the Page2
JournalEntry number is 1

Entry the Page1
JournalEntry number is 1
Page1 Call GoBack
Leave the Page1
JournalEntry number is 0

Entry the MainPage
JournalEntry number is 0

这里的结果很明显,每当我们进入到新的一个页面时,BackStack数量就会增加一个,当我们调用GoBack返回时,就会减少。回到MainPage时BackStack为空。这个时候在点击Back按钮时就退出了。

下面我们进行另一个试验,从MainPage导航到Page2,然后在从Page2调用Navigate导航到MainPage,结果如下:

Entry the MainPage
JournalEntry number is 0
MainPage Navigate to Page1
Leave the MainPage
JournalEntry number is 1


Entry the Page1
JournalEntry number is 1
Page1 Navigate to Page2
Leave the Page1
JournalEntry number is 2


Entry the Page2
JournalEntry number is 2
Page2 Nagivate to Page1
Leave the Page2
JournalEntry number is 3


Entry the Page1
JournalEntry number is 3
Page1 Nagivate to MainPage
Leave the Page1
JournalEntry number is 4


Entry the MainPage
JournalEntry number is 4

可以看到采用Navigate导航,目前BackStack中保存了4条记录(MainPage,Page1,Page2,Page1)。当我们使用Back会退时结果和第一个一样。但是注意到其中Page1被存放了两次,二这2个Page1是不同的实例。这个很容易证实,我们在Demo的每个页面中填入一个数字,然后造使用GoBack,你会发现两个Page1的页面的数字是不一样的。

 

 

三 控制导航行为

 

 

WP7.1中还提供了操作BackStack的方法,在NavigationService中还提供了一个RemoveBackEntry的方法,这个方法可以从BackStack中移除一个项目,如果没有可以删除的项目就会抛出异常,而要删除多个项目的话需要调用多次。而且此方法必须在UI现场调用。

Demo4中,我们从MainPage导航到Page1,然后点击移除历史记录的按钮。结果如下:

Entry the MainPage
JournalEntry number is 0
MainPage Navigate to Page1
Leave the MainPage
JournalEntry number is 1

Entry the Page1
JournalEntry number is 1
Remove History Entry
JournalEntry number is 0

此时MainPage已经从BackStack中移除,此时我们点击GoBack按钮已经不在有效,而点击Back按钮程序将直接退出。但是此时继续点删除按钮,也不会引发异常。

JournalEntry entry = this.NavigationService.RemoveBackEntry();

方法会返回一个JournalEntry对象,我们不能做更多操作,只能通过Uri属性得到移除的页面的Uri。而除此之外还有一个JournalEntryRemoved事件,这个时间会在删除时触发,我们可以重写他绑定的OnRemovedFromJournal方法,但是我们也做不了过多的操作,只能从JournalEntryRemovedEventArgs对象中获得移除的JournalEntry对象。目前我们好像还不能对BackStack进行替换和移除到起始页之间的页面等操作,但是MSDN上好像有写WP7.1的更新。

  • Replace the current page with a new page. This can be achieved by navigating forward to the new page and then removing the top of the back stack.
  • Remove all pages up to a given page, which is typically the first page. This can be accomplished by removing all the intervening pages and navigating back to the first page.
  • Clear the entire page back stack and replace with a given page. This can be accomplished by navigating forward to the new root page and then removing all the entries in the back stack.

最后要提到的是,在PhoneApplicationFrame类中也提供了这些在NavigationService中的方法和事件,实现也是相同,应该也是调用NavigationService中的实现。

在WP7.1还有一个虚方法我们可以重写导航的行为。

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
   if (e.IsCancelable && e.NavigationMode == NavigationMode.New)
   {
       e.Cancel = true;
   }
   base.OnNavigatingFrom(e);
}

在这个方法中,我么可以判断当前导航能否取消。而通过NavigationMode 可以判断当前是创建新页面,还是从BackStack返回一个页面,而通过Url我们可以知道程序具体访问的页面,然后使用Cancle属性决定是否取消这个导航。

 

 

四 Back按键的控制

 

 

在BackStack栈中,机器的Back按钮作用很大,我们可以对Back按钮的行为进行控制。在WP7中有以下2个方法可以控制Back按键的行为。

        void MainPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Debug.WriteLine("MainPage_BackKeyPress");
            e.Cancel = false;
        }

        protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
        {
            Debug.WriteLine("OnBackKeyPress");
            base.OnBackKeyPress(e);
        }

在点击Back按键的时候,会先调用OnBackKeyPress方法,然后调用BackKeyPress事件绑定的方法。我们发现这2个方法参数都是CancelEventArgs ,所以我们可以调用e.Cancel = True 来取消Back按键的功能。

在WP7.1中,Frame也提供了一个BackKeyPress事件:

(Application.Current as App).RootFrame.BackKeyPress += new EventHandler<System.ComponentModel.CancelEventArgs>(RootFrame_BackKeyPress);

void RootFrame_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
{
   Debug.WriteLine("RootFrame_BackKeyPress");
}

这个事件会捕获框架中所有页面的Back按钮时间,而不仅仅是当前的页面。

 

注意的问题:

 

在重写回退键我们有一些问题需要注意,Back按键不仅可以对页面进行导航,还可以关闭一些弹出窗体,比如MessageBox,Popup或者是ListPicker和ContectMenu。比如我在MainPage点击Back按钮时弹出一个MessageBox询问是否退出的选项。在我点击Back页面之前,MainPage界面上的ListPicker或ContectMenu都是弹出的状态,这个时候系统会关闭控件弹出内容,但同时也会弹出是否退出的MessageBox,但是你点击是调用了e.Cancle = True也不会退出程序,所以在重写Back按键方法时,要注意界面上的控件状态。

 

 

五 总结

 

 

经过几篇文章的介绍,应该对于Windows Phone的页面导航有了一定认识,有一些内容没有涉及,但是整体来说还是相对比较简单。对我们来说用到最多的就是OnNavigatedToOnNavigatedFrom方法,也是最重要的。因为页面导航不仅仅是页面的切换,还涉及到页面数据的保存和恢复,页面传值,墓碑状态的数据保存和恢复,打开和关闭页面时新建和回收一些资源等等。所以很重要,而在后面介绍到墓碑机制时也会用到。所以导航虽然简单,但是到项目中,页面数据保存恢复操作还是很麻烦的。

 

DEMO4下载地址:http://download.csdn.net/source/3495956


如果本文对您有帮助,可以扫描下方二维码打赏!您的支持是我的动力!
微信打赏 支付宝打赏

1 评论

xuweidu进行回复 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注