XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2017/04/17

Xamarin.Forms 與 Azure Mobile App Lab 6

在 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="&#xf067;"
                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"
  }
]

沒有留言:

張貼留言