走進 Prism for Xamarin.Forms
一、使用環境
OS:Win 10 16273
VS:VS2017- 15.3.4
Xamarin:4.6.3.4,nuget:2.4
Android Emulator:Visual Studio for Android Emulator(相比 Android Emulator不用下載SDK,而且啟動快)
二、安裝 Prism 模塊
工具——擴展和更新——搜索 Prism Template Pack——安裝
三、開始搞起
1.先建個項目
2.添加頁面
Views文件夾右鍵——添加——新建項,彈出來的對話框先選中左邊的 Prism 節點
確定後,你會發現 App.xaml.cs 文件裏註入了新建的頁面, ViewModels 文件夾下也多出了 ViewModel ,Views 新添加的文件也是和 ViewModel 綁定好的
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="SD.Xamarin.Views.LoginPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" Title="Login" prism:ViewModelLocator.AutowireViewModel="True"> <ContentPage.ToolbarItems> <ToolbarItem Text="Regist" /> </ContentPage.ToolbarItems> <StackLayout Padding="20" Spacing="20" VerticalOptions="Center"> <Entry Placeholder="Username" Text="{Binding Username}" /> <Entry IsPassword="true" Placeholder="Password" Text="{Binding Password}" /> <Button BackgroundColor="#77D065" Command="{Binding LoginCommand}" Text="Login" TextColor="White" /> </StackLayout> </ContentPage>
public class LoginPageViewModel : BindableBase { private readonly INavigationService _navigationService; private readonly IEventAggregator _eventAggregator; private readonly IPageDialogService _pageDialogService; private string _username; public string Username { get { return _username; } set { _username = value; RaisePropertyChanged(); } } private string _password; public string Password { get { return _password; } set { _password = value; RaisePropertyChanged(); } } private ICommand _loginCommand; public ICommand LoginCommand { get { return _loginCommand ?? new DelegateCommand(Login); } set { _loginCommand = value; } } public LoginPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator, IPageDialogService pageDialogService) { _navigationService = navigationService; _eventAggregator = eventAggregator; _pageDialogService = pageDialogService; } private async void Login() { if (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)) { await _navigationService.NavigateAsync(nameof(DataCabinPage)); } else { await _pageDialogService.DisplayAlertAsync("Error", "Wrong Username or Password", "OK!"); } } }
3.添加一個 Master 頁面作為主頁面
<?xml version="1.0" encoding="utf-8" ?> <MasterDetailPage x:Class="SD.Xamarin.Views.MasterPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:behaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" xmlns:views="clr-namespace:SD.Xamarin.Views;assembly=SD.Xamarin" Title="Master" prism:ViewModelLocator.AutowireViewModel="True"> <MasterDetailPage.Master> <NavigationPage Title="Required Foo" Icon="hamburger.png"> <x:Arguments> <views:DataCabinPage /> </x:Arguments> </NavigationPage> </MasterDetailPage.Master> </MasterDetailPage>
Master裏的子頁面
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="SD.Xamarin.Views.DataCabinPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:behaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" Title="DataCabin" prism:ViewModelLocator.AutowireViewModel="True"> <ContentPage.ToolbarItems> <ToolbarItem Command="GoBackCommand" Text="Back" /> </ContentPage.ToolbarItems> <ListView x:Name="listView" CachingStrategy="RecycleElement" GroupDisplayBinding="{Binding Key}" GroupShortNameBinding="{Binding Key}" IsGroupingEnabled="True" ItemsSource="{Binding DataCabinsGrouped}" SelectedItem="{Binding SelectedDataCabin}"> <ListView.Behaviors> <behaviors:EventToCommandBehavior Command="{Binding ItemTappedCommand}" EventName="ItemTapped" /> </ListView.Behaviors>
<ListView.GroupHeaderTemplate> <DataTemplate> <ViewCell> <StackLayout Orientation="Horizontal"> <Image Source="hamburger.png" /> <Label FontSize="18" Text="{Binding Key}" TextColor="DeepSkyBlue" VerticalTextAlignment="Center" /> </StackLayout> </ViewCell> </DataTemplate> </ListView.GroupHeaderTemplate> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Label Text="{Binding Name}" TextColor="White" VerticalTextAlignment="Center" /> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </ContentPage>
public class DataCabinPageViewModel : BindableBase { private readonly INavigationService _navigationService; private readonly IEventAggregator _eventAggregator; private readonly IPageDialogService _pageDialogService; private DataCabinModel _selectedDataCabin; public DataCabinModel SelectedDataCabin { get { return _selectedDataCabin; } set { _selectedDataCabin = value; RaisePropertyChanged(); } } private ObservableCollection<DataCabinModel> _dataCabins; public ObservableCollection<DataCabinModel> DataCabins { get { return _dataCabins; } set { _dataCabins = value; RaisePropertyChanged(); } } private ObservableCollection<GroupingModel<string, DataCabinModel>> _dataCabinsGrouped; public ObservableCollection<GroupingModel<string, DataCabinModel>> DataCabinsGrouped { get { return _dataCabinsGrouped; } set { _dataCabinsGrouped = value; RaisePropertyChanged(); } } private ICommand _itemTappedCommand; public ICommand ItemTappedCommand { get { return _itemTappedCommand ?? new DelegateCommand(ItemTapped); } set { _itemTappedCommand = value; } } private ICommand _goBackCommand; public ICommand GoBackCommand { get { return _goBackCommand ?? new DelegateCommand(GoBack); } set { _goBackCommand = value; } } public DataCabinPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator, IPageDialogService pageDialogService) { _navigationService = navigationService; _eventAggregator = eventAggregator; _pageDialogService = pageDialogService; DataCabins = new ObservableCollection<DataCabinModel>() { new DataCabinModel(){Id=1,Name = "T1",GroupName="G1",DisplayType= DataCabinType.Chart}, new DataCabinModel(){Id=2,Name = "T2",GroupName="G1",DisplayType= DataCabinType.Grid}, new DataCabinModel(){Id=3,Name = "T3",GroupName="G2",DisplayType= DataCabinType.Guage}, new DataCabinModel(){Id=4,Name = "T4",GroupName="G2",DisplayType= DataCabinType.Map} }; var grouped = from menuItem in DataCabins orderby menuItem.Id group menuItem by menuItem.GroupName into menuItemGroup select new GroupingModel<string, DataCabinModel>(menuItemGroup.Key, menuItemGroup); DataCabinsGrouped = new ObservableCollection<GroupingModel<string, DataCabinModel>>(grouped); } private async void ItemTapped() { switch (SelectedDataCabin.DisplayType) { case DataCabinType.Chart: await _navigationService.NavigateAsync("/Master/Navigation/" + nameof(ChartPage)); break; case DataCabinType.Grid: await _navigationService.NavigateAsync("/Master/Navigation/" + nameof(GridPage)); break; case DataCabinType.Guage: await _navigationService.NavigateAsync("/Master/Navigation/" + nameof(GuagePage)); break; case DataCabinType.Map: await _navigationService.NavigateAsync("/Master/Navigation/" + nameof(MapPage)); break; default: throw new ArgumentOutOfRangeException(); } } private void GoBack() { _navigationService.NavigateAsync(nameof(DataCabinPage)); } }
App.xaml
public partial class App : PrismApplication { public App(IPlatformInitializer initializer = null) : base(initializer) { } protected override void OnInitialized() { InitializeComponent(); NavigationService.NavigateAsync(nameof(LoginPage)); } protected override void RegisterTypes() { Container.RegisterTypeForNavigation<NavigationPage>("Navigation"); Container.RegisterTypeForNavigation<RegistPage>(); Container.RegisterTypeForNavigation<LoginPage>(); Container.RegisterTypeForNavigation<MasterPage>("Master"); Container.RegisterTypeForNavigation<ChartPage>(); Container.RegisterTypeForNavigation<GridPage>(); Container.RegisterTypeForNavigation<GuagePage>(); Container.RegisterTypeForNavigation<MapPage>(); Container.RegisterTypeForNavigation<DataCabinPage>(); } }
這是最終的 App 文件,註意其中的NavigationPage 和MasterPage 後邊都加了參數,用來導航用的,因為想要漢堡包樣式
漢堡包的圖片是從官方例子復制的,需要放到
Android:
IOS:直接 Resources 文件夾下
導航的寫法 await _navigationService.NavigateAsync("/Master/Navigation/" + nameof(ChartPage)); 這裏就是App.xaml.cs 文件裏註冊時的那個參數,本來想把前邊也寫出nameof 的方式,但是發現直接失敗了,就只能這樣了
其他的代碼都很建單,也沒寫什麽邏輯,就不貼了,大概就是這個樣子,嗯,下一步就要引入 syncfusion 的控件才行了,這樣才好看,也能有很多控件用(主要是實在不知道寫什麽業務)
動態圖
四、模擬器
工具——Visual Studio Emulator for Android 彈出的裏邊選擇一個下載就好了,是基於Hyper-V 的,需要確定你的機器支持
窗口——其他窗口——Xamarin.Forms Previewer 也是可以預覽的,但是用了Prism 後,App.xaml.cs 裏的構造函數變了,然後就顯示不了了~~
五、遇到的問題
1.F5 運行後,執行了 編譯——部署,然後就停了,不能像WPF 項目一樣實時Debug ,也不知道需要配置什麽,這樣一旦出錯,就得一點點試,很不舒服
2.點擊主頁後跳轉到子頁面,再彈出漢堡包跳轉第二個,再跳轉第三個後 程序就崩潰了,也不知道為什麽
3.有時頁面的ToolbarItem 不顯示,但是放到漢堡包裏的那個就顯示,不知道怎麽搞的,
以上問題有知道的,請多指教啊
六、總結
Xamarin 整合到VS 裏後,環境配置相比剛出來時好配置好多,VS Emulator 的加入也省去了下載 Android SDK時的困難,而且還特別大,雖然VS的某些功能還是需要FQ下載。
WP已死,沒必要開發,UWP 肯定是回到桌面的 UWP 開發比較好,調試和用法更好用,而且還可以查看虛擬樹什麽的,好方便的。
CM框架也要出4.0了,到時再試試CM
七、參考例子
Prism:https://github.com/PrismLibrary/Prism-Samples-Forms
Xamarin:https://github.com/xamarin/xamarin-forms-samples
走進 Prism for Xamarin.Forms