XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2018/04/12

Xamarin 旋轉木馬 Carousel 控制項的使用練習教學

旋轉木馬 Carousel 控制項,這個需求是最多學員提出的問題需求,今天剛好有空,我就把如何在 Xamarin.Forms 專案中,開發出一個具有旋轉木馬效果的頁面程式,寫成這篇教學文章。

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式
在這篇教學文章中,我們將要使用 CarouselView.FormsPlugin 這個第三方 NuGet 套件來幫助我們實踐這樣的需求,若您對於 CarouselView.FormsPlugin 這個套件想要更進一步的了解可以參考 CarouselView control for Xamarin Forms 的文件說明。

建立一個 Xamarin Forms for Prism 專案

  • 首先,我們打開 Visual Studio 2017 (任何一種版本 Visual Studio Community 2017 / Visual Studio Professional 2017 / Visual Studio Enterprise 2017皆可)
  • 點選 Visual Studio 2017 功能表的 [檔案] > [新增] > [專案]
  • 當出現 [新增專案] 對話窗,請依序選擇 [已安裝] > [Visual C#] > [Prism] > [Prism Blank App (Xamarin.Forms)]
  • 請在最下方 [名稱] 文字輸入盒欄位,輸入您想要的專案名稱
  • 最後,請點選 [確定] 按鈕
  • 現在,Visual Studio 2017 將會顯示 [PRISM PROJECT WIZARD] 這個對話窗,您可以在這個對話窗中,選擇需要跨平台的目標,在這裡,您將會有三種選擇: ANDROID, iOS, UWP,請依照您的需求,勾選需要建立的專案類型。接著,請在下方 [Container] 下拉選單中,選擇 Unity 這個選項;最後,請點選 [CREATE PROJECT] 這個按鈕,請 Prism Template Pack 所提供的樣板精靈,幫助我們產生出可用於 Prism 框架的 Xamarin.Forms 方案與專案。
PRISM PROJECT WIZARD
  • 此時,Visual Studio 2017 的 Prism Template Pack 將會開始幫您建立起四個專案(假設我們在上個步驟,勾選的所有平台目標),分別是:
    • Xamarin.Android 原生專案
    • Xamarin.iOS 原生專案
    • Windows UWP 原生專案
    • .NET Standard Class Library SCL 共用類別庫專案

CarouselView.FormsPlugin 的 NuGet 套件

  • 請滑鼠右擊 SCL 專案節點,選擇 [管理方案的 NuGet 套件]
  • 點選 瀏覽 標籤頁次,並在搜尋文字輸入盒中輸入 CarouselView.FormsPlugin
  • 當出現搜尋結果之後,請勾選所有的專案檢查盒,將這個 CarouselView.FormsPlugin NuGet 套件,安裝到 SCL 的專案中
Microsoft.EntityFrameworkCore.Sqlite

開始撰寫 應用程式的 Models

  • 滑鼠右擊 SCL 專案,選擇 [加入] > [新增資料夾]
  • 在剛剛產生的資料夾節點中,輸入 Models
  • 在剛剛產生的 Models 資料夾,使用滑鼠右擊
  • 選擇 [加入] > [類別]
  • 在 [新增項目] 對話窗,在最下方了名稱欄位中,輸入 MyItem
  • 之後,請點選 [新增] 按鈕
新增項目
  • 使用底下的程式碼,替換掉剛剛產生的檔案內容
  • 在這裡,我們定義出每個旋轉木馬要顯示項目的資料模型,其中,分別為 該項目的名稱、背景顏色、圖片 URL
C# CSharp
public class MyItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public string Name { get; set; }
    public Color Background { get; set; }
    public string ImageUrl { get; set; }
}
  • 現在,請在 Views 資料夾中,滑鼠雙擊 MainPage.xaml 節點,開啟這個檔案
  • 使用底下的程式碼,替換掉 MainPage.xaml 的檔案內容
  • 從底下的 XAML 內容,我們可以看到我們使用了這個命名空間 CarouselView.FormsPlugin.Abstractions 的 CarouselViewControl 控制項,用來顯示在該頁面上,做為要顯示旋轉木馬的地方。
  • 由於旋轉木馬顯示需求,是要能夠將一群集合資料,可以採用類似旋轉木馬的效果,顯示出來,因此,我們需要定義出 ItemsSource 這個控制項屬性,將其綁定到該頁面的 檢視模型 ViewModel 上。
  • 若想要知道使用者當時轉動到哪個項目了,我們需要使用 Prism 提供的 行為 Behavior 擴充功能,使用 EventToCommandBehavior 這個行為,設定當 PositionSelected 這個事件被觸發的時候,需要執行 ViewModel 中的 MyPositionSelectedCommand 命令委派方法。
  • 在這個 MyPositionSelectedCommand 命令中,我們會到過該選轉木馬控制項的 Position 屬性,綁定到 ViewModel 中的 MyPosition C# 屬性,透過這個 MyPosition C# Property 屬性,我們就可以知道,這個時候,使用者選轉到哪個集合紀錄上。
  • 我們可以透過 CarouselViewControl.ItemTemplate 這個屬性,進行宣告這個旋轉木馬要出現的樣貌,您可以透過底下 XAML 中的 這個標記區段的 XAML 內容,進行宣告每筆紀錄要出現樣貌,當然,善用資料綁定 (Data Binding) 或者數值轉換器 (Value Converter),就可以做到每筆紀錄,根據當成 ViewModel 中實際資料內容,進而控制頁面顯示出不同的效果。
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:cv="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions"
             xmlns:behaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
             x:Class="XFCarousel.Views.MainPage"
             Title="旋轉木馬控制項使用範例">

    <Grid
        >
        <cv:CarouselViewControl
            WidthRequest="300" HeightRequest="400"
            HorizontalOptions="Center" VerticalOptions="Center"
            ItemsSource="{Binding MyItemsSource}"
            ShowArrows="true"
            ShowIndicators="true"
            Position="{Binding MyPosition}"
            Orientation="Horizontal"
            >
            <cv:CarouselViewControl.Behaviors>
                <behaviors:EventToCommandBehavior
                    EventName="PositionSelected"
                    Command="{Binding MyPositionSelectedCommand}"/>
            </cv:CarouselViewControl.Behaviors>
            <cv:CarouselViewControl.ItemTemplate>
                <DataTemplate>
                    <Grid
                        >
                        <BoxView                            
                            Color="{Binding Background}"/>
                        <Label
                            HorizontalOptions="Center" VerticalOptions="Start"
                            Text="{Binding Name}"/>
                        <Image
                            HorizontalOptions="Center" VerticalOptions="Center"
                            WidthRequest="200" HeightRequest="300"
                            Aspect="AspectFit"
                            Source="{Binding ImageUrl}"/>
                    </Grid>
                </DataTemplate>
            </cv:CarouselViewControl.ItemTemplate>
        </cv:CarouselViewControl>

        <Label
            HorizontalOptions="Center" VerticalOptions="End"
            FontSize="30"
            Text="{Binding Hint}"/>
    </Grid>

</ContentPage>
  • 請在 ViewModels 資料夾中,找到 MainPageViewModel 檔案,使用滑鼠雙擊這個檔案,打開他
  • 使用底下的程式碼,替換掉這個檔案內容
C# CSharp
public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
{
    public event PropertyChangedEventHandler PropertyChanged;
    public ObservableCollection<MyItem> MyItemsSource { get; set; } = new ObservableCollection<MyItem>();
    public int MyPosition { get; set; }
    public string Hint { get; set; }
    private readonly INavigationService _navigationService;
    public DelegateCommand MyPositionSelectedCommand { get; set; }

    public MainPageViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;

        MyPositionSelectedCommand = new DelegateCommand(() =>
        {
            Hint = $"您點選了 {MyItemsSource[MyPosition].Name}";
        });
    }

    public void OnNavigatedFrom(NavigationParameters parameters)
    {

    }

    public void OnNavigatingTo(NavigationParameters parameters)
    {

    }

    public void OnNavigatedTo(NavigationParameters parameters)
    {
        MyItemsSource.Clear();

        MyItemsSource.Add(new MyItem()
        {
            Name = "LightBlue",
            Background = Color.LightBlue,
            ImageUrl = "https://avatars2.githubusercontent.com/u/790012?s=200&v=4",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightCoral",
            Background = Color.LightCoral,
            ImageUrl = "http://icons.iconarchive.com/icons/graphicloads/100-flat/256/home-icon.png",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightCyan",
            Background = Color.LightCyan,
            ImageUrl = "http://icons.iconarchive.com/icons/graphicloads/100-flat-2/256/mobile-2-icon.png",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightGoldenrodYellow",
            Background = Color.LightGoldenrodYellow,
            ImageUrl = "http://files.softicons.com/download/game-icons/super-mario-icons-by-sandro-pereira/png/256/Mushroom%20-%20Super.png",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightGreen",
            Background = Color.LightGreen,
            ImageUrl = "http://images.all-free-download.com/images/graphiclarge/harry_potter_icon_6825007.jpg",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightSlateGray",
            Background = Color.LightSlateGray,
            ImageUrl = "http://www.icosky.com/icon/png/Movie%20%26%20TV/Doraemon/smile.png",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LimeGreen",
            Background = Color.LimeGreen,
            ImageUrl = "http://images.pocketgamer.co.uk/artwork/imgthumbs/na-owuz/10_mario_facts11.jpg",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightSalmon",
            Background = Color.LightSalmon,
            ImageUrl = "https://rocketdock.com/images/screenshots/thumbnails/Awesome_small.png",
        });
        MyItemsSource.Add(new MyItem()
        {
            Name = "LightGray",
            Background = Color.LightGray,
            ImageUrl = "http://wikiclipart.com/wp-content/uploads/2018/01/Pig-face-cute-face-finance-happy-hog-pig-piggy-icon-icon-search-engine-clip-art.png",
        });
    }

}

執行與測試

  • 設定 Android 專案為預設起始專案
  • 請重建 Android 原生專案
  • 若沒有問題,請執行這個 Android 專案
  • 您可以使用手勢操作,在旋轉木馬控制項上,左右滑動,就可以看到不同的紀錄內容,或者,可以使用 < 或者 > 這兩個標記,點選他們,也可以進行左右的滑動
  • 當您切換到一個新的紀錄,您將會看到在螢幕的最下方,會看到這個紀錄的名稱
旋轉木馬 旋轉木馬 旋轉木馬 旋轉木馬

2018/04/09

Xamarin 新手入門 Part 1 Xamarin.Android 專案開發教學

重要提醒:由於 Visual Studio 2017 的安裝過程有持續更新,因此,若想要知道最新的 Visual Studio 2017 for Xamarin 的安裝過程,可以參考 2018 Q2 最新文章 Visual Studio 2017 for Xamarin 開發環境之安裝與設定說明 (2018 Q2 版本)



èXamarin 新手入門 Part 1 Xamarin.Android 專案開發教學
Xamarin 新手入門 Part 2 Xamarin.iOS 專案開發教學
Xamarin 新手入門 Part 6 我該選擇 Xamarin.Forms 或者 Xamarin 原生方式來開發跨平台應用程式專案呢?

對於已經具備擁有 .NET / C# 開發技能的開發者,可以使用 Xamarin.Forms Toolkit 開發工具,便可以立即開發出可以在 Android / iOS 平台上執行的 App;對於要學習如何使用 Xamarin.Forms & XAML 技能,現在已經推出兩本電子書來幫助大家學這這個開發技術。
這兩本電子書內包含了豐富的逐步開發教學內容與相關觀念、各種練習範例,歡迎各位購買。
Xamarin.Forms 電子書
想要購買 Xamarin.Forms 快速上手 電子書,請點選 這裡

想要購買 XAML in Xamarin.Forms 基礎篇 電子書,請點選 這裡

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式


由於 Xamairn 開發環境不斷地進行成長與演變,所以,特別針對關於 Xamarin 的新手開發者,我這裡特別準備了一系列的新手開發練習文章,這些文章將是採用 2018 年最新的 Visual Studio 2017 與 Xamarin Toolkit 工具集所撰寫而成的;讓對於 Xamarin 有興趣的同好,可以跟著這一系列的文章,透過逐步練習,了解到 Xamarin 的好處。這一系列文章將會說明如何使用 Xamarin Android + Android XML 視覺宣告定義 來開發出原生的 Android App、使用 Xamarin iOS + Xcode Storyboard 設計頁面視覺並開發出原生的 iOS App、使用 Visual Studio 2017 提供的 Cross Platform 專案樣板並搭配 XAML 宣告式標記語言來設計頁面內容,在應用程式中絕大部分會用到的頁面畫面與商業邏輯,都將會在一個 .NET Standard Class Library 標準類別庫進行設計,如此可以將大部分的程式碼進行共用的 Xamarin.Forms 的開發方式、最後,會進行說明,如何搭配 Prism 這個開發框架,讓您的 Xamarin.Forms 專案開發上更加如虎添翼。
不論您選擇使用 Xamarin.Android 或者 Xamarin.iOS 原生開發方式進行專案設計,或者使用 Xamarin.Forms 架構來進行跨平台專案開發,對於身為 .NET / C# 的開發者而言,Xamarin 將會是您的首選,因為,在這個開發專案過程中,您不再需要面對不同的程式語言 Java / Objective-C / Swift 等等,我們可以使用 C# 這個程式語言與我們孰悉的 .NET Framework 開發環境,就可以完成您所有應用程式上會用到的商業邏輯設計。
首先,第一篇文章,將是針對 Xamarin.Android 的原生開發的練習,不過,在此之前,各位需要先行準備好您的 Visual Studio 2017 + Xamarin 開發環境;關於要如何準備您們的 Xamarin 開發環境,可以參考 第一次安裝 Visual Studio 2017 到新的作業系統上 及 Visual Studio 2017 / Xamarin 企業級行動化開發平台環境建置問題排除指引 或者 可以參考 Xamarin / Xamarin.Forms 行動跨平台 Mobile Cross-Platform 開發學習指引問答集 FAQ;若您還有 Xamarin 相關的問題,可以到 Xamarin.Forms @ Taiwan 與 Xamarin 同好一同進行討論。
您也可以先行觀看這部只有 5 分鐘的 Getting Started with Xamarin Android Deploying to Emulators 教學影片,任您可以有更清晰的了解整個過程。同樣的,您也可以觀看 Getting Started with Xamarin Android Modifying the UI 這部教學影片,了解如何設計 Android 應用程式個頁面 UI (User Interface 使用者介面)的過程。
您也可以查看微軟官方的 Xamarin 介紹文章 使用者入門系列,來了解更多關於 Xamarin Android 原生專案的開發技術與技巧。

建立一個 Xamarin Android 專案

  • 首先,我們打開 Visual Studio 2017 (任何一種版本 Visual Studio Community 2017 / Visual Studio Professional 2017 / Visual Studio Enterprise 2017皆可)
  • 點選 Visual Studio 2017 功能表的 [檔案] > [新增] > [專案]
  • 當出現 [新增專案] 對話窗,請依序選擇 [已安裝] > [Visual C#] > [Android] > [單一檢視應用程式 (Android)]
  • 請在最下方 [名稱] 文字輸入盒欄位,輸入您想要的專案名稱
  • 最後,請點選 [確定] 按鈕
Visual Studio 2017 Xamarin Android new project
  • 一旦 Visual Studio 的方案 ( Solution ) , 專案 ( Project ) 建立完成之後,您可以從 方案總管 ( Solution Explorer ) 中,看到整個 Xamarin Android 的專案結構。
方案總管
  • 其中, Assets 目錄下,可以存放一些該專案會用到的檔案,例如,SQLite 資料庫檔案、音樂或者影片檔案等等。
  • 在 Resources 目錄下,可以存放您的應用程式頁面會用到資源,例如,在整個應用程式中會用到的不同縮放比率下的圖片檔案資源;其中,這個專案預設會有一個頁面,這個頁面所要呈現的內容,將會定義在 layout 資料夾下的 Main.axml 內,我們先來查看一下這個檔案內容;這個檔案是個 XML 檔案,我們可以滑鼠雙擊 Main.xaml ,以便打開這個檔案。
  • 稍微等候一下,您將會看到如下圖的畫面,這裡就是我們要設計頁面呈現內容的地方,在左方,有工具箱視窗,這裡有各種 Android 原生的控制項 (Control / Widget),您可以使用拖拉的方式,選擇您需要的控制項,拖拉到頁面上。也就是說,所有頁面可以使用與可以看到的視覺控制項與版面配置 Layout,都與 Android SDK 上所提到的相同,因此,使用這樣的開發方式,建議若您有 Android SDK 開發經驗,則是更好,否則,還是要來了解一下 Android SDK 之相關內容;您也可以從這份 使用者介面 文件上進行深入研究。
    在設計應用程式中的每個頁面畫面內容,您可以透過拖拉工具箱的方式,從工具箱中找到您需要的控制項,拖拉到設計畫面上,若想要進行某個控制項的相關細節與特性進行設定,您可以先點選這個控制項,接著從 Visual Studio 屬性視窗中,找您想要修改的屬性,就可以進行這個控制項的特性變更,這與之前大家在於使用 Windows Forms 開發框架下的做法相同;另外就是,每個頁面所可以看到的內容,都會定義在一個 XML 檔案內,您也可以直接修改這個 XML 檔案,達到一樣的效果。
    在這中間視窗的上方,您可以看到有3個下拉選單,您可以透過這些下拉選單,改變預覽的樣貌,在下拉選單的右方,有些按鈕,可以改變這個頁面的呈現方式。
Main.xaml
  • 在中間的視窗,我們從該視窗的下方,看到兩個標籤頁次 : Designer / Source,分別可以呈現該頁面的視覺預覽畫面和這個頁面的 XML 宣告內容,所以,我們可以點選 Source 標籤頁次,您就會看到這個頁面的 XML 宣告內容。更多關於這方面的資訊,可以查看 User Interface & Navigation 網頁介紹的內容。
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/myButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
</LinearLayout>
  • 您可以使用滑鼠雙擊 Android 專案中的 Properties 節點,接著切換到 Android 資訊清單頁面標籤,在這個頁面中,您可以找到 最低 Android 版本 這個下拉選單欄位,您可以從這個下拉選單,選擇您的 Android 應用程式可以支援最低之 Android API 版本。
Xamarin Android Project Properties
  • 現在我們需要來執行這個 Xamarin Android 專案,在這裡請先確認這個專案是否已經有連結上適當的模擬器或者實體手機作為執行與測試的目標,接著,您可以按下 F5 或者工具列上的綠色三角形按鈕。
Visual Studio Toolbar

準備進行 Xamarin Android 專案的建置、執行、與除錯

  • 若您的開發環境在安裝、設定上都正確無誤,此時,您可以在 Visual Studio for Android Emulator 上,看到如左下圖的執行結果,現在,您可以點選模擬器上的按鈕五次,此時,模擬器的畫面就是出現如右下圖的結果。因此,我們可以看的出來,這個 Xamarin Android 專案,確實可以產生出可以在 Android 設備上運行的應用程式,並且,我們可以設計頁面上的各種不同使用者介面與控制項,可以與使用者進行互動,並且將我們的商業邏輯加入到這個應用程式內,如此,這個應用程式將會依照我們設計的需求邏輯來運行。
  • 更多關於要產生您的應用程式與有效地進行專案測試技巧,可以參考 部署及測試
Visual Studio for Android Emulator Visual Studio for Android Emulator

了解這個專案中的程式碼

  • 現在,讓我們按下 Shift + F5 來中止除錯執行,回到 Visual Studio 程式中。
  • 在 方案總管 視窗中,在最下方找到 MainActivity.cs 檔案,使用滑鼠雙擊這個檔案,打開它,這是一個 C# 程式碼,其內容如下所示:
  • 在底下,我們可以看到,我們透過 button 這個變數,就可以取得頁面上的按鈕物件,接著,我們進行這個按鈕物件的點擊事件的綁定,在這裡我們使用的匿名 Anonymous 方法來設計;在這個匿名方法中,我們會將總共點擊這按鈕的次數,顯示在這個按鈕上。而您可以看到,我們完完全全都是使用 C# 程式語言來做到這樣需求設計。
  • 從這個 MainActivity 類別定義程式碼中,看到這個類別是繼承 Activity 類別,而一個 Activity 就是表示該應用程式中整個螢幕畫面。我們在這個 MainActivity 類別中,使用 SetContentView 方法,使用關注點分離的 MVC 設計方法,指定這個頁面要顯示、使用到的 UI 控制項與排列方法在上面所提到的 Main.axml 檔案內;因此,這個頁面將會使用這個 XML 檔案內容的宣告事項,將 Layout / Widget 顯示在螢幕上。最後,我們透過了 FindViewById 這個方法,找到按鈕控制項的物件,並且設定該按鈕的點選事件,綁訂到我們設定為匿名委派方法 (Anonymous Delegate Method) 內,也就是說,當使用者在裝置或手機螢幕上點選了這個按鈕,我們將會把當時總共點選次數的文字,設定到這個按鈕上,如此,我們就可以從這個按鈕上,看到當時使用者總共點選這個按鈕幾次了。
關於更多的 UI 與其互動設計需求,可以參考 控制項 這份文件中的說明
C# CSharp
using Android.App;
using Android.Widget;
using Android.OS;

namespace XAQStart
{
    [Activity(Label = "XAQStart", MainLauncher = true, Icon = "@mipmap/icon")]
    public class MainActivity : Activity
    {
        int count = 1;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            Button button = FindViewById<Button>(Resource.Id.myButton);

            button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); };
        }
    }
}

進階研讀

您可以繼續來研讀 Xamarin 新手入門 Part 2 Xamarin.iOS 專案開發教學

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式


關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程




2018/04/06

Prism 使用 Unity 在 Xamarin.Forms 中,進行三種注入實作物件練習

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式

當我們使用 Prism 來開發 Xamarin.Forms 專案的時候,可以選擇不同 DI 容器 Container 的開發框架,不論您選擇的是哪一個,這些 DI 容器都可以支援 1.建構函式注入 Constructor Injection 2. 屬性注入 Property Injection 3. 方法注入 Method Injection ,這三種相依性注入途徑。在這篇文章中,我們將來練習與研究,我們如何使用 Visual Studio 2017 擴充功能的 Prism Template Pack 這個套件所提供的專案樣板,建立起一個 Xamarin.Forms 的專案,接著,我們在這個專案內的 ViewModel 來練習這三種相依性注入的用法,不過,在這裡,我們選擇的 IoC 套件是 Unity。

準備測試環境

  • 首先,我們先要使用 Prism Template Pack 建立一個 Xamarin.Forms 跨平台行動專案
  • 接著,請打開 App.xaml.cs 檔案,依照底下程式碼替換掉
  • 在新的 App.xaml.cs 檔案中,我們建立了兩個介面 IServiceA, IServiceB ,並且使用這兩個介面時做出這兩個類別 ServiceA, ServiceB
  • 為了要能夠讓 Unity IoC 容器可以自動注入 IServiceA, IServiceB 的實作物件,我們在 App.RegisterTypes 方法內,使用 containerRegistry.Register<IServiceA, ServiceA>(); , containerRegistry.Register<IServiceB, ServiceB>(); 敘述,註冊了這兩個介面與具體實作類別間的關係。
C# CSharp
public partial class App : PrismApplication
{
    /* 
     * The Xamarin Forms XAML Previewer in Visual Studio uses System.Activator.CreateInstance.
     * This imposes a limitation in which the App class must have a default constructor. 
     * App(IPlatformInitializer initializer = null) cannot be handled by the Activator.
     */
    public App() : this(null) { }

    public App(IPlatformInitializer initializer) : base(initializer) { }

    protected override async void OnInitialized()
    {
        InitializeComponent();

        await NavigationService.NavigateAsync("NavigationPage/MainPage");
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation<NavigationPage>();
        containerRegistry.RegisterForNavigation<MainPage>();

        containerRegistry.Register<IServiceA, ServiceA>();
        containerRegistry.Register<IServiceB, ServiceB>();
    }
}
public interface IServiceA
{
    string MethodA1();
}

public interface IServiceB
{
    string MethodB1();
}

public class ServiceA : IServiceA
{
    public string MethodA1()
    {
        return "MethodA1() " ;
    }
}

public class ServiceB : IServiceB
{
    public string MethodB1()
    {
        return "MethodB1() ";
    }
}
  • 現在,我們打開 MVVM 中的 View 檢視,也就是 MainPage.xaml 檔案,並且使用底下 XAML 宣告標記,替換到原先檔案內容;在這裡,我們在頁面宣告了三個文字控制項,將分別用來顯示 建構式注入、屬性注入、方法注入後的執行結果。
MainPage
<?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="XFPropInject.Views.MainPage"
             Title="Unity IoC 三種相依性注入用法">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="{Binding ConstructorInjection}" />
        <Label Text="{Binding PropertyInjection}" />
        <Label Text="{Binding MethodInjection}" />
    </StackLayout>

</ContentPage>
  • 最後,請使用底下 C# 程式碼,將 MainPageViewModel 使用底下程式碼來替換掉
  • 在這裡,_ServiceA 的實作具體物件,將會透過 Unity IoC 的建構式注入方式,將其注入到建構函式內,而到底要由 IoC 幫我們產生哪個具體實作型別物件呢?其實,在上面的步驟中,我們在 App.RegisterTypes 方法內,就有宣告這個介面與具體實作型別間的關聯
  • 另外,_ServiceB 這個介面物件,由於在其 C# 屬性 (Property) 上宣告了 [Dependency] 這個屬性 (Attribute),這個屬性可以參考 DependencyAttribute Class 文件,做進一步的深入了解;因為我們在這個 C# 屬性(Property)上宣告了 [Dependency] 這個屬性 (Attribute),所以,當使用到這個屬性的時候,將會透過屬性注入的方式,來取得這個抽象介面的具體實作物件。
  • 最後一個注入具體實作物件的方式,那就是方法注入,在這裡,我們宣告了一個方法 Initialize(IServiceB serviceMethodB),由於我們在這個方法前面,使用了 [InjectionMethod] 屬性 (Aattribute);更多關於這個屬性的說明,可以參考 InjectionMethodAttribute 文件。因為 Initialize 方法標記了 [InjectionMethod] 屬性 (Aattribute),因此,在 IoC 建立該物件之後,也會執行這個方法,也就是會幫我們注入了 IServiceB 的具體實作物件進來。
C# CSharp
public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
{
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly INavigationService _navigationService;
    public string ConstructorInjection { get; set; }
    public string PropertyInjection { get; set; }
    IServiceA _ServiceA;
    public string MethodInjection { get; set; }
    [Dependency]
    public IServiceB _ServiceB { get; set; }
    [Dependency]
    public IPageDialogService _dialogService { get; set; }
    IServiceB _ServiceMethodB;
    public MainPageViewModel(INavigationService navigationService, IServiceA serviceA)
    {
        _navigationService = navigationService;
        _ServiceA = serviceA;
    }

    public void OnNavigatedFrom(NavigationParameters parameters)
    {

    }

    public void OnNavigatingTo(NavigationParameters parameters)
    {
        ConstructorInjection = $"建構式注入 {_ServiceA.MethodA1()} Hash:{_ServiceA.GetHashCode()}";
        PropertyInjection = $"屬性注入 {_ServiceB.MethodB1()} Hash:{_ServiceB.GetHashCode()}";
        MethodInjection = $"方法注入 {_ServiceMethodB.MethodB1()} Hash:{_ServiceMethodB.GetHashCode()}";
        //await _dialogService.DisplayAlertAsync("通知", "完成相依性注入練習", "打完收工");
    }
    public void OnNavigatedTo(NavigationParameters parameters)
    {

    }

    [InjectionMethod]
    public void Initialize(IServiceB serviceMethodB)
    {
        _ServiceMethodB = serviceMethodB;
    }

}

執行結果

  • 下圖為在 Android 模擬器下執行的結果。
  • 其中我們也觀察到了,透過 屬性注入與方法注入的 IServiceB 介面之具體實作物件,並不是同一個。
Unity IoC for Xamarin.Forms