天天看點

如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:

原文: 如何在UWP中統一處理不同裝置間的頁面回退邏輯

      已經有一段時間沒有寫部落格來記錄自己的學習點滴了。現在回想起來确實有些慚愧,期間經曆了一些事情,到目前為止算是平息了,是時候該收收心來充實自己了。

      在本篇缪文中,樓主打算給UWP開發的初學者講述一個在開發中經常遇到的很現實的問題:頁面回退邏輯 。

      衆所周知,UWP的應用程式理論上是可以運作在Windows上的各種裝置上,其中包括Windows PC、WindowsMobile、XBox、IOT等。當我們的UWP應用程式運作在不同的裝置上時,不同裝置間的頁面回退邏輯我們就要考慮周全,要考慮不同裝置間的頁面回退操作該如何設計才能更好的滿足使用者的使用需求。是以,我們有必要将不同裝置間的頁面回退邏輯進行統一封裝,這樣一來不僅有利于代碼的維護,而且也有利于回退功能的擴充,實作了實作了“高内聚低耦合“。為了友善,樓主這裡隻簡單論述一下當我們的UWP應用程式運作在PC上和Mobile上時該如何處理不同平台的頁面回退邏輯。當應用程式運作在PC上時,頁面回退常常是通過使用者點選應用程式提供的一個回退按鈕來進行頁面回退,但是當我們的應用程式運作在Mobile上時,使用者更願意使用手機裝置上提供的實體後退鍵來進行頁面回退,這樣一來,我們就需要使用封裝的思想來進行封裝。

1、理論分析:

     在新的MSDN中,微軟為我們提供了一套新的API:SystemNavigationManager 。當UWP應用程式在PC上運作的時候,通過此API,我們可以為應用程式提供一個回退按鈕來向使用者暗示此頁面是可以回退的,當使用者點選該按鈕後,頁面成功回退。但是當我們的UWP應用程式運作在Mobile上時,如果還是用這種方法來進行頁面回退的的話,對使用者來說就可能不是很友好,是以,我們要投其說好,用手機裝置上的實體後退鍵來實作相應的頁面回退邏輯,其對應的API為:HardwareButtons.BackPressed。分析到這,我們基本上明白該如何處理這兩中裝置間的回退邏輯的差異。So,問題來了:我們該把這套邏輯放到哪裡合适?何時使用這套邏輯較為合适? 這是兩道主觀題,仁者見仁智者見智。樓主這裡抛磚引玉,為初學者論述一種方法。

     由于應用程式剛啟動的時候會觸發App.OnLaunched()函數,是以我們需要修改OnLaunched()函數;其次,為了保證頁面的唯一性,我們這裡使用“架構頁”的方法來承載不同的頁面,通過Frame來完成頁面的跳轉;最後,我們還需要實作一個使用者控件來方式應用程式的主題架構。

     總結一句話就是:讓應用程式來加載我們的使用者控件,讓使用者控件來承載我們的架構頁,讓架構頁來完成應用程式的頁面跳轉。

     是不是感覺很繞口??沒關系,接下來我們看看實際的代碼該如何寫………………

2、代碼實作:

首先:

  我們需要為我們的應用程式建立一個頁面跳轉服務類:NavigationService,該類封裝頁面的回退邏輯。需要指出的是:由于SystemNavigationManager 可以實作不同平台的回退邏輯,是以我們沒必要再單獨将Mobile的實體後退鍵封裝(謝謝yan_xiaodi的糾正)。代碼很簡單,我相信你看一下就會的。

如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
1 public class NavigationService
 2     {
 3         public static NavigationService Instance { get; protected set; }
 4 
 5         private Frame frame;
 6 
 7         public Stack<Type> PageStack { get; protected set; }
 8 
 9         public NavigationService(Frame frame)
10         {
11             if (Instance != null)
12             {
13                 throw new Exception("Only one navigation service can exist in a App.");
14             }
15             Instance = this;
16             this.frame = frame;
17             this.PageStack = new Stack<Type>();
18 
19             SystemNavigationManager.GetForCurrentView().BackRequested += NavigationService_BackRequested;
20         }
21 
22         public void NavigateTo(Type pageType, object parameter)
23         {
24             if (PageStack.Count > 0)
25             {
26                 //傳回位于Stack頂部的對象但不将其移除。
27                 if (PageStack.Peek() == pageType)
28                 {
29                     return;
30                 }
31             }
32             PageStack.Push(pageType);
33             frame.Navigate(pageType, parameter);
34             UpdateBackButtonVisibility();
35         }
36 
37         public void NavigateToHome()
38         {
39             var type = frame.BackStack.FirstOrDefault().SourcePageType;
40             frame.Navigate(type, null);
41             frame.BackStack.Clear();
42             UpdatePageStack();
43             UpdateBackButtonVisibility();
44         }
45         private void UpdatePageStack()
46         {
47             if (PageStack.Count > 0)
48             {
49                 PageStack.Pop();
50             }
51         }
52 
53         private void UpdateBackButtonVisibility()
54         {
55             SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = frame.CanGoBack ?
56                  AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
57         }
58         private void NavigationService_BackRequested(object sender, BackRequestedEventArgs e)
59         {
60             if (frame.CanGoBack)
61             {
62                 frame.GoBack();
63                 UpdatePageStack();
64                 e.Handled = true;
65             }
66             UpdateBackButtonVisibility();
67         }
68     }      

View Code

其次:

  頁面跳轉服務類算是已經封裝完成,接下來我們就需要建立一個使用者控件來承載應用程式的主體架構。

如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
1 <UserControl
 2     x:Class="NavigationDemo.ShellView"
 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 5     xmlns:local="using:NavigationDemo"
 6     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 7     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 8     mc:Ignorable="d"
 9     d:DesignHeight="300"
10     d:DesignWidth="400">
11     <UserControl.Resources>
12         <x:String x:Key="home">首頁</x:String>
13         <x:String x:Key="article">文章</x:String>
14         <x:String x:Key="question">問題</x:String>
15         <x:String x:Key="thing">東西</x:String>
16     </UserControl.Resources>
17     <Grid>
18         <SplitView IsPaneOpen="True" DisplayMode="CompactInline" OpenPaneLength="100">
19             <SplitView.Pane>
20                 <ListView>
21                     <ListViewItem x:Name="homeCmd" Content="{StaticResource home}">
22                     <ListViewItem x:Name="articleCmd" Content="{StaticResource article}"/>
23                     <ListViewItem x:Name="questionCmd" Content="{StaticResource question}"/>
24                     <ListViewItem x:Name="thingCmd" Content="{StaticResource thing}"/>
25                 </ListView>
26             </SplitView.Pane>
27             <SplitView.Content>
28                 <Frame x:Name="frame" x:FieldModifier="public"/>
29             </SplitView.Content>
30         </SplitView>
31 
32     </Grid>
33 </UserControl>      

然後:

  主體架構控件已經設計完成,接下來我們就修改改造App類。我們需要為應用程式提供一個全局的頁面跳轉,這樣友善使用;其次我們需要将應用程式的初始頁面改造為一個使用者控件,這樣就保證引用程式始終加載的是一個使用者控件。

如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
1 public static NavigationService NavService { get; set; }
 2 protected override void OnLaunched(LaunchActivatedEventArgs e)
 3         {
 4             //ShellView是我們建立的使用者控件
 5             ShellView rootFrame = Window.Current.Content as ShellView;
 6 
 7             // Do not repeat app initialization when the Window already has content,
 8             // just ensure that the window is active
 9             if (rootFrame == null)
10             {
11                 // Create a Frame to act as the navigation context and navigate to the first page
12                 rootFrame = new ShellView();
13                 if (rootFrame.frame == null)//frame是我們在使用者控件中建立的架構頁面
14                 {
15                     rootFrame.frame = new Frame();
16                 }
17                 NavService = new NavigationService(rootFrame.frame);
18 
19                 rootFrame.frame.NavigationFailed += OnNavigationFailed;
20 
21                 if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
22                 {
23                     //TODO: Load state from previously suspended application
24                 }
25 
26                 // Place the frame in the current Window
27                 Window.Current.Content = rootFrame;
28             }
29 
30             if (rootFrame.frame.Content == null)
31             {
32                 // When the navigation stack isn't restored navigate to the first page,
33                 // configuring the new page by passing required information as a navigation
34                 // parameter
35                 //確定應用程式初始加載的是指定的首頁
36                 NavService.NavigateTo(typeof(MainPage), e.Arguments);
37             }
38             // Ensure the current window is active
39             Window.Current.Activate();
40         }      

最後:

   代碼敲到這兒算是已經完成的差不多,現在萬事俱備,隻欠東風,注冊我們的跳轉事件,我這裡隻簡單跳轉4個頁面,腦洞大的朋友可以多設計幾個。在我們的使用者控件對應的背景代碼中為應用程式的全局菜單注冊頁面跳轉事件。

如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:
1    private void homeCmd_Tapped(object sender, TappedRoutedEventArgs e)
 2         {
 3             App.NavService.NavigateToHome();
 4         }
 5 
 6         private void articleCmd_Tapped(object sender, TappedRoutedEventArgs e)
 7         {
 8             App.NavService.NavigateTo(typeof(BlankPage1), null);
 9         }
10 
11         private void questionCmd_Tapped(object sender, TappedRoutedEventArgs e)
12         {
13             App.NavService.NavigateTo(typeof(BlankPage2), null);
14         }
15 
16         private void thingCmd_Tapped(object sender, TappedRoutedEventArgs e)
17         {
18             App.NavService.NavigateTo(typeof(BlankPage3), null);
19         }      

 好了,寫到這了算是已經大功告成了。我們還是看一下實際的運作效果吧。

如何在UWP中統一處理不同裝置間的頁面回退邏輯1、理論分析:2、代碼實作:3、總結:

  這是在PC上運作的效果,在手機上運作的效果和這類似,但是頁面回退是使用實體後退鍵來完成的,感興趣的朋友可以自行嘗試一下。

      需要指出的是,如果你在手機上運作的話,你會發現這種方法會給你額外贈送一個彩蛋:當我們需要對系統标題欄的顔色進行設定的時候,我們完全可以在我們的使用者控件中實作,哪怕我們需要填充一種圖檔或者其他複雜的元素都可以通過簡單幾行XAML代碼都是可以搞定的。

3、總結:

   這種處理方法不知能否滿足各位的某種實際需求? 需求千千萬,代碼改不斷,是以作為一個程式猿,我們不僅要提高我們的編碼能力,同時解決問題的能力也要不斷提高。這裡簡要總結一下使用到的知識:

    封裝的思想;

    使用者控件;

    架構頁;

    好像也沒啥了:)

   廢話說來這麼多,不知各位看官是否看懂???俗話說得好:實踐出真知。是以建議感興趣的朋友還是親自嘗試一下比較好。

點選下載下傳示例

  再次感謝 youngytj,Leandro指正代碼中寫的不好的部分,歡迎各位大大繼續拍磚。代碼已修改。

  

繼續閱讀