在 Azure 行動應用後端專案,新增差旅費用頁面需要用的資料表
Enable-Migrations
Checking if the context targets an existing database...
Detected database created with a database initializer. Scaffolded migration '201702111723152_InitialCreate' corresponding to existing database. To use an automatic migration instead, delete the Migrations folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter.
Code First Migrations enabled for project XamarinAzureDayService.
Add-Migration Initial
Scaffolding migration 'Initial'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration Initial' again.
using System.Data.Entity.Migrations;
using XamarinAzureDayService.Migrations;
var migrator = new DbMigrator(new Migrations.Configuration());
migrator.Update();
建立資料傳輸物件 (DTO) 類別
- 為了要開始產生一個我們自訂的資料表,我們需要定義資料表控制器, 設定資料表控制器需要三個步驟︰
public class BusinessTripExpense : EntityData
{
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; }
}
EntityData 類別
public bool Deleted { get; set; }
public abstract class EntityData : ITableData
{
protected EntityData();
[System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute]
[Index(IsClustered = true)]
[TableColumn(TableColumnType.CreatedAt)]
public DateTimeOffset? CreatedAt { get; set; }
[TableColumn(TableColumnType.Deleted)]
public bool Deleted { get; set; }
[System.ComponentModel.DataAnnotations.KeyAttribute]
[TableColumn(TableColumnType.Id)]
public string Id { get; set; }
[System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute]
[TableColumn(TableColumnType.UpdatedAt)]
public DateTimeOffset? UpdatedAt { get; set; }
[TableColumn(TableColumnType.Version)]
[System.ComponentModel.DataAnnotations.TimestampAttribute]
public byte[] Version { get; set; }
}
建立資料表控制器。
使用 Azure 行動應用程式資料表控制器
public class BusinessTripExpenseController : TableController<BusinessTripExpense>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
XamarinAzureDayContext context = new XamarinAzureDayContext();
DomainManager = new EntityDomainManager<BusinessTripExpense>(context, Request);
}
// GET tables/BusinessTripExpense
public IQueryable<BusinessTripExpense> GetAllBusinessTripExpense()
{
return Query();
}
// GET tables/BusinessTripExpense/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<BusinessTripExpense> GetBusinessTripExpense(string id)
{
return Lookup(id);
}
// PATCH tables/BusinessTripExpense/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<BusinessTripExpense> PatchBusinessTripExpense(string id, Delta<BusinessTripExpense> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/BusinessTripExpense
public async Task<IHttpActionResult> PostBusinessTripExpense(BusinessTripExpense item)
{
BusinessTripExpense current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/BusinessTripExpense/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteBusinessTripExpense(string id)
{
return DeleteAsync(id);
}
}
加入 Migration
Add-Migration BusinessTripExpense
發佈到 Azure 雲端上
PostMan 測試資料
本地端
Azure
這個練習範例專案,請參考
第一次準備工作
加入 NuGet 套件
加入 MobileServiceClient 物件
public class MainHelper
{
public static string MainURL = "https://xamarinazureday.azurewebsites.net";
public static MobileServiceClient client = new MobileServiceClient(MainURL);
}
建立資料表會用到的資料模型
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 行動應用服務功能。
宣告 Azure 行動應用服務資料物件
- 我們需要加入 Azure 行動應用服務的資料表宣告,這樣,我們才能夠針對遠端 SQL Server 資料庫內的資料表,進行 CRUD 處理。
IMobileServiceTable<BusinessTripExpense> 差旅費用Table = MainHelper.client.GetTable<BusinessTripExpense>();
查詢/排序/過濾紀錄
#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
新增紀錄
#region Azure 行動應用服務的新增
foo差旅費用 = new BusinessTripExpense
{
備註 = x.差旅費用項目.備註,
出差日期 = x.差旅費用項目.出差日期,
國內外 = x.差旅費用項目.國內外,
地點 = x.差旅費用項目.地點,
是否有單據 = x.差旅費用項目.是否有單據,
費用 = x.差旅費用項目.費用,
項目名稱 = x.差旅費用項目.項目名稱,
類型 = x.差旅費用項目.類型,
};
await 差旅費用Table.InsertAsync(foo差旅費用);
await Init();
#endregion
修改紀錄
#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
刪除紀錄
#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" ?>
<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="上一頁">
<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
HorizontalOptions="Fill" VerticalOptions="Fill"
Grid.Row="1"
ItemsSource="{Binding 差旅費用項目清單}"
SelectedItem="{Binding 點選差旅費用項目, Mode=TwoWay}"
SeparatorVisibility="None"
HasUnevenRows="True"
>
<ListView.Behaviors>
<behaviors:EventHandlerBehavior EventName="ItemTapped">
<behaviors:InvokeCommandAction Command="{Binding 點選差旅費用項目Command}" />
</behaviors:EventHandlerBehavior>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<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 原生專案修正
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();
實際執行結果
檢測後端資料庫儲存結果
[
{
"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"
}
]
在 Azure 行動應用後端專案,新增我要請假頁面需要用的資料表
建立資料傳輸物件 (DTO) 類別
- 為了要開始產生一個我們自訂的資料表,我們需要定義資料表控制器, 設定資料表控制器需要三個步驟︰
public class LeaveRecord : EntityData
{
public string 申請人 { get; set; }
public DateTime 請假日期 { get; set; }
public TimeSpan 開始時間 { get; set; }
public int 請假時數 { get; set; }
public string 假別 { get; set; }
public string 請假理由 { get; set; }
public string 職務代理人 { get; set; }
}
建立資料表控制器。
使用 Azure 行動應用程式資料表控制器
public class LeaveRecordController : TableController<LeaveRecord>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
XamarinAzureDayContext context = new XamarinAzureDayContext();
DomainManager = new EntityDomainManager<LeaveRecord>(context, Request);
}
// GET tables/LeaveRecord
public IQueryable<LeaveRecord> GetAllLeaveRecord()
{
return Query();
}
// GET tables/LeaveRecord/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<LeaveRecord> GetLeaveRecord(string id)
{
return Lookup(id);
}
// PATCH tables/LeaveRecord/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<LeaveRecord> PatchLeaveRecord(string id, Delta<LeaveRecord> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/LeaveRecord
public async Task<IHttpActionResult> PostLeaveRecord(LeaveRecord item)
{
LeaveRecord current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/LeaveRecord/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteLeaveRecord(string id)
{
return DeleteAsync(id);
}
}
加入 Migration
Add-Migration LeaveRecord
發佈到 Azure 雲端上
PostMan 測試資料
本地端
Azure
這個練習範例專案,請參考
第一次準備工作
加入 NuGet 套件
加入 MobileServiceClient 離線初始化方法
using Microsoft.WindowsAzure.MobileServices;
using Microsoft.WindowsAzure.MobileServices.SQLiteStore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XFDoggy.Models;
using XFDoggy.Repositories;
namespace XFDoggy.Helpers
{
public static class MainHelper
{
public static string MainURL = "https://xamarinazureday.azurewebsites.net";
public static MobileServiceClient client = new MobileServiceClient(MainURL);
public static string offlineDbPath = @"localstore.db";
public static MobileServiceSQLiteStore store = new MobileServiceSQLiteStore(offlineDbPath);
public static 請假紀錄Manager 請假紀錄Manager = new 請假紀錄Manager();
public static void AzureMobileOfflineInit()
{
var store = MainHelper.store;
store.DefineTable<LeaveRecord>();
MainHelper.client.SyncContext.InitializeAsync(store);
}
}
}
設定啟動應用程式的時候,要進行離線資料庫初始化
protected override void OnInitialized()
{
InitializeComponent();
#region 進行離線資料庫初始化
MainHelper.AzureMobileOfflineInit();
#endregion
NavigationService.NavigateAsync($"LoginPage");
}
建立資料表會用到的資料模型
public class LeaveRecord
{
public string Id { get; set; }
public string 申請人 { get; set; }
public DateTime 請假日期 { get; set; }
public TimeSpan 開始時間 { get; set; }
public int 請假時數 { get; set; }
public string 假別 { get; set; }
public string 請假理由 { get; set; }
public string 職務代理人 { get; set; }
[Version]
public string Version { get; set; }
}
建立 請假紀錄Manager
/// <summary>
/// 進行請假記錄的檔案存取功能
/// </summary>
public class 請假紀錄Manager
{
// 取得 Azure Mobile App 中的 請假紀錄 離線資料表物件
public IMobileServiceSyncTable<LeaveRecord> 請假紀錄Table;
// 進行同步工作的時候,產生的錯誤訊息
public string ErrorMessage = "";
public 請假紀錄Manager()
{
// 取得 請假記錄 離線資料表物件
請假紀錄Table = MainHelper.client.GetSyncTable<LeaveRecord>();
}
/// <summary>
/// 取得請假記錄清單
/// </summary>
/// <param name="是否要先進行同步工作"></param>
/// <returns></returns>
public async Task<ObservableCollection<LeaveRecord>> GetAsync(bool 是否要先進行同步工作 = false)
{
ErrorMessage = "";
try
{
if (是否要先進行同步工作)
{
// 將本地端的異動與伺服器端的紀錄進行同步處理
ErrorMessage = await SyncAsync();
}
// 取得所有的請假記錄集合
IEnumerable<LeaveRecord> items = await 請假紀錄Table
.OrderByDescending(x => x.請假日期)
.ToEnumerableAsync();
return new ObservableCollection<LeaveRecord>(items);
}
catch (MobileServiceInvalidOperationException msioe)
{
ErrorMessage = string.Format(@"Invalid sync operation: {0}", msioe.Message);
}
catch (Exception e)
{
ErrorMessage = string.Format(@"Sync error: {0}", e.Message);
}
return null;
}
/// <summary>
/// 使用 Id 鍵值進行查詢出這筆紀錄
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<LeaveRecord> LookupAsync(string id)
{
var fooObj = await 請假紀錄Table.LookupAsync(id);
return fooObj;
}
/// <summary>
/// 進行記錄更新
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public async Task UpdateAsync(LeaveRecord item)
{
await 請假紀錄Table.UpdateAsync(item);
}
/// <summary>
/// 新增一筆紀錄
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public async Task InsertAsync(LeaveRecord item)
{
await 請假紀錄Table.InsertAsync(item);
}
/// <summary>
/// 刪除這筆紀錄
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public async Task DeleteAsync(LeaveRecord item)
{
await 請假紀錄Table.DeleteAsync(item);
}
/// <summary>
/// 進行資料同步,並且取得最新線上的紀錄
/// </summary>
/// <returns></returns>
public async Task<string> SyncAsync()
{
var fooResult = "";
ReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;
try
{
var fooClient = MainHelper.client;
await fooClient.SyncContext.PushAsync();
// 這裡可以設定要取得那些資料
var fooK = 請假紀錄Table.Where(x => x.請假日期 > DateTime.Now.AddMonths(-3));
await 請假紀錄Table.PullAsync(
//The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
//Use a different query name for each unique query in your program
"請假紀錄", fooK);
}
catch (MobileServicePushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling. A real application would handle the various errors like network conditions,
// server conflicts and others via the IMobileServiceSyncHandler.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//這裡要解決本地與伺服器端資料衝突的問題,底下是使用伺服器版本,蓋掉本機版本
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// 取消這筆本地端的資料異動,該異動紀錄將會從本地端移除
//await error.CancelAndDiscardItemAsync();
}
fooResult = @"Error executing sync operation. Item: {error.TableName} (" + error.Item["id"] + "). Operation discarded.";
}
}
return fooResult;
}
}
在 ViewMolde 加入這個資料表的 CRUD 行動應用服務功能
我要請假HomePage.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"
xmlns:localControls="clr-namespace:XFDoggy.CustomControls"
xmlns:behaviors="clr-namespace:XFDoggy.Behaviors"
xmlns:converters="clr-namespace:XFDoggy.Converters"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XFDoggy.Views.我要請假HomePage"
Title="我要請假"
x:Name="ThisPage"
NavigationPage.BackButtonTitle="上一頁">
<ContentPage.Resources>
<ResourceDictionary>
<converters:假別Converter x:Key="假別Converter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="新增" Command="{Binding 點選新增請假紀錄項目Command}" />
<ToolbarItem Text="更新" Command="{Binding 點選更新請假紀錄項目Command}" />
</ContentPage.ToolbarItems>
<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
HorizontalOptions="Fill" VerticalOptions="Fill"
Grid.Row="1"
ItemsSource="{Binding 請假紀錄項目清單}"
SelectedItem="{Binding 點選請假紀錄項目, Mode=TwoWay}"
SeparatorVisibility="None"
HasUnevenRows="True"
>
<ListView.Behaviors>
<behaviors:EventHandlerBehavior EventName="ItemTapped">
<behaviors:InvokeCommandAction Command="{Binding 點選請假紀錄項目Command}" />
</behaviors:EventHandlerBehavior>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid
RowSpacing="0" ColumnSpacing="0"
>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
RowSpacing="0" ColumnSpacing="0"
Grid.Row="0" Grid.Column="0"
Grid.RowSpan="2"
>
<BoxView
HorizontalOptions="Fill" VerticalOptions="Fill"
Color="Black"/>
<localControls:FontAwesomeLabel
Text="{Binding 假別, Converter={StaticResource 假別Converter}}"
Font="36"
TextColor="White"
HorizontalOptions="Center" VerticalOptions="Center"
/>
</Grid>
<Grid
RowSpacing="0" ColumnSpacing="0"
Grid.Row="0" Grid.Column="1"
>
<Label
Margin="10,0"
Text="{Binding 請假日期, StringFormat='{0:yyyy-MM-dd}'}"
TextColor="Black"
FontSize="24"
HorizontalOptions="Start" VerticalOptions="Start"
LineBreakMode="WordWrap">
</Label>
</Grid>
<Grid
RowSpacing="0" ColumnSpacing="0"
Grid.Row="1" Grid.Column="1"
>
<StackLayout
Orientation="Horizontal"
Spacing="0"
>
<Label
Margin="10,0"
Text="{Binding 開始時間, StringFormat='{0}'}"
TextColor="Black"
FontSize="24"
HorizontalOptions="Start" VerticalOptions="Start"
>
</Label>
<Label
Margin="10,0"
Text="{Binding 請假時數, StringFormat='{0}小時'}}"
TextColor="Black"
FontSize="24"
HorizontalOptions="Start" VerticalOptions="Start"
>
</Label>
</StackLayout>
</Grid>
<BoxView
Grid.Row="0" Grid.Column="0"
Grid.RowSpan="2" Grid.ColumnSpan="2"
HorizontalOptions="Fill" VerticalOptions="End"
HeightRequest="2"
Color="#8c8c8c" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
</ContentPage>
差旅費用申請HomePageViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using Prism.Navigation;
using Prism.Events;
using XFDoggy.Repositories;
using XFDoggy.Models;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using XFDoggy.Helpers;
using Microsoft.WindowsAzure.MobileServices;
using Microsoft.WindowsAzure.MobileServices.Sync;
using Microsoft.WindowsAzure.MobileServices.SQLiteStore;
using Prism.Services;
namespace XFDoggy.ViewModels
{
public class 我要請假HomePageViewModel : BindableBase, INavigationAware
{
#region Repositories (遠端或本地資料存取)
//請假紀錄Repository foo請假紀錄Repository = new 請假紀錄Repository();
#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>
/// 點選請假紀錄項目
/// </summary>
public 請假紀錄項目ViewModel 點選請假紀錄項目
{
get { return this._點選請假紀錄項目; }
set { this.SetProperty(ref this._點選請假紀錄項目, value); }
}
#endregion
#endregion
#region Field 欄位
public DelegateCommand 點選請假紀錄項目Command { get; set; }
public DelegateCommand 點選新增請假紀錄項目Command { get; set; }
public DelegateCommand 點選更新請假紀錄項目Command { get; set; }
public readonly IPageDialogService _dialogService;
private readonly INavigationService _navigationService;
private readonly IEventAggregator _eventAggregator;
#endregion
#region Constructor 建構式
public 我要請假HomePageViewModel(INavigationService navigationService, IEventAggregator eventAggregator,
IPageDialogService dialogService)
{
#region 相依性服務注入的物件
_dialogService = dialogService;
_eventAggregator = eventAggregator;
_navigationService = navigationService;
#endregion
#region 頁面中綁定的命令
點選請假紀錄項目Command = new DelegateCommand(async () =>
{
var fooID = 點選請假紀錄項目.ID;
點選請假紀錄項目 = null;
await _navigationService.NavigateAsync($"我要請假記錄修改Page?ID={fooID}");
});
點選新增請假紀錄項目Command = new DelegateCommand(async () =>
{
await _navigationService.NavigateAsync($"我要請假記錄修改Page");
});
點選更新請假紀錄項目Command = new DelegateCommand(async () =>
{
try
{
await Init(true);
}
catch (Exception ex)
{
await _dialogService.DisplayAlertAsync("錯誤", $"{ex.Message}", "確定");
}
});
#endregion
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public async void OnNavigatedTo(NavigationParameters parameters)
{
await Init();
}
#endregion
#region Navigation Events (頁面導航事件)
#endregion
#region 設計時期或者執行時期的ViewModel初始化
#endregion
#region 相關事件
#endregion
#region 相關的Command定義
#endregion
#region 其他方法
/// <summary>
/// 進行要顯示資料的初始化
/// </summary>
private async Task Init(bool 是否要先進行同步工作 = false)
{
var foo請假紀錄Collection = await MainHelper.請假紀錄Manager.GetAsync(是否要先進行同步工作);
#region 檢查進行同步更新的時候,是否有異常問題發生
if (foo請假紀錄Collection == null)
{
if (string.IsNullOrEmpty(MainHelper.請假紀錄Manager.ErrorMessage) == false)
{
await _dialogService.DisplayAlertAsync("錯誤", $"{MainHelper.請假紀錄Manager.ErrorMessage}", "確定");
return;
}
}
#endregion
請假紀錄項目清單.Clear();
foreach (var item in foo請假紀錄Collection)
{
請假紀錄項目清單.Add(new 請假紀錄項目ViewModel
{
ID = item.Id,
假別 = item.假別,
申請人 = item.申請人,
職務代理人 = item.職務代理人,
請假日期 = item.請假日期,
請假時數 = item.請假時數,
請假理由 = item.請假理由,
開始時間 = item.開始時間,
});
}
}
#endregion
}
}
我要請假記錄修改Page.xaml
我要請假記錄修改PageViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using Prism.Navigation;
using Prism.Events;
using Prism.Services;
using XFDoggy.Models;
using XFDoggy.Repositories;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using XFDoggy.Helpers;
namespace XFDoggy.ViewModels
{
public class 我要請假記錄修改PageViewModel : BindableBase, INavigationAware
{
#region Repositories (遠端或本地資料存取)
請假紀錄Repository foo請假紀錄Repository = new 請假紀錄Repository();
#endregion
#region ViewModel Property (用於在 View 中作為綁定之用)
#region 請假紀錄項目
private 請假紀錄項目ViewModel _請假紀錄項目;
/// <summary>
/// 請假紀錄項目
/// </summary>
public 請假紀錄項目ViewModel 請假紀錄項目
{
get { return this._請假紀錄項目; }
set { this.SetProperty(ref this._請假紀錄項目, value); }
}
#endregion
#region 假別清單
private ObservableCollection<string> _假別清單 = new ObservableCollection<string>();
/// <summary>
/// 假別清單
/// </summary>
public ObservableCollection<string> 假別清單
{
get { return _假別清單; }
set { SetProperty(ref _假別清單, value); }
}
#endregion
#region 點選假別
private string _點選假別;
/// <summary>
/// 點選假別
/// </summary>
public string 點選假別
{
get { return this._點選假別; }
set { this.SetProperty(ref this._點選假別, 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 顯示刪除按鈕
private bool _顯示刪除按鈕;
/// <summary>
/// 顯示刪除按鈕
/// </summary>
public bool 顯示刪除按鈕
{
get { return this._顯示刪除按鈕; }
set { this.SetProperty(ref this._顯示刪除按鈕, value); }
}
#endregion
#endregion
#region Field 欄位
新增或修改Enum 新增或修改 = 新增或修改Enum.修改;
string fooID = "";
請假紀錄項目 foo請假紀錄項目;
LeaveRecord foo請假紀錄;
public DelegateCommand 刪除按鈕Command { get; set; }
public DelegateCommand 儲存按鈕Command { get; set; }
public DelegateCommand 取消按鈕Command { get; set; }
private readonly INavigationService _navigationService;
private readonly IEventAggregator _eventAggregator;
public readonly IPageDialogService _dialogService;
#endregion
#region Constructor 建構式
public 我要請假記錄修改PageViewModel(INavigationService navigationService, IEventAggregator eventAggregator,
IPageDialogService dialogService)
{
//
#region 相依性服務注入的物件
_dialogService = dialogService;
_eventAggregator = eventAggregator;
_navigationService = navigationService;
#endregion
#region 頁面中綁定的命令
刪除按鈕Command = new DelegateCommand(async () =>
{
var fooAnswer = await _dialogService.DisplayAlertAsync("警告", "您確定刪除這筆請假記錄", "確定", "取消");
if (fooAnswer == true)
{
foo請假紀錄 = await MainHelper.請假紀錄Manager.LookupAsync(請假紀錄項目.ID);
if (foo請假紀錄 != null)
{
await MainHelper.請假紀錄Manager.DeleteAsync(foo請假紀錄);
}
await _navigationService.GoBackAsync();
}
});
儲存按鈕Command = new DelegateCommand(async () =>
{
var fooCheck = await Check資料完整性();
if (fooCheck == false)
{
return;
}
if (新增或修改 == 新增或修改Enum.新增)
{
foo請假紀錄 = new LeaveRecord
{
假別 = 點選假別,
申請人 = 請假紀錄項目.申請人,
職務代理人 = 請假紀錄項目.職務代理人,
請假日期 = 請假紀錄項目.請假日期,
請假時數 = 請假紀錄項目.請假時數,
請假理由 = 請假紀錄項目.請假理由,
開始時間 = 請假紀錄項目.開始時間,
};
await MainHelper.請假紀錄Manager.InsertAsync(foo請假紀錄);
await _navigationService.GoBackAsync();
}
else
{
foo請假紀錄 = await MainHelper.請假紀錄Manager.LookupAsync(請假紀錄項目.ID);
if (foo請假紀錄 != null)
{
foo請假紀錄.假別 = 點選假別;
foo請假紀錄.申請人 = 請假紀錄項目.申請人;
foo請假紀錄.職務代理人 = 請假紀錄項目.職務代理人;
foo請假紀錄.請假日期 = 請假紀錄項目.請假日期;
foo請假紀錄.請假時數 = 請假紀錄項目.請假時數;
foo請假紀錄.請假理由 = 請假紀錄項目.請假理由;
foo請假紀錄.開始時間 = 請假紀錄項目.開始時間;
await MainHelper.請假紀錄Manager.UpdateAsync(foo請假紀錄);
}
else
{
return;
}
await _navigationService.GoBackAsync();
}
await _navigationService.GoBackAsync();
});
取消按鈕Command = new DelegateCommand(async () =>
{
var fooAnswer = await _dialogService.DisplayAlertAsync("警告", "您確定取消這筆請假記錄", "確定", "取消");
if (fooAnswer == true)
{
await _navigationService.GoBackAsync();
}
});
#endregion
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public async void OnNavigatedTo(NavigationParameters parameters)
{
if (parameters.ContainsKey("ID") == true)
{
fooID = parameters["ID"] as string;
新增或修改 = 新增或修改Enum.修改;
Title = "請假記錄修改";
顯示刪除按鈕 = true;
}
else
{
fooID = "";
新增或修改 = 新增或修改Enum.新增;
Title = "請假記錄新增";
顯示刪除按鈕 = false;
}
await Init();
}
#endregion
#region Navigation Events (頁面導航事件)
#endregion
#region 設計時期或者執行時期的ViewModel初始化
#endregion
#region 相關事件
#endregion
#region 相關的Command定義
#endregion
#region 其他方法
/// <summary>
/// 進行要顯示資料的初始化
/// </summary>
private async Task Init()
{
#region 假別清單項目初始化
假別清單.Clear();
假別清單.Add("請選擇");
假別清單.Add("特休假");
假別清單.Add("事假");
假別清單.Add("病假");
#endregion
await foo請假紀錄Repository.Read();
if (string.IsNullOrEmpty(fooID) == true)
{
請假紀錄項目 = new 請假紀錄項目ViewModel();
點選假別 = 假別清單[0];
}
else
{
foo請假紀錄 = await MainHelper.請假紀錄Manager.LookupAsync(fooID);
//foo請假紀錄項目 = foo請假紀錄Repository.請假紀錄.FirstOrDefault(x => x.ID == fooID);
if (foo請假紀錄 == null)
{
請假紀錄項目 = new 請假紀錄項目ViewModel();
點選假別 = 假別清單[0];
}
else
{
請假紀錄項目 = new 請假紀錄項目ViewModel
{
ID = foo請假紀錄.Id,
假別 = foo請假紀錄.假別,
申請人 = foo請假紀錄.申請人,
職務代理人 = foo請假紀錄.職務代理人,
請假日期 = foo請假紀錄.請假日期,
請假時數 = foo請假紀錄.請假時數,
請假理由 = foo請假紀錄.請假理由,
開始時間 = foo請假紀錄.開始時間,
};
var fooObj = 假別清單.FirstOrDefault(x => x == 請假紀錄項目.假別);
if (fooObj != null)
{
點選假別 = fooObj;
}
else
{
點選假別 = 假別清單[0];
}
}
}
}
private async Task<bool> Check資料完整性()
{
if (點選假別 == 假別清單[0])
{
await _dialogService.DisplayAlertAsync("警告", "您尚未選擇請假類別", "確定");
return false;
}
return true;
}
#endregion
}
}
實際執行結果
檢測後端資料庫儲存結果
在 Azure 行動應用後端專案,新增需要身分認證的才能夠存取的資料表
建立資料傳輸物件 (DTO) 類別
- 為了要開始產生一個我們自訂的資料表,我們需要定義資料表控制器, 設定資料表控制器需要三個步驟︰
public class WorkLog : EntityData
{
public string 專案名稱 { get; set; }
public DateTime 日期 { get; set; }
public double 處理時間 { get; set; }
public string 工作內容 { get; set; }
}
建立資料表控制器。
使用 Azure 行動應用程式資料表控制器
[Authorize]
public class WorkLogController : TableController<WorkLog>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
XamarinAzureDayContext context = new XamarinAzureDayContext();
DomainManager = new EntityDomainManager<WorkLog>(context, Request);
}
// GET tables/WorkLog
public IQueryable<WorkLog> GetAllWorkLog()
{
return Query();
}
// GET tables/WorkLog/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<WorkLog> GetWorkLog(string id)
{
return Lookup(id);
}
// PATCH tables/WorkLog/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<WorkLog> PatchWorkLog(string id, Delta<WorkLog> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/WorkLog
public async Task<IHttpActionResult> PostWorkLog(WorkLog item)
{
WorkLog current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/WorkLog/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteWorkLog(string id)
{
return DeleteAsync(id);
}
}
加入 Migration
Add-Migration WorkLog
{
"message": "Authorization has been denied for this request."
}
發佈到 Azure 雲端上
PostMan 測試資料
本地端
Azure
這個練習範例專案,請參考
在 Azure 中,加入 Facebook / Google 身分驗證設定
申請 Facebook 身分驗證的ID與密鑰
申請 Google 身分驗證的ID與密鑰
Azure Mobile 驗證/授權 設定
Facebook
Google
完成設定
建立資料表會用到的資料模型
public class WorkLog
{
public string Id { get; set; }
public string 專案名稱 { get; set; }
public DateTime 日期 { get; set; }
public double 處理時間 { get; set; }
public string 工作內容 { get; set; }
}
在 ViewMolde 加入這個資料表的 CRUD 行動應用服務功能。
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
}
public class 填寫工作日報表記錄修改PageViewModel : BindableBase, INavigationAware
{
#region Repositories (遠端或本地資料存取)
//工作日報表Repository foo工作日報表Repository = new 工作日報表Repository();
IMobileServiceTable<WorkLog> WorkLogTable = MainHelper.client.GetTable<WorkLog>();
#endregion
#region ViewModel Property (用於在 View 中作為綁定之用)
#region 工作日報表項目
private 工作日報表項目ViewModel _工作日報表項目;
/// <summary>
/// 工作日報表項目
/// </summary>
public 工作日報表項目ViewModel 工作日報表項目
{
get { return this._工作日報表項目; }
set { this.SetProperty(ref this._工作日報表項目, 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 顯示刪除按鈕
private bool _顯示刪除按鈕;
/// <summary>
/// 顯示刪除按鈕
/// </summary>
public bool 顯示刪除按鈕
{
get { return this._顯示刪除按鈕; }
set { this.SetProperty(ref this._顯示刪除按鈕, value); }
}
#endregion
#endregion
#region Field 欄位
新增或修改Enum 新增或修改 = 新增或修改Enum.修改;
string fooID = "";
WorkLog 工作日報表Model;
public DelegateCommand 儲存按鈕Command { get; set; }
public DelegateCommand 取消按鈕Command { get; set; }
public DelegateCommand Can刪除按鈕Command { get; set; }
private readonly INavigationService _navigationService;
private readonly IEventAggregator _eventAggregator;
public readonly IPageDialogService _dialogService;
#endregion
#region Constructor 建構式
public 填寫工作日報表記錄修改PageViewModel(INavigationService navigationService, IEventAggregator eventAggregator,
IPageDialogService dialogService)
{
#region 相依性服務注入的物件
_dialogService = dialogService;
_eventAggregator = eventAggregator;
_navigationService = navigationService;
#endregion
#region 頁面中綁定的命令
Can刪除按鈕Command = new DelegateCommand(async () =>
{
var fooAnswer = await _dialogService.DisplayAlertAsync("警告", "您確定刪除這筆請假記錄", "確定", "取消");
if (fooAnswer == true)
{
工作日報表Model = await WorkLogTable.LookupAsync(fooID);
if (工作日報表Model != null)
{
await WorkLogTable.DeleteAsync(工作日報表Model);
_eventAggregator.GetEvent<漢堡按鈕啟動或隱藏Event>().Publish(true);
await _navigationService.GoBackAsync();
}
else
{
return;
}
}
},
() =>
{
//設定這個命令在何種狀態下可以被執行
return 新增或修改 == 新增或修改Enum.新增 ? false : true;
}
);
儲存按鈕Command = new DelegateCommand(async () =>
{
var fooCheck = await Check資料完整性();
if (fooCheck == false)
{
return;
}
if (新增或修改 == 新增或修改Enum.新增)
{
工作日報表Model = new WorkLog();
工作日報表Model.專案名稱 = 工作日報表項目.專案名稱;
工作日報表Model.工作內容 = 工作日報表項目.工作內容;
工作日報表Model.日期 = 工作日報表項目.日期;
工作日報表Model.處理時間 = 工作日報表項目.處理時間;
await WorkLogTable.InsertAsync(工作日報表Model);
_eventAggregator.GetEvent<漢堡按鈕啟動或隱藏Event>().Publish(true);
await _navigationService.GoBackAsync();
return;
}
else
{
工作日報表Model = await WorkLogTable.LookupAsync(fooID);
if (工作日報表Model != null)
{
工作日報表Model.Id = 工作日報表項目.ID;
工作日報表Model.專案名稱 = 工作日報表項目.專案名稱;
工作日報表Model.工作內容 = 工作日報表項目.工作內容;
工作日報表Model.日期 = 工作日報表項目.日期;
工作日報表Model.處理時間 = 工作日報表項目.處理時間;
await WorkLogTable.UpdateAsync(工作日報表Model);
_eventAggregator.GetEvent<漢堡按鈕啟動或隱藏Event>().Publish(true);
await _navigationService.GoBackAsync();
return;
}
else
{
return;
}
}
});
取消按鈕Command = new DelegateCommand(async () =>
{
var fooAnswer = await _dialogService.DisplayAlertAsync("警告", "您確定取消這筆請假記錄", "確定", "取消");
if (fooAnswer == true)
{
_eventAggregator.GetEvent<漢堡按鈕啟動或隱藏Event>().Publish(true);
await _navigationService.GoBackAsync();
}
});
#endregion
_eventAggregator.GetEvent<漢堡按鈕啟動或隱藏Event>().Publish(false);
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public async void OnNavigatedTo(NavigationParameters parameters)
{
if (parameters.ContainsKey("ID") == true)
{
fooID = parameters["ID"] as string;
新增或修改 = 新增或修改Enum.修改;
Title = "工作日報記錄修改";
顯示刪除按鈕 = true;
}
else
{
fooID = "";
新增或修改 = 新增或修改Enum.新增;
Title = "工作日報記錄新增";
顯示刪除按鈕 = false;
}
// 要求檢查這個命令是否可以被使用
Can刪除按鈕Command.RaiseCanExecuteChanged();
await Init();
}
#endregion
#region Navigation Events (頁面導航事件)
#endregion
#region 設計時期或者執行時期的ViewModel初始化
#endregion
#region 相關事件
#endregion
#region 相關的Command定義
#endregion
#region 其他方法
private async Task Init()
{
if (string.IsNullOrEmpty(fooID) == true)
{
工作日報表項目 = new 工作日報表項目ViewModel();
}
else
{
工作日報表Model = await WorkLogTable.LookupAsync(fooID);
if (工作日報表Model == null)
{
工作日報表項目 = new 工作日報表項目ViewModel();
}
else
{
工作日報表項目 = new 工作日報表項目ViewModel
{
ID = 工作日報表Model.Id,
專案名稱 = 工作日報表Model.專案名稱,
工作內容 = 工作日報表Model.工作內容,
日期 = 工作日報表Model.日期,
是否顯示日期區塊 = true,
當日累計工時 = 0,
處理時間 = 工作日報表Model.處理時間,
};
}
}
}
private async Task<bool> Check資料完整性()
{
if (string.IsNullOrEmpty(工作日報表項目.專案名稱) == true)
{
await _dialogService.DisplayAlertAsync("警告", "專案名稱 必須要輸入", "確定");
return false;
}
return true;
}
#endregion
}
設計身分驗證機制
定義與實作 IAuthenticate
定義 IAuthenticate
public interface IAuthenticate
{
Task<bool> Authenticate(MobileServiceAuthenticationProvider p登入方式);
}
- 將下列靜態成員新增至 App 類別,以使用平台特定實作來初始化介面。
public static IAuthenticate Authenticator { get; private set; }
public static void Init(IAuthenticate authenticator)
{
Authenticator = authenticator;
}
using Microsoft.WindowsAzure.MobileServices;
using Prism.Unity;
using System.Threading.Tasks;
using XFDoggy.Helpers;
using XFDoggy.Models;
using XFDoggy.Views;
namespace XFDoggy
{
using Microsoft.WindowsAzure.MobileServices;
using Prism.Unity;
using System.Threading.Tasks;
using XFDoggy.Helpers;
using XFDoggy.Models;
using XFDoggy.Views;
namespace XFDoggy
{
#region Azure Mobile 身分驗證介面
public interface IAuthenticate
{
Task<bool> Authenticate(MobileServiceAuthenticationProvider p登入方式);
}
#endregion
public partial class App : PrismApplication
{
#region Azure Mobile 身分驗證介面初始化
public static IAuthenticate Authenticator { get; private set; }
public static void Init(IAuthenticate authenticator)
{
Authenticator = authenticator;
}
#endregion
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
#region 進行離線資料庫初始化
MainHelper.AzureMobileOfflineInit();
#endregion
NavigationService.NavigateAsync($"LoginPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<MDPage>();
Container.RegisterTypeForNavigation<NaviPage>();
Container.RegisterTypeForNavigation<OnCall電話Page>();
Container.RegisterTypeForNavigation<我要請假HomePage>();
Container.RegisterTypeForNavigation<我要請假記錄修改Page>();
Container.RegisterTypeForNavigation<差旅費用申請HomePage>();
Container.RegisterTypeForNavigation<差旅費用申請紀錄修改Page>();
Container.RegisterTypeForNavigation<最新消息HomePage>();
Container.RegisterTypeForNavigation<填寫工作日報表HomePage>();
Container.RegisterTypeForNavigation<填寫工作日報表記錄修改Page>();
Container.RegisterTypeForNavigation<LoginPage>();
Container.RegisterTypeForNavigation<LoginPage>();
}
}
}
在 Android 中,實作 IAuthenticate
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IAuthenticate
{
#region Azure 行動應用之身分驗證用到程式碼
// Define a authenticated user.
private MobileServiceUser user;
public async Task<bool> Authenticate(MobileServiceAuthenticationProvider p登入方式)
{
// 驗證結果
var success = false;
// 要顯示的訊息
var message = string.Empty;
try
{
// 每次登入的時候,都要強制重新登入,不使用上次登入的資訊
CookieManager.Instance.RemoveAllCookie();
await MainHelper.client.LogoutAsync();
// 呼叫 Azure Mobile 用戶端的 LoginAsync 方法,依據指定的登入類型,進行身分驗證登入
user = await MainHelper.client.LoginAsync(this, p登入方式);
if (user != null)
{
message = string.Format("you are now signed-in as {0}.", user.UserId);
success = true;
}
}
catch (Exception ex)
{
message = ex.Message;
}
// 顯示登入成功或者失敗.
//AlertDialog.Builder builder = new AlertDialog.Builder(this);
//builder.SetMessage(message);
//builder.SetTitle("Sign-in result");
//builder.Create().Show();
return success;
}
#endregion
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.tabs;
ToolbarResource = Resource.Layout.toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
#region Azure 行動應用之身分驗證用到程式碼
// 將在 Android 原生平台實作的 IAuthenticate 物件,指定到 核心PCL 專案內
App.Init((IAuthenticate)this);
#endregion
LoadApplication(new App(new AndroidInitializer()));
}
}
在 iOS 中,實作 IAuthenticate
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IAuthenticate
{
#region Azure Mobile 身分驗證
// Define a authenticated user.
private MobileServiceUser user;
public async Task<bool> Authenticate(MobileServiceAuthenticationProvider p登入方式)
{
var success = false;
var message = string.Empty;
try
{
// Sign in with Facebook login using a server-managed flow.
if (user == null)
{
// 每次登入的時候,都要強制重新登入,不使用上次登入的資訊
foreach (var cookie in NSHttpCookieStorage.SharedStorage.Cookies)
{
NSHttpCookieStorage.SharedStorage.DeleteCookie(cookie);
}
await MainHelper.client.LogoutAsync();
// 呼叫 Azure Mobile 用戶端的 LoginAsync 方法,依據指定的登入類型,進行身分驗證登入
user = await MainHelper.client.LoginAsync(UIApplication.SharedApplication.KeyWindow.RootViewController, p登入方式);
if (user != null)
{
message = string.Format("You are now signed-in as {0}.", user.UserId);
success = true;
}
}
}
catch (Exception ex)
{
message = ex.Message;
}
// Display the success or failure message.
UIAlertView avAlert = new UIAlertView("Sign-in result", message, null, "OK", null);
avAlert.Show();
return success;
}
#endregion
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(new iOSInitializer()));
// http://stackoverflow.com/questions/24521355/azure-mobile-services-invalid-operation-exception-platform-specific-assembly-n
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();
return base.FinishedLaunching(app, options);
}
}
將驗證加入登入頁面中
public LoginPageViewModel(INavigationService navigationService, IPageDialogService dialogService)
{
_dialogService = dialogService;
_navigationService = navigationService;
登入Command = new DelegateCommand(async () =>
{
await _navigationService.NavigateAsync($"xf:///MDPage?Menu={MenuItemEnum.關於.ToString()}/NaviPage/MainPage?title=多奇數位創意有限公司");
});
Facebook登入Command = new DelegateCommand(async () =>
{
var fooResult = await App.Authenticator.Authenticate(Microsoft.WindowsAzure.MobileServices.MobileServiceAuthenticationProvider.Facebook);
if(fooResult == true)
{
await _navigationService.NavigateAsync($"xf:///MDPage?Menu={MenuItemEnum.關於.ToString()}/NaviPage/MainPage?title=多奇數位創意有限公司");
}
});
Google登入Command = new DelegateCommand(async () =>
{
var fooResult = await App.Authenticator.Authenticate(Microsoft.WindowsAzure.MobileServices.MobileServiceAuthenticationProvider.Google);
if (fooResult == true)
{
await _navigationService.NavigateAsync($"xf:///MDPage?Menu={MenuItemEnum.關於.ToString()}/NaviPage/MainPage?title=多奇數位創意有限公司");
}
});
}
實際執行結果
在 Azure 中,加入訊息推播設定
建立通知中樞
設定 Goolge Firebase Cloud Messaging (FCM) 的連結
將 Firebase 設定 綁定到 Azure 推播中樞
第一次準備工作
加入 Google Cloud Messaging Client 元件
修改 原生 Android 專案
設計自己的 GcmServiceBase 類別
using Android.App;
using Android.Content;
using Android.Media;
using Android.Support.V4.App;
using Android.Util;
using Gcm.Client;
using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using XFDoggy.Helpers;
[assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
//GET_ACCOUNTS is only needed for android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
namespace XFDoggy.Droid.Infrastructure
{
[Service]
public class GcmService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
public GcmService()
: base(PushHandlerBroadcastReceiver.SENDER_IDS) { }
protected override void OnRegistered(Context context, string registrationId)
{
Log.Verbose("PushHandlerBroadcastReceiver", "GCM Registered: " + registrationId);
RegistrationID = registrationId;
var push = MainHelper.client.GetPush();
MainActivity.CurrentActivity.RunOnUiThread(() => Register(push, null));
}
public async void Register(Microsoft.WindowsAzure.MobileServices.Push push, IEnumerable<string> tags)
{
try
{
const string templateBodyGCM = "{\"data\":{\"message\":\"$(messageParam)\"}}";
JObject templates = new JObject();
templates["genericMessage"] = new JObject
{
{ "body", templateBodyGCM}
};
await push.RegisterAsync(RegistrationID, templates);
Log.Info("Push Installation Id", push.InstallationId.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
Debugger.Break();
}
}
protected override void OnMessage(Context context, Intent intent)
{
Log.Info("PushHandlerBroadcastReceiver", "GCM Message Received!");
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
//Store the message
var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private);
var edit = prefs.Edit();
edit.PutString("last_msg", msg.ToString());
edit.Commit();
string message = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(message))
{
createNotification("多奇數位創意通知", message);
return;
}
string msg2 = intent.Extras.GetString("msg");
if (!string.IsNullOrEmpty(msg2))
{
createNotification("New hub message!", msg2);
return;
}
createNotification("Unknown message details", msg.ToString());
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show ui
var uiIntent = new Intent(this, typeof(MainActivity));
//Use Notification Builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
//Create the notification
//we use the pending intent, passing our ui intent over which will get called
//when the notification is tapped.
var notification = builder.SetContentIntent(PendingIntent.GetActivity(this, 0, uiIntent, 0))
.SetSmallIcon(Android.Resource.Drawable.SymActionEmail)
.SetTicker(title)
.SetContentTitle(title)
.SetContentText(desc)
//Set the notification sound
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
//Auto cancel will remove the notification once the user touches it
.SetAutoCancel(true).Build();
//Show the notification
notificationManager.Notify(1, notification);
}
protected override void OnUnRegistered(Context context, string registrationId)
{
Log.Error("PushHandlerBroadcastReceiver", "Unregistered RegisterationId : " + registrationId);
}
protected override void OnError(Context context, string errorId)
{
Log.Error("PushHandlerBroadcastReceiver", "GCM Error: " + errorId);
}
}
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]
public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
{
public static string[] SENDER_IDS = new string[] { "300143732939" };
}
}
修改 MainActivity
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Prism.Unity;
using Microsoft.Practices.Unity;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MobileServices;
using XFDoggy.Helpers;
using Gcm.Client;
using XFDoggy.Droid.Infrastructure;
[assembly: UsesPermission(Android.Manifest.Permission.AccessNetworkState)]
[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
[assembly: UsesPermission(Android.Manifest.Permission.CallPhone)]
namespace XFDoggy.Droid
{
[Activity(Label = "多奇數位創意", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IAuthenticate
{
#region Firebase 的推播設定用程式碼
// Create a new instance field for this activity.
static MainActivity instance = null;
// Return the current activity instance.
public static MainActivity CurrentActivity
{
get
{
return instance;
}
}
#endregion
#region Azure 行動應用之身分驗證用到程式碼
// Define a authenticated user.
private MobileServiceUser user;
public async Task<bool> Authenticate(MobileServiceAuthenticationProvider p登入方式)
{
// 驗證結果
var success = false;
// 要顯示的訊息
var message = string.Empty;
try
{
// 呼叫 Azure Mobile 用戶端的 LoginAsync 方法,依據指定的登入類型,進行身分驗證登入
user = await MainHelper.client.LoginAsync(this, p登入方式);
if (user != null)
{
message = string.Format("you are now signed-in as {0}.", user.UserId);
success = true;
}
}
catch (Exception ex)
{
message = ex.Message;
}
// 顯示登入成功或者失敗.
//AlertDialog.Builder builder = new AlertDialog.Builder(this);
//builder.SetMessage(message);
//builder.SetTitle("Sign-in result");
//builder.Create().Show();
return success;
}
#endregion
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.tabs;
ToolbarResource = Resource.Layout.toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
#region Azure 行動應用之身分驗證用到程式碼
// 將在 Android 原生平台實作的 IAuthenticate 物件,指定到 核心PCL 專案內
App.Init((IAuthenticate)this);
#endregion
#region Firebase 的推播設定用程式碼
// Set the current instance of MainActivity.
instance = this;
#endregion
LoadApplication(new App(new AndroidInitializer()));
#region Firebase 的推播設定用程式碼
try
{
// Check to ensure everything's set up right
GcmClient.CheckDevice(this);
GcmClient.CheckManifest(this);
// Register for push notifications
System.Diagnostics.Debug.WriteLine("Registering...");
GcmClient.Register(this, PushHandlerBroadcastReceiver.SENDER_IDS);
}
catch (Java.Net.MalformedURLException)
{
CreateAndShowDialog("There was an error creating the client. Verify the URL.", "Error");
}
catch (Exception e)
{
CreateAndShowDialog(e.Message, "Error");
}
#endregion
}
#region Firebase 的推播設定用程式碼
private void CreateAndShowDialog(String message, String title)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetMessage(message);
builder.SetTitle(title);
builder.Create().Show();
}
#endregion
#region Firebase 的推播設定用程式碼
#endregion
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
}
執行與測試
- 您需要有安裝 Google Play 服務的裝置,才能夠進行底下的測試