在 Xamarin.Forms 專案,修正使用 Azure 行動應用的線上資料表
在這裡,我們將要把我們寫好的 Xamarin.Forms 專案,加入使用 Azure 行動應用服務功能。
第一次準備工作
在您原先做好的 Xamarin.Forms 專案中,需要加入相關 NuGet 套件與建立一個
MobileServiceClient
物件加入 NuGet 套件
- 使用 Visual Studio 2015 打開
XFDoggy.sln
專案 - 滑鼠右擊
方案XFDoggy
,選擇管理方案的 NuGet套件
- 在
瀏覽
標籤頁次的搜尋文字輸入盒內,填入Microsoft.Azure.Mobile.Client
搜尋這個套件 - 點選所有專案都要安裝這個套件,並點選
安裝
按鈕
加入 MobileServiceClient 物件
- 滑鼠右擊核心PCL專案的 XFDoggy,點選
加入
>新增資料夾
- 輸入
Helpers
- 滑鼠右擊核心PCL專案的
核心PCL專案
資料夾,點選加入
>類別
- 在對話窗中,點選
Visual C#
>類別
- 在名稱欄位輸入
MainHelper
,最後點選新增
按鈕 - 將 MainHelper 類別修改成底下的程式碼請修正相關命名空間錯誤
public class MainHelper
{
/// <summary>
/// 指向 Azure Mobile App 服務的主要網址
/// </summary>
public static string MainURL = "https://xamarinazureday.azurewebsites.net";
/// <summary>
/// Azure Mobile App 線上版本的用戶端
/// </summary>
public static MobileServiceClient client = new MobileServiceClient(MainURL);
}
關於建立 MobileServiceClient 物件的時候,需要傳遞的 URL 參數 ( 行動應用的主要 URL ),您可以從 Azure 儀表板中,找到XamarinAzureDay
行動應用程式,在這個XamarinAuzreDay App Service
刀鋒視窗中,預設會開啟概觀
標籤頁次,您可以從右方看到 URL 欄位,這裡就是這個行動應用的主要 URL。請使用 Https 的通訊協定
修改 Xamarin.Forms 專案
在這裡,我們將要修正 差旅費用 申請作業的相關功能;在這裡,我們需要修正兩個頁面的 ViewModel,分別是,顯示清單與紀錄新增或修改頁面。原則上,我們在這裡並不會修正原有這個頁面中 Xamarin.Forms 的程式處理邏輯。
建立資料表會用到的資料模型
- 滑鼠右擊核心PCL專案的
Models
資料夾,點選加入
>類別
- 在對話窗中,點選
Visual C#
>類別
- 在名稱欄位輸入
BusinessTripExpense
,最後點選新增
按鈕 - 將 BusinessTripExpense 類別修改成底下的程式碼
public class BusinessTripExpense
{
public string Id { get; set; }
public DateTime 出差日期 { get; set; }
public string 類型 { get; set; }
public string 項目名稱 { get; set; }
public string 地點 { get; set; }
public double 費用 { get; set; }
public string 備註 { get; set; }
public bool 國內外 { get; set; }
public bool 是否有單據 { get; set; }
}
在 ViewMolde 加入這個資料表的 CRUD 行動應用服務功能。
- 在核心PCL專案內,找到資料夾
ViewModels
內的差旅費用申請HomePageViewModel.cs
- 滑鼠雙擊這個檔案,打開它
宣告 Azure 行動應用服務資料物件
- 我們需要加入 Azure 行動應用服務的資料表宣告,這樣,我們才能夠針對遠端 SQL Server 資料庫內的資料表,進行 CRUD 處理。請解析要加入的適當命名空間
IMobileServiceTable<BusinessTripExpense> 差旅費用Table = MainHelper.client.GetTable<BusinessTripExpense>();
查詢/排序/過濾紀錄
- 要從遠端SQL資料庫內查詢資料表內的所有資料,可以使用底下程式碼。您可以針對
差旅費用Table
物件,使用 LINQ 方法,進行所要查詢資料的條件、排序行為等等工作,Azure 行動應用服務物件會將您的需求透過 OData 要求轉譯成 SQL 查詢。
#region 呼叫 Azure 行動應用後台,取得最新後台資料表的清單
var fooList = await 差旅費用Table.OrderByDescending(x => x.出差日期).ToListAsync();
foreach (var item in fooList)
{
foo差旅費用項目 = new 差旅費用項目ViewModel
{
ID = item.Id,
出差日期 = item.出差日期,
項目名稱 = item.項目名稱,
地點 = item.地點,
類型 = item.類型,
是否有單據 = item.是否有單據,
國內外 = item.國內外,
費用 = item.費用,
備註 = item.備註,
};
差旅費用項目清單.Add(foo差旅費用項目);
}
#endregion
新增紀錄
- 要新增一筆資料到遠端SQL資料庫內,可以使用底下程式碼產生一個新的資料表紀錄物件,呼叫
差旅費用Table.InsertAsync
,便可新增一筆紀錄到遠端SQL資料庫內在這裡,請不要指定Id
屬性有任何值,就樣才能夠讓 Azure 行動應用用戶端幫我們新增一筆記錄到資料表中
#region Azure 行動應用服務的新增
foo差旅費用 = new BusinessTripExpense
{
備註 = x.差旅費用項目.備註,
出差日期 = x.差旅費用項目.出差日期,
國內外 = x.差旅費用項目.國內外,
地點 = x.差旅費用項目.地點,
是否有單據 = x.差旅費用項目.是否有單據,
費用 = x.差旅費用項目.費用,
項目名稱 = x.差旅費用項目.項目名稱,
類型 = x.差旅費用項目.類型,
};
await 差旅費用Table.InsertAsync(foo差旅費用);
await Init();
#endregion
修改紀錄
- 要修改一筆資料到遠端SQL資料庫內,可以使用底下程式碼
差旅費用Table.UpdateAsync
,便可修改遠端SQL資料庫的紀錄
#region Azure 行動應用服務的新增
foo差旅費用 = await 差旅費用Table.LookupAsync(x.差旅費用項目.ID);
foo差旅費用.備註 = x.差旅費用項目.備註;
foo差旅費用.出差日期 = x.差旅費用項目.出差日期;
foo差旅費用.國內外 = x.差旅費用項目.國內外;
foo差旅費用.地點 = x.差旅費用項目.地點;
foo差旅費用.是否有單據 = x.差旅費用項目.是否有單據;
foo差旅費用.費用 = x.差旅費用項目.費用;
foo差旅費用.項目名稱 = x.差旅費用項目.項目名稱;
foo差旅費用.類型 = x.差旅費用項目.類型;
await 差旅費用Table.UpdateAsync(foo差旅費用);
#endregion
刪除紀錄
- 要刪除一筆資料到遠端SQL資料庫內,可以使用底下程式碼
差旅費用Table.DeleteAsync
,便可刪除遠端SQL資料庫的紀錄
#region Azure 行動應用服務的新增
foo差旅費用 = await 差旅費用Table.LookupAsync(x.差旅費用項目.ID);
await 差旅費用Table.DeleteAsync(foo差旅費用);
#endregion
完整的 差旅費用申請HomePage View / ViewModels
差旅費用申請HomePage.xaml
這裡修正了一些物件名稱
<?xml version="1.0" encoding="utf-8" ?>
<!--NavigationPage.BackButtonTitle="上一頁" 這個設定,主要是針對 iOS 平台來使用到的-->
<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"
xmlns:localControls="clr-namespace:XFDoggy.CustomControls"
xmlns:behaviors="clr-namespace:XFDoggy.Behaviors"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XFDoggy.Views.差旅費用申請HomePage"
Title="差旅費用申請"
x:Name="ThisPage"
NavigationPage.BackButtonTitle="上一頁">
<!--這個頁面的版面配置-->
<!--Data Binding 資料繫結請參考:https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/-->
<Grid
RowSpacing="0" ColumnSpacing="0"
BackgroundColor="{StaticResource 頁面背景Color}"
>
<!--該頁面的主要背景顏色設定-->
<Grid
RowSpacing="0" ColumnSpacing="0"
Margin="10"
BackgroundColor="{StaticResource 頁面內容本文背景Color}"
>
<Grid.RowDefinitions>
<!--這個部分為頁面標題與文字的空間規劃-->
<RowDefinition Height="40"/>
<!--這個部分是其他內容區域-->
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--頁面標題與文字-->
<Grid
RowSpacing="0" ColumnSpacing="0"
BackgroundColor="{StaticResource 頁面內容標題背景Color}"
>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Label
Text="差旅費用申請清單"
Style="{StaticResource 頁面內文標題文字Style}"
/>
</Grid>
<!--清單資料-->
<!--ListView 請參考:https://developer.xamarin.com/api/type/Xamarin.Forms.ListView/-->
<ListView
HorizontalOptions="Fill" VerticalOptions="Fill"
Grid.Row="1"
ItemsSource="{Binding 差旅費用項目清單}"
SelectedItem="{Binding 點選差旅費用項目, Mode=TwoWay}"
SeparatorVisibility="None"
HasUnevenRows="True"
>
<!--這個部分為要使用 MVVM 綁定命令方式,將要透過 XAML Behavior來做到-->
<!--Xamarin.Forms Behavior 請參考:https://developer.xamarin.com/guides/xamarin-forms/behaviors/-->
<ListView.Behaviors>
<behaviors:EventHandlerBehavior EventName="ItemTapped">
<behaviors:InvokeCommandAction Command="{Binding 點選差旅費用項目Command}" />
</behaviors:EventHandlerBehavior>
</ListView.Behaviors>
<ListView.ItemTemplate>
<!--定義每筆紀錄要出現的樣貌-->
<DataTemplate>
<!--ViewCell 請參考:https://developer.xamarin.com/api/type/Xamarin.Forms.ViewCell/-->
<ViewCell>
<Grid
RowSpacing="0" ColumnSpacing="0"
>
<StackLayout
Orientation="Vertical"
>
<Label
Margin="10,0"
Text="{Binding 出差日期, StringFormat='{0:yyyy-MM-dd}'}}"
TextColor="Black"
FontSize="24"
HorizontalOptions="Start" VerticalOptions="Start"
LineBreakMode="WordWrap">
</Label>
<StackLayout
Orientation="Horizontal"
Margin="0,5">
<Label
Margin="10,0,0,0"
Text="{Binding 類型}"
TextColor="Black"
FontSize="14"
HorizontalOptions="Start" VerticalOptions="Start"
LineBreakMode="TailTruncation"
WidthRequest="150">
</Label>
<Label
Margin="10,0,10,0"
Text="{Binding 費用, StringFormat='{0} 元'}"
TextColor="Black"
FontSize="14"
HorizontalOptions="End" VerticalOptions="Start"
LineBreakMode="TailTruncation">
</Label>
</StackLayout>
</StackLayout>
<!--分隔線-->
<BoxView
HorizontalOptions="Fill" VerticalOptions="End"
HeightRequest="2"
Color="#8c8c8c" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<!--新增按鈕-->
<!--這裡採用浮動式的按鈕設計-->
<Grid
RowSpacing="0" ColumnSpacing="0"
HorizontalOptions="End" VerticalOptions="End"
Margin="0,0,30,30"
>
<BoxView Color="Black" WidthRequest="50" HeightRequest="50"/>
<localControls:FontAwesomeLabel
Text=""
Font="36"
TextColor="White"
HorizontalOptions="Center" VerticalOptions="Center"
>
<localControls:FontAwesomeLabel.GestureRecognizers>
<TapGestureRecognizer Command="{Binding 新增按鈕Command}" />
</localControls:FontAwesomeLabel.GestureRecognizers>
</localControls:FontAwesomeLabel>
</Grid>
</Grid>
</ContentPage>
差旅費用申請HomePageViewModel.cs
這裡修正了一些物件名稱
public class 差旅費用申請HomePageViewModel : BindableBase, INavigationAware
{
#region Repositories (遠端或本地資料存取)
// 取得 Azure Mobile App 中的 差旅費用 資料表物件
IMobileServiceTable<BusinessTripExpense> 差旅費用Table = MainHelper.client.GetTable<BusinessTripExpense>();
#endregion
#region ViewModel Property (用於在 View 中作為綁定之用)
#region 差旅費用項目清單
private ObservableCollection<差旅費用項目ViewModel> _差旅費用項目清單 = new ObservableCollection<差旅費用項目ViewModel>();
/// <summary>
/// 工作日報表項目清單
/// </summary>
public ObservableCollection<差旅費用項目ViewModel> 差旅費用項目清單
{
get { return _差旅費用項目清單; }
set { SetProperty(ref _差旅費用項目清單, value); }
}
#endregion
#region 點選差旅費用項目
private 差旅費用項目ViewModel _點選差旅費用項目;
/// <summary>
/// PropertyDescription
/// </summary>
public 差旅費用項目ViewModel 點選差旅費用項目
{
get { return this._點選差旅費用項目; }
set { this.SetProperty(ref this._點選差旅費用項目, value); }
}
#endregion
#endregion
#region Field 欄位
BusinessTripExpense foo差旅費用;
public DelegateCommand 點選差旅費用項目Command { get; set; }
public DelegateCommand 新增按鈕Command { get; set; }
private readonly INavigationService _navigationService;
private readonly IEventAggregator _eventAggregator;
#endregion
#region Constructor 建構式
public 差旅費用申請HomePageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
{
#region 相依性服務注入的物件
_eventAggregator = eventAggregator;
_navigationService = navigationService;
#endregion
#region 頁面中綁定的命令
點選差旅費用項目Command = new DelegateCommand(async () =>
{
#region 建立要傳遞到下個頁面的參數
var fooNavigationParameters = new NavigationParameters();
fooNavigationParameters.Add("點選工作日報表項目", 點選差旅費用項目);
fooNavigationParameters.Add("新增或修改", 新增或修改Enum.修改);
#endregion
//點選工作日報表項目 = null;
await _navigationService.NavigateAsync("差旅費用申請紀錄修改Page", fooNavigationParameters);
});
新增按鈕Command = new DelegateCommand(async () =>
{
#region 建立要傳遞到下個頁面的參數
var fooNavigationParameters = new NavigationParameters();
fooNavigationParameters.Add("點選工作日報表項目", new 差旅費用項目ViewModel());
fooNavigationParameters.Add("新增或修改", 新增或修改Enum.新增);
#endregion
await _navigationService.NavigateAsync("差旅費用申請紀錄修改Page", fooNavigationParameters);
});
#endregion
#region 事件聚合器訂閱
_eventAggregator.GetEvent<差旅費用紀錄維護動作Event>().Subscribe(async x =>
{
switch (x.新增或修改Enum)
{
case 新增或修改Enum.新增:
#region Azure 行動應用服務的新增
foo差旅費用 = new BusinessTripExpense
{
備註 = x.差旅費用項目.備註,
出差日期 = x.差旅費用項目.出差日期,
國內外 = x.差旅費用項目.國內外,
地點 = x.差旅費用項目.地點,
是否有單據 = x.差旅費用項目.是否有單據,
費用 = x.差旅費用項目.費用,
項目名稱 = x.差旅費用項目.項目名稱,
類型 = x.差旅費用項目.類型,
};
await 差旅費用Table.InsertAsync(foo差旅費用);
await Init();
#endregion
break;
case 新增或修改Enum.修改:
#region 更新欄位資料
var fooObj = 差旅費用項目清單.FirstOrDefault(z => z.ID == x.差旅費用項目.ID);
if (fooObj != null)
{
var fooIdx = 差旅費用項目清單.IndexOf(fooObj);
差旅費用項目清單[fooIdx] = x.差旅費用項目;
}
#region Azure 行動應用服務的新增
foo差旅費用 = await 差旅費用Table.LookupAsync(x.差旅費用項目.ID);
foo差旅費用.備註 = x.差旅費用項目.備註;
foo差旅費用.出差日期 = x.差旅費用項目.出差日期;
foo差旅費用.國內外 = x.差旅費用項目.國內外;
foo差旅費用.地點 = x.差旅費用項目.地點;
foo差旅費用.是否有單據 = x.差旅費用項目.是否有單據;
foo差旅費用.費用 = x.差旅費用項目.費用;
foo差旅費用.項目名稱 = x.差旅費用項目.項目名稱;
foo差旅費用.類型 = x.差旅費用項目.類型;
await 差旅費用Table.UpdateAsync(foo差旅費用);
#endregion
#endregion
break;
case 新增或修改Enum.刪除:
var fooObjDel = 差旅費用項目清單.FirstOrDefault(z => z.ID == x.差旅費用項目.ID);
if (fooObjDel != null)
{
差旅費用項目清單.Remove(fooObjDel);
}
#region Azure 行動應用服務的新增
foo差旅費用 = await 差旅費用Table.LookupAsync(x.差旅費用項目.ID);
await 差旅費用Table.DeleteAsync(foo差旅費用);
#endregion
break;
default:
break;
}
});
#endregion
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public async void OnNavigatedTo(NavigationParameters parameters)
{
if (parameters.ContainsKey("差旅費用紀錄維護動作內容") == false)
{
await Init();
}
}
#endregion
#region Navigation Events (頁面導航事件)
#endregion
#region 設計時期或者執行時期的ViewModel初始化
#endregion
#region 相關事件
#endregion
#region 相關的Command定義
#endregion
#region 其他方法
/// <summary>
/// 進行要顯示資料的初始化
/// </summary>
private async Task Init()
{
差旅費用項目ViewModel foo差旅費用項目;
差旅費用項目清單.Clear();
#region 呼叫 Azure 行動應用後台,取得最新後台資料表的清單
var fooList = await 差旅費用Table.OrderByDescending(x => x.出差日期).ToListAsync();
foreach (var item in fooList)
{
foo差旅費用項目 = new 差旅費用項目ViewModel
{
ID = item.Id,
出差日期 = item.出差日期,
項目名稱 = item.項目名稱,
地點 = item.地點,
類型 = item.類型,
是否有單據 = item.是否有單據,
國內外 = item.國內外,
費用 = item.費用,
備註 = item.備註,
};
差旅費用項目清單.Add(foo差旅費用項目);
}
#endregion
}
#endregion
}
iOS 原生專案修正
- 打開 iOS 原生專案中
AppDelegate.cs
檔案 - 在類別
AppDelegate
的FinishedLaunching
方法內的呼叫FinishedLaunching
方法前,加入底下程式碼
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();
實際執行結果
在這裡,我們使用 Android 平台做為測試標的
我們在這裡可以新增任意數量紀錄,也可以隨意修改與刪除
檢測後端資料庫儲存結果
您可以使用 PostMan 來取得後端SQL資料庫內的 差旅費用 資料表,實際儲存的紀錄內容。
在這裡,我們將會使用底下 URL 來查詢(記得要加入適當的 Header)
底下是實際查詢結果內容
[
{
"deleted": false,
"updatedAt": "2017-02-12T07:36:18.204Z",
"createdAt": "2017-02-12T07:31:55.314Z",
"version": "AAAAAAAACAE=",
"id": "6de91a3368964410b514d50e22a3f1a6",
"是否有單據": false,
"國內外": true,
"備註": null,
"費用": 5200,
"地點": "Taipei",
"項目名稱": "Accommondation cost",
"類型": "Hotel",
"出差日期": "2017-02-11T16:00:00Z"
},
{
"deleted": false,
"updatedAt": "2017-02-12T07:30:20.064Z",
"createdAt": "2017-02-12T07:30:20.064Z",
"version": "AAAAAAAAB/w=",
"id": "dbdf03a4240b4a84b9e0ac9d42510182",
"是否有單據": false,
"國內外": true,
"備註": "By Uber",
"費用": 310,
"地點": "Taipei",
"項目名稱": "Airport to Hotel",
"類型": "Traffic Taxi",
"出差日期": "2017-02-10T16:00:00Z"
}
]