歡迎光臨
每天分享高質量文章

使用MvvmCross實現Xamarin.Forms的漢堡選單佈局

在我的[Xamarin&MvvmCross手冊](https://yanxiaodi.gitbook.io/xamarin-mvvmcross-handbook/)中,我展示了使用MvvmCross Framework開發基本Xamarin應用程式的基礎知識。在開發真實應用程式時需要考慮更多細節,例如佈局,樣式和資料庫等。例如,漢堡選單佈局是現代移動應用程式中非常常見的導航樣式。我們可以使用MasterDetail導航樣式來實現漢堡選單。接下來,我將向您展示如何在Xamarin.Forms應用程式中實現MasterDetail佈局。在開始之前,我建議您閱讀有關MasterDetailPage的官方檔案:[Xamarin.Forms Master-Detail Page](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/master-detail-page)。

我的開發環境如下所示:

  • Windows 10 version 10.0.17134
  • Visual Studio 2017 version 15.9.4
  • Xamarin.Forms version 3.4.0.1008975
  • MvvmCross version 6.2.2

讓我們開始吧。

透過MvxScaffolding建立專案

如果您是MvvmCross的新手,使用MvvmCross建立Xamarin應用程式可能有點棘手。幸運的是,我們有一些專案模板來簡化我們的工作。您可以在官方檔案中找到它們:[MvvmCross入門](https://www.mvvmcross.com/documentation/getting-started/getting-started)。我建議你使用這個:[MvxScaffolding](https://github.com/Plac3hold3r/MvxScaffolding)它是新的,支援.net標準。您可以透過單擊VS 2017中的工具 – 擴充套件和更新來搜尋它,如下所示:

安裝後,您可以在MvvmCross類別中建立一個新的Xamarin.Forms應用程式:

輸入MvxFormsMasterDetailDemo作為專案名稱。 MvxScaffolding為我們提供了一個非常友好的介面來定製應用程式。為了更好地理解,我們選擇Blank模板,如下所示:

預設設定不包含UWP專案。如果您需要支援UWP平臺,請選擇它,並選擇Min SDK版本為1803.由於舊的Windows 10版本不支援某些新功能,因此建議使用較新的版本。此外,您需要輸入描述作為UWP應用程式名稱。

單擊“NEXT”按鈕,您將看到一個摘要視窗。檢查所有資訊,然後單擊“DONE”按鈕。 MvxScaffolding將生成一個具有良好結構的基本空白Xamarin.Forms應用程式。

建立MasterDetailPage

MasterDetailPage是應用程式的根頁面。實際上,它是MasterDetailPage類的一個實體。它不應該用作子頁面,以確保在不同平臺上都能有一致的使用者體驗。

建立ViewModel

接下來,在MvxFormsMasterDetailDemo.Core專案的ViewModels檔案夾中新增一個名為MasterDetailViewModel的新類檔案。將其更改為繼承自MvxViewModel類。通常,我們還需要使用NavigationService來實現ViewModel中的導航。因此,使用依賴註入註入IMvxNavigationService的實體:

using MvvmCross.Navigation;
using MvvmCross.ViewModels;

namespace MvxFormsMasterDetailDemo.Core.ViewModels
{
public class MasterDetailViewModel : MvxViewModel
{
readonly IMvxNavigationService _navigationService;

public MasterDetailViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
}
}

建立XAML檔案

Xamarin.Forms為我們提供了一些導航樣式,包括分層導航,選項卡式頁面,MasterDetailPage和模態頁面等。根據我們的要求,我們希望在主頁面上有一個漢堡選單。所以我們可以使用MasterDetailPage,它是應用程式的根頁面,包含兩個區域:左邊是MasterPage,右邊是DetailPage。我們可以將選單放在MasterPage中。單擊選單項時,導航服務將在DetailPage區域中顯示另一頁。

在MvvmCross中,Xamarin.Forms中提供了不同的“MvxFromsPagePresenter”來對應不同頁面型別,用於定義檢視的顯示方式。我們使用MvxPagePresentationAttribute來指定不同的頁面型別。有關更多詳細資訊,請在此處檢視檔案:[Xamarin.Forms View Presenter](https://www.mvvmcross.com/documentation/platform/xamarin.forms/xamarin-forms-view-presenter)。

開啟MvxFormsMasterDetailDemo.Core專案中的App.cs檔案。請註意,框架將啟動HomeViewModel作為第一頁。現在讓我們建立一個MasterDetailPage並用它來替換第一頁。

右鍵單擊MvxFormsMasterDetailDemo.UI專案中的Pages檔案夾,然後選擇AddNew Item。從Xamarin.Forms類別中選擇“Content Page”,如下所示:

開啟MasterDetailPage.xaml檔案。請註意,此頁面是一個“ContentPage”。我們需要將其更改為繼承自MvxMasterDetailPage。用以下程式碼替換XAML程式碼:



我們使用MvxMasterDetailPage來替換預設的ContentPage型別。為此,我們需要新增以下程式碼:

xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"

要設定MasterDetailPage的ViewModel,我們需要指定x:TypeArguments的值為viewModels:MasterDetailViewModel。不要忘記透過新增xmlns:viewModels =“clr-namespace:MvxFormsMasterDetailDemo.Core.ViewModels; assembly = MvxFormsMasterDetailDemo.Core”來匯入viewModels名稱空間。

開啟MasterDetailPage.xaml.cs檔案,將它的基類從ContentPage替換為MvxMasterDetailPage。將“MvxMasterDetailPagePresentation”屬性新增到類中,如下麵的程式碼:

using MvvmCross.Forms.Presenters.Attributes;
using MvvmCross.Forms.Views;
using MvxFormsMasterDetailDemo.Core.ViewModels;
using Xamarin.Forms.Xaml;

namespace MvxFormsMasterDetailDemo.UI.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[MvxMasterDetailPagePresentation(Position = MasterDetailPosition.Root, WrapInNavigationPage = false, Title = "MasterDetail Page")]
public partial class MasterDetailPage : MvxMasterDetailPage
{
public MasterDetailPage()
{
InitializeComponent();
}
}
}

我們來看看MvxMasterDetailPagePresentation屬性。 “MvxMasterDetailPagePresentation”有一些非常重要的屬性。Position是一個列舉值,用於表示頁面的型別,這裡是Root。請設定如圖所示的其他屬性,否則,您可能會得到一些奇怪的結果。

建立MasterPage

MasterPage用於顯示漢堡包選單,是一個包含ListViewContentPage。我們將使用資料系結來初始化選單項。

建立ViewModel

在MvxFormsMasterDetailDemo.Core專案的ViewModels檔案夾中建立一個MenuViewModel類。使用以下程式碼替換內容:

using System.Collections.ObjectModel;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;

namespace MvxFormsMasterDetailDemo.Core.ViewModels
{
public class MenuViewModel : MvxViewModel
{
readonly IMvxNavigationService _navigationService;

public MenuViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
MenuItemList = new MvxObservableCollection()
{
"Contacts",
"Todo"
};
}

        #region MenuItemList;
private ObservableCollection _menuItemList;
public ObservableCollection MenuItemList
{
get => _menuItemList;
set => SetProperty(ref _menuItemList, value);
}
        #endregion
}
}

它有一個MenuItemList屬性來儲存一些選單項。為簡單起見,只有兩個字串:ContactsTodo。我們還需要在建構式中註入IMvxNavigationService的實體。

建立XAML檔案

接下來,將名為MenuPage.xaml的新ContentPage新增到MvxFormsMasterDetailDemo.UI專案的Pages檔案夾中。開啟MenuPage.xaml檔案並用以下程式碼替換內容:








開啟MenuPage.xaml.cs檔案並設定基類和屬性,如下所示:

using MvvmCross.Forms.Presenters.Attributes;
using MvvmCross.Forms.Views;
using MvxFormsMasterDetailDemo.Core.ViewModels;
using Xamarin.Forms.Xaml;

namespace MvxFormsMasterDetailDemo.UI.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
    [MvxMasterDetailPagePresentation(Position = MasterDetailPosition.Master, WrapInNavigationPage = false, Title = "HamburgerMenu Demo")]
public partial class MenuPage : MvxContentPage
    {
        public MenuPage ()
        {
            InitializeComponent ();
        }
    }
}

MvxMasterDetailPagePresentation屬性的Position屬性應設定為Master,這意味著該頁面將顯示為MasterDetailPage的MasterPage。 MasterPage還有另一個問題:它必須設定Title屬性,否則,你的應用程式將被卡住。因此,您必須設定MvxMasterDetailPagePresentation屬性的Title屬性。

現在我們需要為ListView設定資料系結。我們的ViewModel中已經有一個MenuItemList,所以我們現在應該做的是設定ListViewItemsSource,如下所示:







目前,我們只使用TextCell來顯示選單文字。在我們實現漢堡包選單的全部功能之前,讓我們先來建立DetailPages。

建立DetailPages

為簡單起見,我們只新增兩個頁面作為詳細資訊頁面。

建立ViewModels

在MvxFormsMasterDetailDemo.Core專案的ViewModels檔案夾中新增兩個名為ContactsViewModelTodoViewModel的新檔案。使它們分別從MvxViewModel類繼承:

using MvvmCross.ViewModels;

namespace MvxFormsMasterDetailDemo.Core.ViewModels
{
public class ContactsViewModel : MvxViewModel
{
}
}

 

using MvvmCross.ViewModels;

namespace MvxFormsMasterDetailDemo.Core.ViewModels
{
public class TodoViewModel : MvxViewModel
{
}
}

建立XAML檔案

將兩個ContentPage檔案新增到MvxFormsMasterDetailDemo.UI專案的Pages檔案夾中,並將它們命名為ContactsPage.xamlTodoPage.xaml。要使用MvvmCross的功能,我們需要將它們更改為繼承自MvxContentPage。開啟ContactsPage.xaml檔案並使用以下程式碼替換內容:








Label控制元件用於指示當前頁面。

開啟ContactsPage.xaml.cs檔案並更新為如下內容:

using MvvmCross.Forms.Presenters.Attributes;
using MvvmCross.Forms.Views;
using MvxFormsMasterDetailDemo.Core.ViewModels;
using Xamarin.Forms.Xaml;

namespace MvxFormsMasterDetailDemo.UI.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
    [MvxMasterDetailPagePresentation(Position = MasterDetailPosition.Detail, NoHistory = true, Title = "Contacts Page")]
public partial class ContactsPage : MvxContentPage
    {
        public ContactsPage ()
        {
            InitializeComponent ();
        }
    }
}

Position屬性的值是MasterDetailPosition.Detail,這意味著該頁面應該位於MasterDetailPage的Detail區域。 NoHistory屬性應該是true,這樣可以避免對不同平臺的導航出現奇怪的行為。 Title屬性用於在頁面頂部顯示頁面名稱。

相應地對TodoPage.xamlTodoPage.xaml.cs進行相同的更改。不要忘記更新Label控制元件的Text以顯示頁面名稱。

實現選單功能

現在我們有了我們需要顯示的所有頁面:名為MasterDetailPage的根頁面,名為MenuPage的MasterPage,以及兩個名為ContactsPageTodoPage的DetailPages。接下來,我們需要使選單正常工作。

顯示MasterPage和DetailPage

開啟MasterDetailViewModel.cs檔案並改寫ViewAppearing方法,如下所示:

public override async void ViewAppearing()
{
base.ViewAppearing();
await _navigationService.Navigate();
await _navigationService.Navigate();
}

應用程式啟動時,ContactsPage被用作DetailPage。因為我們為MenuPageContactsPage指定了MvxMasterDetailPagePresentation屬性,所以MvvmCross會找到並將其顯示在正確位置。

開啟MvxFormsMasterDetailDemo.Core專案中的App.cs檔案,並用MasterDetailPage替換第一頁:

public class App : MvxApplication
{
public override void Initialize()
{
RegisterAppStart();
}
}

現在我們可以為三個平臺啟動應用程式:

Android:

預設檢視很好。 Xamarin.Forms會自動在頁面左上角新增一個漢堡包圖示按鈕。當我們單擊按鈕時,選單能夠顯示,但沒有頂部標題欄。我們稍後會調整UI。

iOS:

iOS的預設檢視與Android不同。頁面上沒有漢堡包圖示。還有另一個問題與Android相同,也是關於MenuPage標題欄的。看起來我們需要新增一個漢堡圖示並顯示標題欄。我們稍後會這樣做。

UWP:

發生了什麼?MasterPage自動顯示,但沒有預設的漢堡包按鈕。

有關MasterDetailPage導航行為的詳細資訊,請在此處閱讀:[MasterDetailPage Overview](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/master-detail-page#概述)。根據檔案,母版頁應該有一個包含按鈕的導航欄。但現在我們得到了一些不同的結果。無論如何,我們可以自己解決它。

要修複UWP的佈局,只需設定MasterDetailPage的MasterBehavior屬性即可。它是一個列舉值,用於確定詳細資訊頁面在MasterDetailPage中的顯示方式。如果將其設定為“Default”,它將分別為不同平臺顯示DetailPage。這就是我們得到不同結果的原因。

開啟MvxFormsMasterDetailDemo.UI專案中的MasterDetailPage.xaml檔案。在頁面定義中新增MasterBehavior屬性並將其設定為Popover,這意味著DetailPage將改寫或部分改寫MasterPage:




要檢視效果,請執行UWP專案,它看起來像這樣:

在頁面左上方有預設的漢堡包按鈕。執行Android和iOS專案以確保所有內容都不會因輕微更改而受影響。您可能會註意到這三個平臺之間仍存在一些差異。例如,UWP專案有標題欄,但Android和iOS沒有。Android和UWP有預設的漢堡包按鈕,但iOS沒有。我們稍後會修複它們。

設定選單導航

單擊選單項時,應用程式應顯示了正確的DetailPage。現在讓我們為選單項設定Command。開啟MvxFormsMasterDetailDemo.Core專案中的MenuViwModel.cs檔案,並新增一個命令,如下所示:

#region ShowDetailPageAsyncCommand;
private IMvxAsyncCommand _showDetailPageAsyncCommand;
public IMvxAsyncCommand ShowDetailPageAsyncCommand
{
get
{
_showDetailPageAsyncCommand = _showDetailPageAsyncCommand ?? new MvxAsyncCommand(ShowDetailPageAsync);
return _showDetailPageAsyncCommand;
}
}
private async Task ShowDetailPageAsync(string param)
{
// Implement your logic here.
}
#endregion

This is a instance of IMvxAsyncCommand, which contains a parameter from the data-binding. The type of the parameter is string, because we know the object in the MenuItemList is string. If you have some other generic types for the MenuItemList , remember to change the generic type to your real item type.
這是IMvxAsyncCommand的一個實體,包含來自資料系結的引數。引數的型別是string,因為我們知道MenuItemList中的物件是string。如果您的MenuItemList包含其他泛型型別,請記住將泛型型別更改為您的實際項的型別。

然後我們需要為命令設定資料系結。開啟MvxFormsMasterDetailDemo.UI專案中的MenuPage.xaml檔案並檢查當前的ItemTemplate







這裡我們只使用一個簡單的TextCell來顯示選單文字。如何將命令系結到ListView

在我們開始資料系結之前,我建議你閱讀這篇文章:[Xamarin.Forms命令介面](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/commanding)。在Xamarin.Forms中,一些控制元件原生支援`Command`,例如`Button`,`MenuItem`,`TextCell`以及從它們派生的一些類。並且,`SearchBar`也支援`SearchCommand`屬性,實際上是一個`ICommand`型別。 ListViewRefreshCommand屬性也是ICommand介面的一個實體。

對於那些不直接支援ICommand的控制元件,Xamarin.Forms提供了一個TapGestureRecognizer來支援Command系結。有關詳細資訊,請閱讀以下文章:[新增點選手勢識別器](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/tap)。請記住,儘管`GestureRecognizer`支援更多手勢,例如`pinch`,`pan`和`swipe`,但只有`TapGestureRecognizer`支援`ICommand`。另一個限制是view元素必須支援`GestureRecognizers`。

現在讓我告訴你如何使用TapGestureRecognizerCommand系結到選單項。首先,設定MenuPagex:Name屬性:

我們需要設定x:Name來取用頁面的當前ViewModel。

像這樣更新ItemTemplate













我們對ItemTemplate進行了一些更改:

首先,使用ViewCell替換預設的TextCellViewCell為我們提供了更多自定義UI的靈活性。所以我們可以根據需要定義ItemTemplate。例如,我們可能會為每個選單項新增一個圖示。

ViewCell元素中,使用StackLayout控制元件作為容器,它支援GestureRecognizers,因此我們可以將TapGestureRecognizer新增到StackLayout

In the TapGestureRecognizer element, I define two important properties, one is Command, another is CommandParameter. As I said in my previous articles, you must be very clear about your current DataContext that you bind to your view. For our case, I must find the command named ShowDetailPageAsyncCommand in the MenuViewModel, so I use Source={x:Reference MainContent} to get the source object, which is the current Page named MainContent. Now we can get the ViewModel of the page by BindingContext.DataContext, then use BindingContext.DataContext.ShowDetailPageAsyncCommand as the binding path. I have been a little bit confused about BindingContext.DataContext because it is different from the syntax in UWP. Notice that the full syntax is:
TapGestureRecognizer元素中,我定義了兩個重要的屬性,一個是Command,另一個是CommandParameter。正如我在之前的文章中所說,你必須非常清楚你係結到檢視的當前的DataContext。對於我們的情況,我必須在MenuViewModel中找到名為ShowDetailPageAsyncCommand的命令,所以我使用Source={x:Reference MainContent}來獲取源物件,這是當前名為MainContent的頁面。現在我們可以透過BindingContext.DataContext獲取頁面的ViewModel,然後使用BindingContext.DataContext.ShowDetailPageAsyncCommand作為系結路徑。我對BindingContext.DataContext有點困惑,因為它與UWP中的語法不同。請註意,完整語法是:

Command="{Binding Path=BindingContext.DataContext.ShowDetailPageAsyncCommand, Source={x:Reference MainContent}}"

當您把Path放在命令系結的第一個引數的位置時,它可以被刪除。

對於CommandParameter,它更容易。只需將當前字串系結到它。所以語法是CommandParameter="{Binding}"。如果您使用包含某些屬性的物件,只需使用CommandParameter="{Binding YourProperty}"

接下來,讓我們更新命令。再次在MvxFormsMasterDetailApp.Core專案的ViewModels檔案夾中開啟MenuViewModel.cs檔案,並完成ShowDetailPageAsync方法,程式碼如下:

private async Task ShowDetailPageAsync(string param)
{
// Implement your logic here.
switch (param)
{
case "Contacts":
await mvxNavigationService.Navigate();
break;
case "Todo":
await mvxNavigationService.Navigate();
break;
default:
break;
}
}
#endregion

此方法接收來自命令系結的引數。因此,我們可以確定哪個頁面應顯示為DetailPage。

現在啟動應用程式並觀察導航行為。還有一個問題。單擊選單項時,雖然DetailPage顯示正確,但MenuPage仍改寫DetailPage。所以我們必須控制MasterPage的導航行為。為此,我們需要將Xamarin.Forms安裝到MvxFormsMasterDetailDemo.Core專案。您可以透過在NuGet包管理器中搜索Xamarin.Forms來安裝它。請與其他專案安裝相同版本的Xamarin.Forms以避免取用錯誤。對於我的演示解決方案,我使用Xamarin.Forms.3.4.0.1008975

switch程式碼段後面的ShowDetailPageAsync方法中新增一些程式碼:

if (Application.Current.MainPage is MasterDetailPage masterDetailPage)
{
masterDetailPage.IsPresented = false;
}
else if (Application.Current.MainPage is NavigationPage navigationPage
&& navigationPage.CurrentPage is MasterDetailPage nestedMasterDetail)
{
nestedMasterDetail.IsPresented = false;
}

IsPresented用於控制是否顯示母版頁。要隱藏MasterPage,請將其設定為“false”。有關更多詳細資訊,請在此處閱讀:[建立和顯示詳細資訊頁面](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/master-detail-page#creating-and-displaying-the-detail-page)。

啟動所有三個平臺的應用程式,以確保選單正常工作。

設定資料系結的其他方法

使用TextCell的內建命令

XAML世界的資料系結機制是靈活的。實際上,我們有多種方法來實現我們的標的。例如,如果您只使用TextCell來顯示選單項,則有一種簡單的方法可以進行導航。正如我在上一節中所說,TextCell原生支援ICommand。所以我們可以使用這樣的資料系結語法:


    

這種方式更容易。當使用者點選TextCell時,它將觸發Command。但缺點是您無法自定義選單項的UI。 TextCell僅支援文字。如果要新增一些影象或定義複雜的專案佈局,則必須使用ViewCell

使用Bahaviors

另外,您可能認為我們可以使用ItemSelectedItemTapped事件。當然,我們可以!但不幸的是,這些事件並沒有實現ICommand介面,所以我們不能直接使用資料系結。要使用ICommand系結,我們需要使用Behavior將事件轉換為命令,如下所述:[Reusable EventToCommandBehavior](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/behaviors/reusable/event-to-command-behavior)。

您可能不熟悉Behaviors。Behaviors來自Blend SDK,它是XAML世界中非常有用的庫。可以將這些行為附加到某些控制元件並偵聽某些事件,然後在ViewModel中呼叫某些命令。這是為那些不是為了與命令互動而設計的控制元件新增對Command樣式的支援的好方法。因此,我們可以優雅地使用MVVM樣式,而不是在程式碼隱藏檔案中使用事件處理程式。

您可以按照官方檔案中的說明建立EventToCommandBehavior,但我們可以利用第三方庫快速完成:[Behaviors.Xamarin.Forms.Netstandard](https://github.com/1iveowl/Behaviors.Xamarin.Forms.Netstandard)。它不是官方專案,但易於使用。您可以透過NuGet包管理器搜尋`Behaviors.Xamarin.Forms`將其安裝到MvxFormsMasterDetailApp.UI專案:

We can use this library to enable the ListView control to trigger our command in the ViewModel when selecting the item. To do this, add a bindable property called SelectedMenuItem in the MenuViewModel.cs file, which is used to indicate the current selected item, like this:
我們可以使用這個庫來啟用ListView控制元件,在選擇項時在ViewModel中觸發我們的命令。為此,在MenuViewModel.cs檔案中新增一個名為SelectedMenuItem的可系結屬性,該屬性用於指示當前所選項,如下所示:

#region SelectedMenuItem;
private string _selectedMenuItem;
public string SelectedMenuItem
{
get => _selectedMenuItem;
set => SetProperty(ref _selectedMenuItem, value);
}
#endregion

用下麵的程式碼替換我們在上一節中建立的ShowDetailPageAsyncCommand區域:

        #region ShowDetailPageAsyncCommand;
private IMvxAsyncCommand _showDetailPageAsyncCommand;
public IMvxAsyncCommand ShowDetailPageAsyncCommand
{
get
{
_showDetailPageAsyncCommand = _showDetailPageAsyncCommand ?? new MvxAsyncCommand(ShowDetailPageAsync);
return _showDetailPageAsyncCommand;
}
}
private async Task ShowDetailPageAsync()
{
// Implement your logic here.
switch (SelectedMenuItem)
{
case "Contacts":
await _navigationService.Navigate();
break;
case "Todo":
await _navigationService.Navigate();
break;
default:
break;
}
if (Application.Current.MainPage is MasterDetailPage masterDetailPage)
{
masterDetailPage.IsPresented = false;
}
else if (Application.Current.MainPage is NavigationPage navigationPage
&& navigationPage.CurrentPage is MasterDetailPage nestedMasterDetail)
{
nestedMasterDetail.IsPresented = false;
}
}
        #endregion

你找到了區別嗎?我從命令中刪除了引數,併在ShowDetailPageAsync方法中使用了SelectedMenuItem屬性。接下來,我們需要為ListViewSelectedItem設定資料系結。在MvxFormsMasterDetailApp.UI專案的Pages檔案夾中開啟MenuPage.xaml檔案,刪除當前的ListView控制元件,並新增一個新的ListView,如下所示:







透過下麵的程式碼SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}",我們可以在ViewModel中的ListViewSelectedItemSelectedMenuItem屬性之間設定一個TwoWay資料系結。

透過新增以下程式碼,在views:MvxContentPage定義中匯入Behavior的名稱空間:xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"。現在我們可以使用behavior字首來使用庫中的行為。更新ListView的XMAL,如下所示:












我為ListView控制元件放置了一個Behaviors。有一種稱為EventHandlerBehavior的行為,它將由ItemSelected事件觸發。在行為中,有一個InvokeCommandAction,它將呼叫ViewModel中的ShowDetailPageAsyncCommand。請註意資料系結語法。我們需要為系結指定SourcePath。如果你只是使用{Binding ShowDetailPageAsyncCommand},它將無法正常工作。因此,請註意控制元件的當前BindingContext

執行三個平臺的應用程式,您將看到它按預期工作。您可以選擇任何方法來實現選單功能。我只想告訴你如何以不同的方式做到這一點。也許你會將它們用於其他場景。

微調UI

不同平臺的UI存在一些缺陷。例如,iOS的標題欄和漢堡選單圖示不如我們預期的那麼好。讓我們解決它們。

為iOS新增漢堡包圖示

根據MasterDetailPage的官方檔案,我認為iOS也應該顯示像Android和UWP這樣的按鈕,但事實並非如此。我們可以為MasterPage設定一個Icon屬性。

下載影象檔案[這裡](https://github.com/yanxiaodi/MvvmCrossDemo/blob/master/MvxFormsMasterDetailDemo/src/MvxFormsMasterDetailDemo.iOS/Resources/hamburger.png)。將其貼上到MvxFormsMasterDetailDemo.iOS專案的`Resources`檔案夾中。如果沒有這樣的檔案夾,請建立一個。影象的`Build Action屬性應該是BundleResource`。

開啟MvxFormsMasterDetailApp.UI專案的Pages檔案夾中的MenuPage.xaml檔案。將以下程式碼新增到views:MvxContentPage部分:Icon="hamburger.png"。現在啟動iOS應用程式:

那很好!

新增Android和iOS的標題欄

UWP將為MasterPage新增預設標題欄。對於Android和iOS,我們需要分別定義它。

為了為不同的平臺提供一些特定的值,我們可以使用Device類,它包含許多屬性和方法,可以幫助我們自定義特定平臺的佈局和功能。您可以在此處閱讀有關它的詳細資訊:[Xamarin.Forms Device Class](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/device)。

根據我們的要求,我們只需要為Android和iOS新增標題欄。開啟MvxFormsMasterDetailDemo.UI專案的Pages檔案夾中的MenuPage.xaml檔案。在ListView定義之前新增以下程式碼:









實際上,OnPlatform標記正在做一些看似在程式碼中建立switch陳述句的東西。它包含幾個On類,用於接收指示當前平臺的Platform屬性。有一些不同的值來識別不同的平臺:“iOS”,“Android”,“UWP”和“macOS”。所以我們可以建立一個StackLayout,它包含一個Label控制元件,透過設定它的IsVisible屬性來顯示Android和iOS的應用程式名稱。但對於UWP來說,它是不可見的。這意味著新增程式碼不會對UWP進行任何更改。

執行適用於Android和iOS的應用。它適用於Android。但在iOS平臺上,標題欄稍微改寫了手機的狀態列,如下所示:

我們可以為StackLayout新增一些MarginiOS新增另一個OnPlatform標記,如下所示:





它隻影響iOS的UI。現在來看看所有平臺:

iOS:

Android:

UWP:

好的,一切都很好,除了UWP的串列項高度……

調整UWP專案的高度

您可能會註意到,如果我們使用TextCell作為ListView的項模板,那麼Android和iOS的ListView中的專案都有預設的邊距和樣式。但對於UWP平臺,專案沒有預設樣式和適當高度。讓我們定義專案模板的樣式。同時,我們應該確保它適用於每個平臺。

開啟MvxFormsMasterDetailDemo.UI專案的Pages檔案夾中的MenuPage.xaml檔案。透過以下程式碼更新ItemTemplate









這就對了。最後,整個檔案看起來像這樣:





































現在是時候為所有三個平臺啟動應用程式並觀察最終結果!現在,該應用程式顯示三個平臺的正確漢堡選單,它具有適當的邊距和樣式。

小結

在本文中,我向您展示瞭如何透過Xamarin.Forms和MvvmCross Framework為iOS,Android和UWP建立基本的漢堡選單佈局。我不是專業設計師,因此您可能需要為自己的應用程式微調樣式。我希望您可以按照這些步驟建立一個乾凈,優雅的MVVM架構的漢堡選單佈局。另外,我希望你能從我的演示中獲得資料系結基礎知識。請記住,實現相同標的可能有多種方法,而我的實施並不是最好的方法。實際上,我認為為每個專案新增一個圖示會更好!如果您找到更好的解決方案,請留下評論併進行討論。

你可以在我的GitHub上找到repo:[MvxFormsMasterDetailDemo](https://github.com/yanxiaodi/MvvmCrossDemo/tree/master/MvxFormsMasterDetailDemo)。Happy coding!

    贊(0)

    分享創造快樂