在我的[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!