走进 Prism for Xamarin.Forms
2017-10-10

一、使用环境

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 绑定好的

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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 页面作为主页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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里的子页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?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>
<br>        <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>

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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