現在已經完成該應用程式所需要的基礎應用服務類別,因此,可以開始進行開發整個應用程式的運作流程頁面。
在這個階段的練習,您將會需要完成學會底下需求:
- 建立導航使用的頁面檢視 (View)
- 建立導航使用的頁面檢視模型 (ViewModel)
- 註冊導航使用的頁面檢視
- 建立主系統導航抽屜頁面檢視 (View)
- 建立系統導航抽屜頁面檢視模型 (ViewModel)
- 註冊系統導航抽屜頁面
- 建立差旅費用清單資料項目檢視模型 (ViewModel)
- 建立差旅費用清單頁面檢視 (View)
- 建立差旅費用清單頁面檢視模型 (ViewModel)
- 註冊差旅費用清單頁面
- 建立差旅費用項目資料維護頁面檢視 (View)
- 建立差旅費用項目資料維護頁面檢視模型 (ViewModel)
- 註冊差旅費用項目資料維護頁面
- 解決 iOS 應用程式啟動會有例外異常問題
這篇章節的練習專案的原始程式碼將會存放在 GitHubhttps://github.com/vulcanlee/XFAppSample/tree/master/XFDoggy/4.MainRec 內
建立導航使用的頁面檢視 (View)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism NavigationPage (Forms)
- 在底下名稱欄位內,輸入
NaviPage
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
NaviPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<NavigationPage 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"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XFDoggy.Views.NaviPage"
NavigationPage.BarBackgroundColor="{StaticResource ToolbarBackgroundColor}"
NavigationPage.BarTextColor="#FFF"
>
</NavigationPage>
建立導航使用的頁面檢視模型 (ViewModel)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ViewModel
- 在底下名稱欄位內,輸入
NaviPageViewModel
,接著,點選新增
按鈕
註冊導航使用的頁面檢視
- 在核心PCL
XFDoggy
專案內,開啟App.xaml.cs
檔案 - 使用底下C#程式碼替換掉剛剛開啟的檔案內的 RegisterTypes 方法定義
App.xaml.cs
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<LoadingPage>();
Container.RegisterTypeForNavigation<LoginPage>();
Container.RegisterTypeForNavigation<NaviPage>();
}
建立主系統導航抽屜頁面檢視 (View)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism MasterDetailPage (Forms)
- 在底下名稱欄位內,輸入
MainMDPage
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
MainMDPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage 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"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XFDoggy.Views.MainMDPage">
<MasterDetailPage.Master >
<ContentPage Title="功能表" Icon="hamburger.png">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="0,20,0,0" Android="0" WinPhone="0,0"
/>
</ContentPage.Padding>
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="MenuItemLevel1Style" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Start" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="16" />
<Setter Property="TextColor" Value="Black" />
<Setter Property="Margin" Value="35,10,0,0" />
</Style>
<Style x:Key="MenuItemLevel2Style" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Start" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="White" />
<Setter Property="Margin" Value="60,10,0,0" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout
Orientation="Vertical" BackgroundColor="White"
VerticalOptions="FillAndExpand">
<Grid>
<Image>
<Image.Source>
<OnPlatform x:TypeArguments="ImageSource">
<OnPlatform.iOS>
<FileImageSource File="sidebar.png"/>
</OnPlatform.iOS>
<OnPlatform.Android>
<FileImageSource File="sidebar.png"/>
</OnPlatform.Android>
<OnPlatform.WinPhone>
<FileImageSource File="Assets/Images/sidebar.png"/>
</OnPlatform.WinPhone>
</OnPlatform>
</Image.Source>
</Image>
<Image Margin="15,15,0,0"
WidthRequest="64" HeightRequest="64"
Aspect="Fill"
HorizontalOptions="Start" VerticalOptions="Start"
>
<Image.Source>
<OnPlatform x:TypeArguments="ImageSource">
<OnPlatform.iOS>
<FileImageSource File="Logo.png"/>
</OnPlatform.iOS>
<OnPlatform.Android>
<FileImageSource File="Logo.png"/>
</OnPlatform.Android>
<OnPlatform.WinPhone>
<FileImageSource File="Assets/Images/Logo.png"/>
</OnPlatform.WinPhone>
</OnPlatform>
</Image.Source>
</Image>
<Label Text="多奇數位創意有限公司" TextColor="White" FontSize="18"
HorizontalOptions="Start" VerticalOptions="End"
Margin="15,0,0,15"
/>
</Grid>
<Label
Margin="35,30,0,0"
Text ="差旅費用申請"
Style="{StaticResource MenuItemLevel1Style}"
TextColor="{Binding 差旅費用申請Color}"
>
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding 差旅費用申請Command}"/>
</Label.GestureRecognizers>
</Label>
<Label
Text ="登出"
Style="{StaticResource MenuItemLevel1Style}"
TextColor="{Binding 登出Color}"
>
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding 登出Command}"/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
建立主系統導航抽屜頁面檢視模型 (ViewModel)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ViewModel
- 在底下名稱欄位內,輸入
MainMDPageViewModel
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
MainMDPageViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
using XFDoggy.Helps;
namespace XFDoggy.ViewModels
{
public class MainMDPageViewModel : BindableBase
{
private readonly INavigationService _navigationService;
#region 差旅費用申請Color
private Color _差旅費用申請Color;
/// <summary>
/// 差旅費用申請
/// </summary>
public Color 差旅費用申請Color
{
get { return this._差旅費用申請Color; }
set { this.SetProperty(ref this._差旅費用申請Color, value); }
}
#endregion
#region 登出Color
private Color _登出Color;
/// <summary>
/// 登出
/// </summary>
public Color 登出Color
{
get { return this._登出Color; }
set { this.SetProperty(ref this._登出Color, value); }
}
#endregion
public DelegateCommand 差旅費用申請Command { get; private set; }
public DelegateCommand 登出Command { get; private set; }
public MainMDPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
差旅費用申請Command = new DelegateCommand(差旅費用申請);
登出Command = new DelegateCommand(登出);
更新正在執行項目的顏色();
}
private async void 登出()
{
AppData.正在執行功能 = 執行功能列舉.登出;
更新正在執行項目的顏色();
await _navigationService.NavigateAsync("xf:///LoginPage");
}
private async void 差旅費用申請()
{
if (AppData.正在執行功能 != 執行功能列舉.差旅費用申請)
{
AppData.正在執行功能 = 執行功能列舉.差旅費用申請;
更新正在執行項目的顏色();
await _navigationService.NavigateAsync("xf:///MainMDPage/NaviPage/TravelExpensesListPage");
}
}
public void 更新正在執行項目的顏色()
{
清除所有功能的顏色設定();
switch (AppData.正在執行功能)
{
case 執行功能列舉.差旅費用申請:
差旅費用申請Color = Color.FromHex("ed6d45");
break;
case 執行功能列舉.登出:
登出Color = Color.FromHex("ed6d45");
break;
default:
break;
}
}
public void 清除所有功能的顏色設定()
{
差旅費用申請Color = Color.FromHex("040000");
登出Color = Color.FromHex("040000");
}
}
}
註冊主系統導航抽屜頁面檢視
- 在核心PCL
XFDoggy
專案內,開啟App.xaml.cs
檔案 - 使用底下C#程式碼替換掉剛剛開啟的檔案內的 RegisterTypes 方法定義
App.xaml.cs
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<LoadingPage>();
Container.RegisterTypeForNavigation<LoginPage>();
Container.RegisterTypeForNavigation<NaviPage>();
Container.RegisterTypeForNavigation<MainMDPage>();
}
建立差旅費用清單資料項目檢視模型 (ViewModel)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ViewModel
- 在底下名稱欄位內,輸入
TravelExpensesListItemViewModel
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
TravelExpensesListItemViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
namespace XFDoggy.ViewModels
{
public class TravelExpensesListItemViewModel : BindableBase
{
#region ID
private int id;
/// <summary>
/// ID
/// </summary>
public int ID
{
get { return this.id; }
set { this.SetProperty(ref this.id, value); }
}
#endregion
#region Title
private string title;
/// <summary>
/// Title
/// </summary>
public string Title
{
get { return this.title; }
set { this.SetProperty(ref this.title, value); }
}
#endregion
#region TravelDate
private DateTime travelDate;
/// <summary>
/// TravelDate
/// </summary>
public DateTime TravelDate
{
get { return this.travelDate; }
set { this.SetProperty(ref this.travelDate, value); }
}
#endregion
public TravelExpensesListItemViewModel()
{
}
}
}
建立差旅費用清單頁面檢視 (View)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ContentPage (Forms)
- 在底下名稱欄位內,輸入
TravelExpensesListPage
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
TravelExpensesListPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XFDoggy.Views.TravelExpensesListPage"
BackgroundColor="{StaticResource PageBackgroundColor}"
Title="差旅費用"
>
<ContentPage.ToolbarItems>
<ToolbarItem
Command="{Binding 新增Command}"
Text="新增"
Order="Primary"
Priority="0">
<ToolbarItem.Icon>
<OnPlatform x:TypeArguments="FileImageSource"
iOS="Add.png"
Android="Add.png"
WinPhone="Assets/Images/Add.png" />
</ToolbarItem.Icon>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ListView
ItemsSource="{Binding MyData}"
SelectedItem="{Binding MyDataSelected, Mode=TwoWay}"
Margin="20,20"
IsPullToRefreshEnabled="True"
RefreshCommand="{Binding 更新資料Command}"
IsRefreshing="{Binding IsBusy, Mode=TwoWay}"
BackgroundColor="Transparent"
>
<ListView.Behaviors>
<behaviors:EventHandlerBehavior EventName="ItemTapped">
<behaviors:InvokeCommandAction Command="{Binding MyDataClickedCommand}" />
</behaviors:EventHandlerBehavior>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="120" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
FontSize="14"
Text="{Binding Title}"
TextColor="#494849"
VerticalOptions="Center"
/>
<Label Grid.Column="1"
FontSize="14"
Text="{Binding TravelDate, StringFormat='{0:yyyy-MM-dd}'}"
TextColor="#494849"
VerticalOptions="Center"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
建立差旅費用清單頁面檢視模型 (ViewModel)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ViewModel
- 在底下名稱欄位內,輸入
TravelExpensesListPageViewModel
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
TravelExpensesListPageViewModel.cs
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using XFDoggy.Helps;
using XFDoggy.Infrastructure;
using XFDoggy.Models;
namespace XFDoggy.ViewModels
{
public class TravelExpensesListPageViewModel : BindableBase, INavigationAware
{
private readonly INavigationService _navigationService;
private readonly IEventAggregator _eventAggregator;
public DelegateCommand MyDataClickedCommand { get; private set; }
public DelegateCommand 更新資料Command { get; private set; }
public DelegateCommand 新增Command { get; private set; }
#region MyData
private ObservableCollection<TravelExpensesListItemViewModel> myData = new ObservableCollection<TravelExpensesListItemViewModel>();
/// <summary>
/// MyData
/// </summary>
public ObservableCollection<TravelExpensesListItemViewModel> MyData
{
get { return this.myData; }
set { this.SetProperty(ref this.myData, value); }
}
#endregion
#region MyDataSelected
private TravelExpensesListItemViewModel myDataSelected;
/// <summary>
/// MyDataSelected
/// </summary>
public TravelExpensesListItemViewModel MyDataSelected
{
get { return this.myDataSelected; }
set { this.SetProperty(ref this.myDataSelected, value); }
}
#endregion
#region IsBusy
private bool isBusy;
/// <summary>
/// IsBusy
/// </summary>
public bool IsBusy
{
get { return this.isBusy; }
set { this.SetProperty(ref this.isBusy, value); }
}
#endregion
public TravelExpensesListPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
{
_navigationService = navigationService;
_eventAggregator = eventAggregator;
MyDataClickedCommand = new DelegateCommand(MyDataClicked);
更新資料Command = new DelegateCommand(更新資料);
新增Command = new DelegateCommand(新增);
_eventAggregator.GetEvent<CRUDEvent>().Subscribe(CRUD處理事件);
}
private void CRUD處理事件(string obj)
{
if (obj == "Refresh")
{
重新整理資料();
}
}
private async void 新增()
{
var fooPara = new NavigationParameters();
fooPara.Add("模式", "新增");
fooPara.Add("TravelExpense", new TravelExpense
{
Account = AppData.Account,
Category = "",
Domestic = true,
Expense = 0,
HasDocument = false,
Location = "",
Memo = "",
Title = "",
TravelDate = DateTime.Now
});
await _navigationService.NavigateAsync("TravelExpensesPage", fooPara);
}
private async void MyDataClicked()
{
var fooData = AppData.AllTravelExpense.FirstOrDefault(x => x.ID == MyDataSelected.ID);
var fooPara = new NavigationParameters();
fooPara.Add("模式", "修改");
fooPara.Add("TravelExpense", new TravelExpense
{
ID = fooData.ID,
Account = AppData.Account,
Category = fooData.Category,
Domestic = fooData.Domestic,
Expense = fooData.Expense,
HasDocument = fooData.HasDocument,
Location = fooData.Location,
Memo = fooData.Memo,
Title = fooData.Title,
TravelDate = fooData.TravelDate,
});
await _navigationService.NavigateAsync("TravelExpensesPage", fooPara);
}
private async void 更新資料()
{
var fooItems = (await AppData.DataService.GetTravelExpensesAsync(AppData.Account)).ToList();
AppData.AllTravelExpense = fooItems;
重新整理資料();
IsBusy = false;
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
重新整理資料();
}
public void 重新整理資料()
{
MyData.Clear();
foreach (var item in AppData.AllTravelExpense)
{
MyData.Add(new TravelExpensesListItemViewModel
{
ID = item.ID,
Title = item.Title,
TravelDate = item.TravelDate,
});
}
}
}
}
註冊差旅費用清單頁面檢視
- 在核心PCL
XFDoggy
專案內,開啟App.xaml.cs
檔案 - 使用底下C#程式碼替換掉剛剛開啟的檔案內的 RegisterTypes 方法定義
App.xaml.cs
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<LoadingPage>();
Container.RegisterTypeForNavigation<LoginPage>();
Container.RegisterTypeForNavigation<NaviPage>();
Container.RegisterTypeForNavigation<MainMDPage>();
Container.RegisterTypeForNavigation<TravelExpensesListPage>();
}
建立差旅費用項目資料維護頁面檢視 (View)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ContentPage (Forms)
- 在底下名稱欄位內,輸入
TravelExpensesPage
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
TravelExpensesPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage 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"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XFDoggy.Views.TravelExpensesPage"
BackgroundColor="{StaticResource PageBackgroundColor}"
NavigationPage.HasBackButton="False"
Title=""
>
<ContentPage.ToolbarItems>
<ToolbarItem
Command="{Binding 儲存Command}"
Text="儲存"
Order="Primary"
Priority="0">
<ToolbarItem.Icon>
<OnPlatform x:TypeArguments="FileImageSource"
iOS="Save.png"
Android="Save.png"
WinPhone="Assets/Images/Save.png" />
</ToolbarItem.Icon>
</ToolbarItem>
<ToolbarItem
Command="{Binding 取消Command}"
Text="取消"
Order="Primary"
Priority="0">
<ToolbarItem.Icon>
<OnPlatform x:TypeArguments="FileImageSource"
iOS="Cancel.png"
Android="Cancel.png"
WinPhone="Assets/Images/Cancel.png" />
</ToolbarItem.Icon>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<x:String x:Key="LablWidth">40</x:String>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ScrollView
Orientation="Vertical"
Margin="20">
<StackLayout
Orientation="Vertical"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="日期" Grid.Column="0" Style="{StaticResource LabelStyle}" />
<DatePicker Grid.Column="1"
Date="{Binding TravelDate, Mode=TwoWay}"
Format="yyyy-MM-dd"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="類型" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<Picker Grid.Column="1"
x:Name="picker分類"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="名稱" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<Entry Grid.Column="1"
Text="{Binding Title, Mode=TwoWay}"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="地點" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<Entry Grid.Column="1"
Text="{Binding Location, Mode=TwoWay}"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="費用" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<Entry Grid.Column="1"
Text="{Binding Expense, Mode=TwoWay}"
Keyboard="Numeric"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="備註" Grid.Column="0" Style="{StaticResource LabelStyle}"
VerticalOptions="Start"/>
<Editor Grid.Column="1"
Text="{Binding Memo, Mode=TwoWay}"
HeightRequest="300"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="國內" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<Switch Grid.Column="1"
IsToggled="{Binding Domestic, Mode=TwoWay}"
/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource LablWidth}"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="單據" Grid.Column="0" Style="{StaticResource LabelStyle}"/>
<Switch Grid.Column="1"
IsToggled="{Binding HasDocument, Mode=TwoWay}"
/>
</Grid>
<BoxView HeightRequest="80" Color="Transparent" />
</StackLayout>
</ScrollView>
<Grid
IsVisible="{Binding ShowDeleteMode}"
>
<BoxView
HeightRequest="70"
Color="{StaticResource BottomCommandBackgroundColor}"
/>
<Button Text="刪除"
Command="{Binding 刪除Command}"
BackgroundColor="{StaticResource ToolbarBackgroundColor}"
TextColor="#FFF"
WidthRequest="250" HeightRequest="40"
HorizontalOptions="Center"
>
<Button.Margin>
<OnPlatform x:TypeArguments="Thickness"
iOS="0,5,0,5" Android="0,0,0,0" WinPhone="0,5,0,5"/>
</Button.Margin>
</Button>
</Grid>
</StackLayout>
</ContentPage>
建立差旅費用項目資料維護頁面檢視模型 (ViewModel)
- 在核心PCL
XFDoggy
專案內,使用滑鼠右鍵點選ViewModels
資料夾,接著,選擇加入
>新增項目
- 在
加入新項目 - XFDoggy
對話窗中,點選Prism
>Prism ViewModel
- 在底下名稱欄位內,輸入
TravelExpensesPageViewModel
,接著,點選新增
按鈕 - 使用底下程式碼替換掉剛剛產生的檔案內容
TravelExpensesPageViewModel.cs
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using XFDoggy.Helps;
using XFDoggy.Infrastructure;
using XFDoggy.Models;
namespace XFDoggy.ViewModels
{
public class TravelExpensesPageViewModel : BindableBase, INavigationAware
{
#region ViewModel Property
#region ID
private int id;
/// <summary>
/// ID
/// </summary>
public int ID
{
get { return this.id; }
set { this.SetProperty(ref this.id, value); }
}
#endregion
#region Account
private string account;
/// <summary>
/// Account
/// </summary>
public string Account
{
get { return this.account; }
set { this.SetProperty(ref this.account, value); }
}
#endregion
#region TravelDate
private DateTime travelDate = DateTime.Now;
/// <summary>
/// TravelDate
/// </summary>
public DateTime TravelDate
{
get { return this.travelDate; }
set { this.SetProperty(ref this.travelDate, value); }
}
#endregion
#region Category
private string category;
/// <summary>
/// Category
/// </summary>
public string Category
{
get { return this.category; }
set { this.SetProperty(ref this.category, value); }
}
#endregion
#region Title
private string title;
/// <summary>
/// Title
/// </summary>
public string Title
{
get { return this.title; }
set { this.SetProperty(ref this.title, value); }
}
#endregion
#region Location
private string location;
/// <summary>
/// Location
/// </summary>
public string Location
{
get { return this.location; }
set { this.SetProperty(ref this.location, value); }
}
#endregion
#region Expense
private double expense;
/// <summary>
/// Expense
/// </summary>
public double Expense
{
get { return this.expense; }
set { this.SetProperty(ref this.expense, value); }
}
#endregion
#region Memo
private string memo;
/// <summary>
/// Memo
/// </summary>
public string Memo
{
get { return this.memo; }
set { this.SetProperty(ref this.memo, value); }
}
#endregion
#region Domestic
private bool domestic;
/// <summary>
/// Domestic
/// </summary>
public bool Domestic
{
get { return this.domestic; }
set { this.SetProperty(ref this.domestic, value); }
}
#endregion
#region HasDocument
private bool hasDocument;
/// <summary>
/// HasDocument
/// </summary>
public bool HasDocument
{
get { return this.hasDocument; }
set { this.SetProperty(ref this.hasDocument, value); }
}
#endregion
#region Updatetime
private DateTime updatetime;
/// <summary>
/// Updatetime
/// </summary>
public DateTime Updatetime
{
get { return this.updatetime; }
set { this.SetProperty(ref this.updatetime, value); }
}
#endregion
#region CategoryList
private List<string> categoryList;
/// <summary>
/// CategoryList
/// </summary>
public List<string> CategoryList
{
get { return this.categoryList; }
set { this.SetProperty(ref this.categoryList, value); }
}
#endregion
#region ShowDeleteMode
private bool isDeleteMode;
/// <summary>
/// PropertyDescription
/// </summary>
public bool ShowDeleteMode
{
get { return this.isDeleteMode; }
set { this.SetProperty(ref this.isDeleteMode, value); }
}
#endregion
#endregion
public string 紀錄處理模式 { get; set; }
private readonly INavigationService _navigationService;
public readonly IPageDialogService _dialogService;
private readonly IEventAggregator _eventAggregator;
public DelegateCommand 儲存Command { get; private set; }
public DelegateCommand 刪除Command { get; private set; }
public DelegateCommand 取消Command { get; private set; }
public delegate string 讀取Picker選擇項目Del();
public 讀取Picker選擇項目Del foo讀取Picker選擇項目;
public Action<string> foo分類Picker初始化;
public Action<string> foo頁面Title初始化;
TravelExpense fooTravelExpense;
public TravelExpensesPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator,
IPageDialogService dialogService)
{
_navigationService = navigationService;
_dialogService = dialogService;
_eventAggregator = eventAggregator;
儲存Command = new DelegateCommand(儲存);
刪除Command = new DelegateCommand(刪除);
取消Command = new DelegateCommand(取消);
}
private async void 取消()
{
if (檢查資料是否有異動() == true)
{
var fooAnswer = await _dialogService.DisplayAlertAsync("警告", "資料已經有異動,您確定要取消此次資料輸入嗎?", "是", "否");
if (fooAnswer == true)
{
await _navigationService.GoBackAsync();
}
}
else
{
await _navigationService.GoBackAsync();
}
}
private bool 檢查資料是否有異動()
{
bool fooIsChange = false;
Category = foo讀取Picker選擇項目();
if (fooTravelExpense.Category != Category)
{
fooIsChange = true;
}
else if (fooTravelExpense.Domestic != Domestic)
{
fooIsChange = true;
}
else if (fooTravelExpense.Expense != Expense)
{
fooIsChange = true;
}
else if (fooTravelExpense.HasDocument != HasDocument)
{
fooIsChange = true;
}
else if (fooTravelExpense.Location != Location)
{
fooIsChange = true;
}
else if (fooTravelExpense.Memo != Memo)
{
fooIsChange = true;
}
else if (fooTravelExpense.Title != Title)
{
fooIsChange = true;
}
else if (fooTravelExpense.TravelDate.Date != TravelDate.Date)
{
fooIsChange = true;
}
return fooIsChange;
}
private async void 刪除()
{
await AppData.DataService.DeleteTravelExpensesAsync(ID);
var fooItems = (await AppData.DataService.GetTravelExpensesAsync(AppData.Account)).ToList();
AppData.AllTravelExpense = fooItems;
_eventAggregator.GetEvent<CRUDEvent>().Publish("Refresh");
await _navigationService.GoBackAsync();
}
private async void 儲存()
{
Category = foo讀取Picker選擇項目();
fooTravelExpense = new TravelExpense
{
ID = ID,
Account = AppData.Account,
Category = category,
Domestic = Domestic,
Expense = Expense,
HasDocument = HasDocument,
Location = Location,
Memo = Memo,
Title = Title,
TravelDate = TravelDate,
Updatetime = DateTime.Now,
};
if (紀錄處理模式 == "新增")
{
await AppData.DataService.PostTravelExpensesAsync(fooTravelExpense);
}
else
{
await AppData.DataService.PutTravelExpensesAsync(fooTravelExpense);
}
var fooItems = (await AppData.DataService.GetTravelExpensesAsync(AppData.Account)).ToList();
AppData.AllTravelExpense = fooItems;
_eventAggregator.GetEvent<CRUDEvent>().Publish("Refresh");
await _navigationService.GoBackAsync();
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
紀錄處理模式 = parameters["模式"] as string;
fooTravelExpense = parameters["TravelExpense"] as TravelExpense;
ID = fooTravelExpense.ID;
Category = fooTravelExpense.Category;
Domestic = fooTravelExpense.Domestic;
Expense = fooTravelExpense.Expense;
HasDocument = fooTravelExpense.HasDocument;
Location = fooTravelExpense.Location;
Memo = fooTravelExpense.Memo;
Title = fooTravelExpense.Title;
TravelDate = fooTravelExpense.TravelDate;
if (紀錄處理模式 == "新增")
{
ShowDeleteMode = false;
foo頁面Title初始化("差旅費用 新增");
}
else
{
ShowDeleteMode = true;
foo分類Picker初始化(fooTravelExpense.Category);
foo頁面Title初始化("差旅費用 修改");
}
}
private void Init()
{
CategoryList = new List<string>();
var fooItems = AppData.AllTravelExpensesCategory;
foreach (var item in fooItems)
{
CategoryList.Add(item.Name);
}
}
}
}
建立差旅費用項目資料維護頁面後置程式碼 (code behind)
- 在核心PCL
XFDoggy
專案內,開啟檔案TravelExpensesPage.xaml.cs
- 使用底下程式碼替換掉剛剛產生的檔案內容
TravelExpensesPage.xaml.cs
using Xamarin.Forms;
using XFDoggy.Helps;
using XFDoggy.ViewModels;
using System.Linq;
namespace XFDoggy.Views
{
public partial class TravelExpensesPage : ContentPage
{
TravelExpensesPageViewModel fooTravelExpensesPageViewModel;
public TravelExpensesPage()
{
InitializeComponent();
fooTravelExpensesPageViewModel = this.BindingContext as TravelExpensesPageViewModel;
設定Picker選單內容();
fooTravelExpensesPageViewModel.foo讀取Picker選擇項目 = 讀取Picker選擇項目;
fooTravelExpensesPageViewModel.foo分類Picker初始化 = Picker資料初始化;
fooTravelExpensesPageViewModel.foo頁面Title初始化 = 頁面Title初始化;
}
protected override bool OnBackButtonPressed()
{
return true;
}
private void 頁面Title初始化(string obj)
{
this.Title = obj;
}
public void 設定Picker選單內容()
{
foreach (var item in AppData.AllTravelExpensesCategory)
{
picker分類.Items.Add(item.Name);
}
}
public string 讀取Picker選擇項目()
{
string fooRet = "";
var fooIdx = picker分類.SelectedIndex;
if (fooIdx >= 0)
{
fooRet = picker分類.Items[fooIdx];
}
else
{
fooRet = "";
}
return fooRet;
}
public void Picker資料初始化(string category)
{
var fooItem = AppData.AllTravelExpensesCategory.FirstOrDefault(x => x.Name == category);
if (fooItem != null)
{
var fooIdx = AppData.AllTravelExpensesCategory.IndexOf(fooItem);
if (fooIdx >= 0)
{
picker分類.SelectedIndex = fooIdx;
}
}
}
}
}
註冊差旅費用項目資料維護頁面檢視
- 在核心PCL
XFDoggy
專案內,開啟App.xaml.cs
檔案 - 使用底下C#程式碼替換掉剛剛開啟的檔案內的 RegisterTypes 方法定義
App.xaml.cs
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<LoadingPage>();
Container.RegisterTypeForNavigation<LoginPage>();
Container.RegisterTypeForNavigation<NaviPage>();
Container.RegisterTypeForNavigation<MainMDPage>();
Container.RegisterTypeForNavigation<TravelExpensesListPage>();
Container.RegisterTypeForNavigation<TravelExpensesPage>();
}
解決 iOS 應用程式啟動會有例外異常問題
如果您現在在 Android 開始執行這個 Xamarin.Forms 專案,可以正常執行。
不過,當同樣的核心PCL Xamarin.Forms 應用程式在 iOS 上執行,卻發生錯誤。
- 在
XFDoggy.iOS
專案內,開啟AppDelegate.cs
檔案 - 使用底下C#程式碼替換掉剛剛開啟的檔案內的 FinishedLaunching 方法定義
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// 需要加入這行解決無法載入組建的問題
var fooBehaviorBase = new Behaviors.BehaviorBase<Xamarin.Forms.ListView>();
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(new iOSInitializer()));
return base.FinishedLaunching(app, options);
}