XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2018/06/17

Xamarin.Essentials 體驗 2 : Device Information 裝置資訊

根據 Xamarin.Essentials 官方文件中指出:Xamarin.Essentials 跨平台應用程式開發介面的開發人員提供他們的行動應用程式。透過 Xamarin.Essentials 的套件幫忙,可以將這些功能整合到您的應用程式:加速計 應用程式資訊 電池 剪貼簿 指南針 資料傳輸 裝置顯示資訊 裝置資訊 電子郵件 檔案系統的協助程式 手電筒 地理編碼 地理位置 迴轉儀 磁力計 開啟瀏覽器 電話撥號員 喜好設定 螢幕鎖定 安全儲存體 SMS 文字轉換語音 追蹤版本 震動。
我們首先來體驗關於 Device Information 裝置資訊 這項功能,在其官方文件中有提到:DeviceInfo類別會提供執行應用程式之裝置的相關資訊,因此,我們來練習如何使用這些功能。
因此,可以參考底下的專案範例,該專案範例位於 https://github.com/vulcanlee/xamarin-forms-sample2018/tree/master/XFESDeviceInformation

建立測試專案

  • 首先,我們先使用 Prism Template Pack 擴充功能所提供的專案樣板,建立起一個 Xamarin.Forms 專案,在這裡我們僅選擇 Android / iOS / UWP 類型的專案;接著,我們需要把 PropertyChanged.Fody NuGet 套件安裝到 .NET Standard 專案類別庫內,並且安裝 FodyWeavers.xml 檔案。
  • 緊接著,我們要開始安裝 Xamarin.Essentials NuGet 套件到所有的專案內,在這裡,請使用滑鼠右擊方案節點,選擇 管理方案的 NuGet 套件 選項
  • 請在 NuGet - 解決方案 的視窗中,輸入要搜尋的套件名稱 : Xamarin.Essentials
    並且,請勾選 包括搶鮮版 的文字檢查盒,並且安裝這個套件;我寫這篇文章的時候,Xamarin.Essentials 的最新版本為 0.7.0.17 版本,因此,我們安裝這個版本。
    Xamarin Essentials NuGet Installation
  • 當 Xamarin.Essentials 套件安裝完成之後,您將會發現到在 Visual Studio 2017 的錯誤視窗中,出現了底下錯誤訊息
Warning
偵測到 Xamarin.Android.Support.Compat 的版本衝突。請直接從專案參考套件,以解決此問題。 
 XFESFileSystem.Android -> Xamarin.Essentials 0.7.0.17-preview -> Xamarin.Android.Support.CustomTabs 27.0.2 -> Xamarin.Android.Support.Compat (= 27.0.2) 
 XFESFileSystem.Android -> Xamarin.Android.Support.Design 25.4.0.2 -> Xamarin.Android.Support.Compat (= 25.4.0.2).
偵測到 Xamarin.Android.Support.Compat 的版本衝突
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"
             x:Class="XFESDeviceInformation.Views.MainPage"
             Title="Device Information 裝置資訊">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Label Text="{Binding Model, StringFormat='DeviceInfo.Model : {0}'}"/>
        <Label Text="{Binding Manufacturer, StringFormat='DeviceInfo.Manufacturer : {0}'}"/>
        <Label Text="{Binding Name, StringFormat='DeviceInfo.Name : {0}'}"/>
        <Label Text="{Binding VersionString, StringFormat='DeviceInfo.VersionString : {0}'}"/>
        <Label Text="{Binding Platform, StringFormat='DeviceInfo.Platform : {0}'}"/>
        <Label Text="{Binding Idiom, StringFormat='DeviceInfo.Idiom : {0}'}"/>
        <Label Text="{Binding DeviceType, StringFormat='DeviceInfo.DeviceType : {0}'}"/>
    </StackLayout>

</ContentPage>
  • 請打開 MainPageViewModel.cs 這個檔案
    在這個檔案中,我們將會在 OnNavigatedTo 事件中,使用 DeviceInfo 類別各個屬性,將其值取出來,設定要綁定到頁面的屬性物件中。
C#
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XFESDeviceInformation.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    using Xamarin.Essentials;

    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Model { get; set; }
        public string Manufacturer { get; set; }
        public string Name { get; set; }
        public string VersionString { get; set; }
        public string Platform { get; set; }
        public string Idiom { get; set; }
        public string DeviceType { get; set; }
        private readonly INavigationService _navigationService;

        public MainPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;

        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            Model = DeviceInfo.Model;
            Manufacturer = DeviceInfo.Manufacturer;
            Name = DeviceInfo.Name;
            VersionString = DeviceInfo.VersionString;
            Platform = DeviceInfo.Platform;
            Idiom = DeviceInfo.Idiom;
            DeviceType = DeviceInfo.DeviceType.ToString();
        }

    }
}
  • 根據官方文件上的說明
    Platforms 平台,DeviceInfo.Platform 相互關聯會對應至作業系統與常數字串。
    • DeviceInfo.Platforms.iOS – iOS
    • DeviceInfo.Platforms.Android – Android
    • DeviceInfo.Platforms.UWP – UWP
    • DeviceInfo.Platforms.Unsupported – 不支援
    Idioms 慣用語,DeviceInfo.Idiom 相互關聯的常數字串,將應用程式對應至類型的裝置上正在執行。
    • DeviceInfo.Idioms.Phone – 電話
    • DeviceInfo.Idioms.Tablet – 平板電腦
    • DeviceInfo.Idioms.Desktop – 桌面
    • DeviceInfo.Idioms.TV – 電視
    • DeviceInfo.Idioms.Unsupported – 不支援
    Device Type 裝置類型,DeviceInfo.DeviceType 列舉型別來判斷應用程式是否執行下列項目的上實體或虛擬裝置將相互關聯。 模擬器虛擬裝置。
  • Android 平台執行結果
    Xamarin Essentials
  • UWP 平台執行結果
    Xamarin Essentials NuGet Installation



2018/06/16

Xamarin.Forms 的 Switch 控制項的資料異動偵測與設計方法

在這篇文章中,我們將會來探討這樣的頁面使用情境該如何進行設計,請參考下圖,在這個頁面中,將會有三個 Switch 控制項,我們希望能夠做到這三個控制項彼此之間的設定選擇是互斥的,也就是,類似一個 Radio Button 的變形應用;當我們選擇了 選項2 之後,選項1就會變成沒有被選擇狀態,同樣的,當選擇了 選項3,此時,選項1,選項2 就會變成沒有被選擇的狀態。
Xamarin.Forms Switch Controller
由於 Switch 這個控制項並沒有相對應可以使用命令 Command(其實,這個控制像是沒有提供任何的 命令),這個時候,我們第一個想到的處理方式,就是使用 Prism 提供的 Behavior 行為機制 , 您可參考 Using the EventToCommandBehavior ,設定當 Switch 的 Toggled 事件被觸發的時候,便可以執行 ViewModel 中指定的 DelegateCommand 的委派方法。
不過,在這篇文章中,我們將要來說明另外一種做法,那就是使用我們安裝好的 PropertyChanged.Fody 這個 NuGet 套件所提供的功能,那就是我們可以針對所綁定的資料,設計一個事件方法,一旦這個綁定的資料有所異動的時候,相對應的事件方法就會被執行。

建立測試專案

  • 首先,我們先使用 Prism Template Pack 擴充功能所提供的專案樣板,建立起一個 Xamarin.Forms 專案,在這裡我們僅選擇 Android / iOS / UWP 類型的專案;接著,我們需要把 PropertyChanged.Fody NuGet 套件安裝到 .NET Standard 專案類別庫內,並且安裝 FodyWeavers.xml 檔案。
  • 我們來設計這篇文章中使用的範例頁面,在這個頁面中,我們使用了三個 Switch 控制項,這三個 Switch 控制項彼此是互斥的,您可以視為這三個 Switch 控制項的行為,會類似於我們 Windows 程式中的 Radio Button;最後,我們會把您選擇的結果,顯示在最後一個 Label 控制項內。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XFDataChanged.Views.MainPage"
             Title="Switch 控制項的資料異動偵測與設計方法">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <StackLayout Orientation="Horizontal">
            <Label Text="選項 1"/>
            <Switch IsToggled="{Binding Option1}"/>
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="選項 2"/>
            <Switch IsToggled="{Binding Option2}"/>
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="選項 3"/>
            <Switch IsToggled="{Binding Option3}"/>
        </StackLayout>

        <Label Text="{Binding YourChoice, StringFormat='你選擇的是 {0}'}"/>
    </StackLayout>

</ContentPage>
  • 我們開始進行 ViewModel 的 C# 商業邏輯程式碼的設計,在這裡,我們需要設計三個所綁定 Switch 的 C# Property 屬性物件,當它本身的值有異動的話,我們需要有個相對應的事件方法被呼叫與執行。不過,我們對於這樣的事件方法,它的名稱需要使用指定的命名規格,那就是
    On你的綁定屬性Changed
    其中前的 On 與最後的 Changed 是固定不變的,我們來看個例子,若您的綁定屬性物件為 Option1 ,當使用者在頁面中進行操作,變動了 Option1 的值,這個時候,您的 ViewModel 內的
    OnOption1Changed
    方法,就會被自動呼叫了。
    為了要能夠讓 Switch 的運作表現如同 Radio 的效果,我們同時也設計了 SetOption 方法
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XFDataChanged.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public bool Option1 { get; set; }
        public bool Option2 { get; set; }
        public bool Option3 { get; set; }
        public string YourChoice { get; set; }
        private readonly INavigationService _navigationService;

        public MainPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;

        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }
        public void OnOption1Changed()
        {
            SetOption(nameof(Option1));
        }
        public void OnOption2Changed()
        {
            SetOption(nameof(Option2));
        }
        public void OnOption3Changed()
        {
            SetOption(nameof(Option3));
        }
        public void OnNavigatedTo(NavigationParameters parameters)
        {
            ResetOptions();
        }

        public void ResetOptions()
        {
            SetOption(nameof(Option1));
        }
        public bool CorrelationAction { get; set; }
        public void SetOption(string choiceOption)
        {
            if (CorrelationAction == true) return;
            CorrelationAction = true;
            switch (choiceOption)
            {
                case nameof(Option1):
                    Option1 = true;
                    Option2 = false;
                    Option3 = false;
                    break;
                case nameof(Option2):
                    Option1 = false;
                    Option2 = true;
                    Option3 = false;
                    break;
                case nameof(Option3):
                    Option1 = false;
                    Option2 = false;
                    Option3 = true;
                    break;
                default:
                    break;
            }
            YourChoice = choiceOption;
            CorrelationAction = false;
        }
    }
}



Xamarin.Essentials 體驗 1 : File System Helpers 檔案系統存取

根據 Xamarin.Essentials 官方文件中指出:Xamarin.Essentials 跨平台應用程式開發介面的開發人員提供他們的行動應用程式。透過 Xamarin.Essentials 的套件幫忙,可以將這些功能整合到您的應用程式:加速計 應用程式資訊 電池 剪貼簿 指南針 資料傳輸 裝置顯示資訊 裝置資訊 電子郵件 檔案系統的協助程式 手電筒 地理編碼 地理位置 迴轉儀 磁力計 開啟瀏覽器 電話撥號員 喜好設定 螢幕鎖定 安全儲存體 SMS 文字轉換語音 追蹤版本 震動。
我們首先來體驗關於 File System Helpers 檔案系統的協助程式 這項功能,因此,可以參考底下的專案範例,該專案範例位於 https://github.com/vulcanlee/xamarin-forms-sample2018/tree/master/XFESFileSystem

建立測試專案

  • 首先,我們先使用 Prism Template Pack 擴充功能所提供的專案樣板,建立起一個 Xamarin.Forms 專案,在這裡我們僅選擇 Android / iOS / UWP 類型的專案;接著,我們需要把 PropertyChanged.Fody NuGet 套件安裝到 .NET Standard 專案類別庫內,並且安裝 FodyWeavers.xml 檔案。
  • 緊接著,我們要開始安裝 Xamarin.Essentials NuGet 套件到所有的專案內,在這裡,請使用滑鼠右擊方案節點,選擇 管理方案的 NuGet 套件 選項
  • 請在 NuGet - 解決方案 的視窗中,輸入要搜尋的套件名稱 : Xamarin.Essentials
    並且,請勾選 包括搶鮮版 的文字檢查盒,並且安裝這個套件;我寫這篇文章的時候,Xamarin.Essentials 的最新版本為 0.7.0.17 版本,因此,我們安裝這個版本。
    Xamarin Essentials NuGet Installation
    Xamarin Essentials NuGet Installation
  • 當 Xamarin.Essentials 套件安裝完成之後,您將會發現到在 Visual Studio 2017 的錯誤視窗中,出現了底下錯誤訊息
Warning
偵測到 Xamarin.Android.Support.Compat 的版本衝突。請直接從專案參考套件,以解決此問題。 
 XFESFileSystem.Android -> Xamarin.Essentials 0.7.0.17-preview -> Xamarin.Android.Support.CustomTabs 27.0.2 -> Xamarin.Android.Support.Compat (= 27.0.2) 
 XFESFileSystem.Android -> Xamarin.Android.Support.Design 25.4.0.2 -> Xamarin.Android.Support.Compat (= 25.4.0.2).
  • 現在讓我們來修正這個錯誤,在這裡,我使用了 Visual Studio Code 開啟了 Android 專案下的 .csproj 檔案,請搜尋這個關鍵字 PackageReference Include="Xamarin.Essentials"
    此時,您將會看到底下的內容
XML
  <ItemGroup>
    <PackageReference Include="Xamarin.Essentials">
      <Version>0.7.0.17-preview</Version>
    </PackageReference>
    <PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
    <PackageReference Include="Xamarin.Android.Support.Design" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v4" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="25.4.0.2" />
    <PackageReference Include="Prism.Unity.Forms" Version="7.0.0.396" />
  </ItemGroup>
  • 我們需要將 Xamarin.Android.Support.xxx 的套件,全部都變更成從 "25.4.0.2" 到 "27.0.2.1"
    修改完成結果如下所示,請進行存檔該 .csproj 檔案,並且回到 Visual Studio 2017 下,此時,將會重新載入這個 Android 專案
XML
  <ItemGroup>
    <PackageReference Include="Xamarin.Essentials">
      <Version>0.7.0.17-preview</Version>
    </PackageReference>
    <PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
    <PackageReference Include="Xamarin.Android.Support.Design" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v4" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="27.0.2.1" />
    <PackageReference Include="Prism.Unity.Forms" Version="7.0.0.396" />
  </ItemGroup>
  • 現在,我們需要安裝 Xamarin.Android.Support.CustomTabs 這個套件到 Android 專案下,在這個時間點,我們將會安裝最新版本的 "27.0.2.1"
    Xamarin Essentials NuGet Installation
  • 如此,最初產生的錯誤訊息便會消失了。
  • 我們將開始要進行 View ViewModel Model 的設計了,首先,我們開啟 MainPage.xaml 這個檔案,底下是我們測試的頁面 XAML 語法
    這個頁面相當的精簡,有兩個文字輸入盒,用來讓使用者輸入帳號或者密碼,接著,有三個按鈕,可以清空剛剛輸入的內容、將輸入的帳號與密碼儲存到檔案內或者從檔案內讀取出來。在這裡,我們將會要把檔案寫到應用程式的 sandbox 沙箱資料夾內。
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"
             x:Class="XFESFileSystem.Views.MainPage"
             Title="Xamarin.Essentials: 檔案系統的協助程式">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Entry Text="{Binding Account}"
               Placeholder="請輸入帳號"/>
        <Entry Text="{Binding Password}" IsPassword="True"
               Placeholder="請輸入密碼"/>
        <StackLayout
            Orientation="Horizontal">
            <Button Text="清空" Command="{Binding CleanCommand}"/>
            <Button Text="儲存" Command="{Binding SaveCommand}"/>
            <Button Text="讀取" Command="{Binding ReadCommand}"/>
        </StackLayout>
    </StackLayout>

</ContentPage>
  • 請打開 MainPageViewModel.cs 這個檔案
    在這個檔案中,我們定義一個類別 MyUser,其目的是要把讀取或者寫入到檔案的資料,先填入到這個類別的物件內,緊接著透過 Json.NET 套件,將這個物件進行 JSON 的序列化或者反序列化;也就是說,當要寫入到檔案前,我們先取得該物件的 JSON 序列化文字,緊接著把這些字串寫入到檔案內;若要讀取出來的話,則反向操作,先從檔案讀出文字,接著將 JSON 文字反序列化成為 .NET 物件。
    當要讀寫檔案的時候,我們需要指定該應用程式沙箱的目錄位置,我們可以透過 Xamarin.Essentials.FileSystem.AppDataDirectory 屬性來取得各個原生平台的應用程式 sandbox 目錄名稱,接著,使用 System.IO.Path.Combine 建立起該檔案的完整路徑名稱,最後,透過 .NET Standard 中的 System.IO.File 功能,進行檔案的存取。
C#
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XFESFileSystem.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Account { get; set; }
        public string Password { get; set; }
        public DelegateCommand CleanCommand { get; set; }
        public DelegateCommand SaveCommand { get; set; }
        public DelegateCommand ReadCommand { get; set; }
        private readonly INavigationService _navigationService;

        public MainPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            CleanCommand = new DelegateCommand(() =>
            {
                Account = "";
                Password = "";
            });
            SaveCommand = new DelegateCommand(() =>
            {
                var foo = new MyUser()
                {
                    Account = Account,
                    Password = Password,
                };
                var fooSerial = Newtonsoft.Json.JsonConvert.SerializeObject(foo);
                var mainDir = Xamarin.Essentials.FileSystem.AppDataDirectory;
                var fileName = System.IO.Path.Combine(mainDir, "MyData.txt");
                System.IO.File.WriteAllText(fileName, fooSerial);
            });
            ReadCommand = new DelegateCommand(() =>
            {
                var mainDir = Xamarin.Essentials.FileSystem.AppDataDirectory;
                var fileName = System.IO.Path.Combine(mainDir, "MyData.txt");
                var fooSerial= System.IO.File.ReadAllText(fileName);
                var foo = Newtonsoft.Json.JsonConvert.DeserializeObject<MyUser>(fooSerial);
                if(foo!=null)
                {
                    Account = foo.Account;
                    Password = foo.Password;
                }
            });
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }

    public class MyUser
    {
        public string Account { get; set; }
        public string Password { get; set; }
    }
}
  • Android 平台執行結果
    Xamarin Essentials
  • UWP 平台執行結果
    Xamarin Essentials NuGet Installation