在我的[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檔案夾,然後選擇Add
–New 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用於顯示漢堡包選單,是一個包含ListView
的ContentPage
。我們將使用資料系結來初始化選單項。
建立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
屬性來儲存一些選單項。為簡單起見,只有兩個字串:Contacts
和Todo
。我們還需要在建構式中註入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
,所以我們現在應該做的是設定ListView
的ItemsSource
,如下所示:
目前,我們只使用TextCell
來顯示選單文字。在我們實現漢堡包選單的全部功能之前,讓我們先來建立DetailPages。
建立DetailPages
為簡單起見,我們只新增兩個頁面作為詳細資訊頁面。
建立ViewModels
在MvxFormsMasterDetailDemo.Core專案的ViewModels
檔案夾中新增兩個名為ContactsViewModel
和TodoViewModel
的新檔案。使它們分別從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.xaml
和TodoPage.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.xaml
和TodoPage.xaml.cs
進行相同的更改。不要忘記更新Label控制元件的Text以顯示頁面名稱。
實現選單功能
現在我們有了我們需要顯示的所有頁面:名為MasterDetailPage
的根頁面,名為MenuPage
的MasterPage,以及兩個名為ContactsPage
和TodoPage
的DetailPages。接下來,我們需要使選單正常工作。
顯示MasterPage和DetailPage
開啟MasterDetailViewModel.cs
檔案並改寫ViewAppearing
方法,如下所示:
public override async void ViewAppearing()
{
base.ViewAppearing();
await _navigationService.Navigate();
await _navigationService.Navigate();
}
應用程式啟動時,ContactsPage
被用作DetailPage。因為我們為MenuPage
和ContactsPage
指定了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`型別。 ListView
的RefreshCommand
屬性也是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`。
現在讓我告訴你如何使用TapGestureRecognizer
將Command
系結到選單項。首先,設定MenuPage
的x:Name
屬性:
我們需要設定x:Name
來取用頁面的當前ViewModel。
像這樣更新ItemTemplate
:
我們對ItemTemplate
進行了一些更改:
首先,使用ViewCell
替換預設的TextCell
。ViewCell
為我們提供了更多自定義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
另外,您可能認為我們可以使用ItemSelected
或ItemTapped
事件。當然,我們可以!但不幸的是,這些事件並沒有實現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
屬性。接下來,我們需要為ListView
的SelectedItem
設定資料系結。在MvxFormsMasterDetailApp.UI專案的Pages
檔案夾中開啟MenuPage.xaml
檔案,刪除當前的ListView
控制元件,並新增一個新的ListView
,如下所示:
透過下麵的程式碼SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
,我們可以在ViewModel中的ListView
的SelectedItem
和SelectedMenuItem
屬性之間設定一個TwoWay
資料系結。
透過新增以下程式碼,在views:MvxContentPage
定義中匯入Behavior
的名稱空間:xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
。現在我們可以使用behavior
字首來使用庫中的行為。更新ListView
的XMAL,如下所示:
我為ListView
控制元件放置了一個Behaviors
。有一種稱為EventHandlerBehavior
的行為,它將由ItemSelected
事件觸發。在行為中,有一個InvokeCommandAction
,它將呼叫ViewModel中的ShowDetailPageAsyncCommand
。請註意資料系結語法。我們需要為系結指定Source
和Path
。如果你只是使用{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!