樣式如下:

這個例子可以拿來直接用。處理這種布局,主要是需要處理在PC/Mobile不同螢幕寬度下的具體顯示内容,可以使用VisualState來實作不同狀态的切換。
為了将WP8.1版本的項目快速更新到UWP版本,我沒有使用官方示例的方式,而是在MainPage裡放了兩個Frame,在左側的Frame裡放一個清單Page,右側Frame裡放一個詳情Page,這樣之前的頁面的UI和ViewModel都可以原封不動的拿過來,隻需要單獨處理VisualState的切換就可以了。
下面以一個簡單的例子來說一下是如何實作的。最近關于Win10 UWP的内容寫了不少,都是在開發的過程中,把能單獨拿出來的部分再重新做一遍demo,是以如果大家有興趣的話可以照着動手敲一遍,自己實作出來才會了解的更深入。
首先建立一個MVVM-Sidekick項目,命名為MasterDetailDemo。
添加Models目錄,建立一個NewsItem:
建立一個ItemsDataSource類,用于模拟資料,可以傳回一些資料。具體代碼看Demo裡的。
在MainPage中放置一個Grid控件,分為兩列,左側和右側分别放兩個Frame控件:
然後添加兩個頁面,MasterPage和DetailPage。
MasterPage中放置一個ListView控件,調用剛才的ItemsDataSource類,把資料綁定到ListView上,這個就不用詳述了吧。還要給ListView設定項模闆。這部分代碼就不貼了。
現在讓MainPage頁面載入時,左側的Frame自動顯示MasterPage。
打開MainPage_Model.cs檔案,取消對OnBindedViewLoad方法的注釋,修改為以下代碼:
好了,現在當MainPage頁面加載完成後,名為masterFrame的Frame會顯示MasterPage的内容,像下面這樣:
然後要實作點選項的時候,要在右側的Frame裡顯示DetailPage。
打開MasterPage.xaml,在頭部引入以下幾個命名空間:
然後修改項模闆,使用SendToEventRouterAction,這個東東在以前的Blog裡說過,在項模闆的Grid裡添加以下代碼:
在MainPage加載的時候,注冊NewsItemTapped事件,來處理點選事件。打開MainPage_Model.cs檔案,在最後添加一個RegisterCommand方法:
别忘了在Loaded事件裡調用這個方法:
因為DetailPage_Model還沒有可接收參數的構造函數,是以需要在DetailPage_Model裡加兩個構造函數,一個是無參的,一個是可接收參數的,同時還需要加一個可綁定的屬性,用來顯示内容:
這樣在DetailPage裡就可以接收到點選的是哪個NewsItem了,再綁定到界面上,我就随便放了個TextBlock:
運作一下看看,怎麼點了沒反應呢,原來在MainPage的Grid裡,第一列就把寬度占滿了,第二列無法顯示了,來給兩個列設定個寬度吧,第一列設定為2*,第二列設定為3*:
現在可以顯示了:
但是,這隻是第一步,接下來需要處理在不同螢幕寬度下的适配問題,我們可以打開UWP版的郵件,拖動視窗縮放大小,觀察頁面内容變化,可以得出以下特性:
在PC上:
1、當寬度大于一定寬度時,Master和Detail是可以同時顯示的,在剛打開程式沒有點選郵件的時候,右側的Detail實際上顯示了一個空頁面(有背景圖檔);
當逐漸縮小寬度時,又分為兩種情況:
2、如果Detail頁為空頁面時,縮小到一定寬度後,視窗隻顯示Master頁面;
3、如果Detail頁不為空,即顯示郵件正文的時候,縮小到一定寬度後,視窗隻顯示Detail頁面;
在Mobile上:
程式打開時,顯示Master頁面,相當于2;
點選郵件後,顯示Detail頁面,相當于3;
這樣我們可以得出,不管是PC還是Mobile,需要處理三種狀态的切換,我命名為:
NarrowAndBlankDetail
NarrowAndNoBlankDetail
Wide
通過處理這三種狀态的切換,就可以實作類似郵件UWP版的效果了。有些同學可能會問,為什麼不直接使用自帶的AdaptiveTrigger呢,主要是這個AdaptiveTrigger隻能根據寬度來設定,而目前的需求還需要根據Detail頁面是否為空來處理,是以需要自定義一個Trigger了。
郵件UWP預設載入的時候有一個空頁面,是以還需要添加一個BlankPage,這個頁面相當于一個空頁面,裡面可以随便放點什麼東西,比如背景圖檔啊,logo啊,或者廣告什麼的,Trigger會根據Detail頁面是否顯示這個BlankPage來進行處理。
先讓MainPage載入時,預設左側加載MasterPage,右側加載BlankPage:
好,運作效果是這樣:
現在來處理狀态切換。關于StateTrigger,國外已經有人寫了一個項目,實作了多種Trigger,見:
<a href="https://github.com/dotMorten/WindowsStateTriggers">https://github.com/dotMorten/WindowsStateTriggers</a>
這個項目實作了n個實用的Trigger,但好可惜沒有能滿足我的要求的,還是自己動手吧。我參考了他的代碼,繼承了他的接口ITriggerValue,繼承此接口的話可以用在CompositeStateTrigger裡,為了友善以後使用按照這個接口來吧。
說一下主要的代碼實作思路。
首先要定義一個枚舉:
頁面寬度的變化,通過訂閱ApplicationView.GetForCurrentView().VisibleBoundsChanged事件來處理,如果寬度大于720時如何,小于720時如何。當然這個720也可以傳遞屬性進來,我懶得弄就寫死在裡面了。
同時,還需要一個DetailContent屬性,這個屬性需要綁定到第二個Frame的Content上,這樣Trigger可以知道目前是不是BlankPage,通過以下代碼來判斷:
全部代碼如下:
View Code
我在代碼裡輸出了一些資訊,調試的時候可以觀察各種狀态是在什麼時候切換的。
然後在MainPage.xaml裡 應用這個StateTrigger,首先,要在MainPage的ViewModel裡添加一個object,用于綁定DetailFrame的内容:
MainPage.xaml裡的第二個Frame的Content綁定到這個DetailContent上:
注意Mode要設定為TwoWay,這樣才可以讓Trigger知道DetaiFrame的内容。在MainPage.xaml的根Grid裡添加以下Trigger:
還要把預設的gridMain的兩列的寬度預設值分别改為*和0:
Trigger的意義很清楚了,Setter會根據不同的狀态去設定gridMain兩列的寬度來控制MasterPage和DetailPage的顯示和隐藏:
當剛開始進入程式,左側顯示清單,右側顯示BlankPage,這時候如果寬度大于720,兩個頁面正常展示,如果頁面寬度小于720,則隻顯示清單頁;
如果頁面寬度大于720的時候,點選清單,右側正常顯示詳情;
如果頁面寬度小于720,點選清單,清單會隐藏,隻顯示詳情;
基本達到了文章開頭提出的目的。
當在手機上運作的時候,就會發現當點選清單顯示DetailPage後,再按傳回鍵直接退出程式了。因為還沒有處理傳回鍵事件。PC上也一樣,程式左上角應該有個傳回按鈕。下面來處理傳回事件。
基本思路是,點選傳回後,應該先判斷DetailPage是否可GoBack,如果可以就GoBack,直到傳回最開始的BlankPage為止,這樣StateTrigger會自動觸發NarrowAndBlankDetail狀态,顯示MasterPage。
傳回是處理SystemNavigationManager.GetForCurrentView().BackRequested這個事件,打開MainPage.xaml.cs檔案,在OnNavigatedTo裡訂閱這個事件:
使用者點選傳回鍵的時候,首先看DetailPage能否GoBack,再看MasterPage能否GoBack,當沒有可GoBack的時候就把傳回鍵隐藏。
在PC上的傳回鍵預設是隐藏的,還需要在導航到詳情頁的時候将其展示出來,修改MainPage_Model.cs檔案裡的RegisterCommand方法:
現在運作一下,PC上也可以傳回了。當第一次打開的時候,是這樣 的:
如果拖動視窗縮小,則隻會顯示MasterPage:
當點選清單項時,會隻顯示DetailPage:
點選左上角傳回鍵,又隻顯示MasterPage了。
具體切換動畫我不會截圖,大家可以下載下傳demo自己試試。
我們還可以做的更美觀一點。UWP預設的Page切換是有動畫效果的,但這裡因為隻使用StateTrigger設定了Grid的列寬,當從DetailPage傳回MasterPage的時候MasterPage一下子就顯示出來了,感覺有點生硬。現在給切換加一個動畫。
在NarrowAndBlankDetail的VisualState裡,添加一段StoryBoard:
設定透明度從0到1,同時有一個移動的效果。注意這裡的StoryBoard.TargetProperty的寫法,詳細說明可以參考MSDN文檔:
<a href="https://msdn.microsoft.com/zh-cn/library/windows/apps/windows.ui.xaml.media.animation.storyboard.targetproperty.aspx">https://msdn.microsoft.com/zh-cn/library/windows/apps/windows.ui.xaml.media.animation.storyboard.targetproperty.aspx</a>
<a href="https://msdn.microsoft.com/zh-cn/library/windows/apps/jj569302.aspx">https://msdn.microsoft.com/zh-cn/library/windows/apps/jj569302.aspx</a>
再次吐槽一下MSDN文檔真是太難找了。版本太多。
在<VisualStateManager.VisualStateGroups>裡添加Transitions:
同時要在gridMain裡添加以下代碼:
不然動畫無法起作用。
現在運作一下看看,傳回的時候MasterPage也是從左側漸變滑入的,效果好了不少。
這種方式基本可以把WP8.1的代碼直接拿過來用,頁面改動不大。如果您有更好的實作方式,歡迎留言讨論。
這篇基本就寫到這裡了。最近WP圈一片哀嚎,很多無奈的事情。但作為普通個人開發者來說,抱怨也沒用,能做多少就做多少吧,總好過隻吐槽。行動的意義永遠大于口頭空講。
本文得到了禮物說開發者鄭大神的鼎力支援。希望大家下載下傳他的禮物說,做的非常漂亮。
預祝大家新春快樂!
最後給出demo下載下傳:連結:http://pan.baidu.com/s/1hqQTbEW 密碼:ilar