XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2016/07/14

Xamarin.Forms 萬年曆與QR-Code 條碼

萬年曆與QR-Code 條碼

在這個範例專案中,將會實作出 萬年曆與QR-Code 的功能,這兩項功能也是透過插件來完成,不過,來看看要怎麼時做出這樣的功能。
關於,QRCode 掃描,這個套件 ZXing.Net.Mobile 可以讀取 QRCode 圖片,並且回傳結果到手機中,不過,透過 Visual Studio 2015 專案樣板建立的 PCL 套件,其 PCL 版本使用 Profile111,然而 ZXing.Net.Mobile 套件使用的 PCL 版本為 Profile78。因此,當要在各平台的專案中加入這個套件,是沒有問題,不過,當要在 PCL Core 專案中,加入這個套件,卻會產生底下錯誤訊息:
錯誤 無法安裝封裝 'ZXing.Net.Mobile 2.0.4.46'。您正嘗試將此封裝安裝到以 '.NETPortable,Version=v4.5,Profile=Profile111' 為目標的專案,但該封裝不包含任何與架構相容的組件參考或內容檔。如需詳細資訊,請連絡封裝作者。 0
核心PCL無法安裝ZXing.Net.Mobile
所以,需要轉個彎來在 PCL Core 中可以呼叫這個 QRCode 掃描套件,沒錯,那就是要透過DependencyService,在下面的說明將會看到這樣的應用。

建立標籤式的樣板式頁面方案

  1. 首先,開啟您的 Visual Studio 2015
  2. 接著透過 Visual Studio 2015 功能表,選擇這些項目 檔案 > 新增 > 專案 準備新增一個專案。
  3. 接著,Visual Studio 2015 會顯示 新增專案 對話窗,請在這個對話窗上,進行選擇 Visual C# >Cross-Platform > Blank Xaml App (Xamarin.Forms Portable)
  4. 接著,在最下方的 名稱 文字輸入盒處,輸入 XFCALQR 這個名稱,最後使用滑鼠右擊右下方的 確定按鈕。
  5. 當專案建立到一半,若您的開發環境還沒有建置與設定完成 Mac 電腦與 Xamarin Studio for Mac 系統,此時會看到 Xamarin Mac Agent Instructions 對話窗出現,這個對話窗是要提醒您進行與 Mac 電腦連線設定,這是因為,您無法在 Windows 作業系統進行 iOS 相關應用程式的建立與設計工作,而是需要透過 Mac 電腦安裝的 XCode 來協助您完成這些 iOS 應用程式的建立工作。不過,這不影響您繼續開發 Xamarin.Forms 的應用程式,只不過此時,您無法編譯與執行 iOS 的應用程式。
  6. 接著會看到 新的通用Windows專案 對話視窗,此時,您只需要按下 確定 按鈕即可,此時,專案精靈會繼續完成相關平台的專案建立工作。
  7. 最後,整個新的 Xamarin.Forms 專案就建立完成了。

準備安裝套件

請依照底下說明,將這些插件安裝到方案的所有專案中。

行事曆清單來選擇日期

  • 滑鼠右擊方案節點 XFCALQR,接著選擇 管理方案的 NuGet 套件
  • 在 NuGet - 解決方案 子標籤頁次出現後,點選 瀏覽
  • 請在搜尋文字輸入盒內,輸入 XamForms.Controls.Calendar,接著點選 Xam.Plugin.Media 項目;在右方點選要安裝到全部的專案內,最後,點選 安裝 按鈕

QRCode 掃描

  • 滑鼠右擊專案 XFCALQR.Droid下的 參考 節點,滑鼠右擊該節點,接著選擇 管理 NuGet 套件, 在搜尋文字盒內輸入 ZXing.Net.Mobile 找到這個套件,點選 安裝 按鈕,安裝這個套件。
  • 滑鼠右擊專案 XFCALQR.iOS下的 參考 節點,滑鼠右擊該節點,接著選擇 管理 NuGet 套件, 在搜尋文字盒內輸入 ZXing.Net.Mobile 找到這個套件,點選 安裝 按鈕,安裝這個套件。

QR Code 介面與實作

介面 IQrCodeScanningService

  • 對於 QRCode 服務的使用,首先需要定義一個介面,請在核心PCL專案節點上,滑鼠右擊,選擇 加入類別,在 加入新項目 - XFCALQR 對話窗下的名稱欄位,輸入 IQrCodeScanningService
    新增IQrCodeScanningService
  • 請將底下 C# 程式碼複製到這個檔案內。

ˇ IQrCodeScanningService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XFCALQR
{
    public interface IQrCodeScanningService
    {
        Task<string> ScanAsync();
    }
}

Android 實作 QRCodeScannerDroid

  • 在 Android 專案下,需要實作 QRCode 服務,請在 XFCALQR.Droid 專案節點上,滑鼠右擊,選擇 加入 > 類別,在 加入新項目 - XFCALQR.Droid 對話窗下,點選 Visual C# > Class,最後在下方名稱欄位,輸入 QRCodeScannerDroid
  • 請將底下 C# 程式碼複製到這個檔案內。

QRCodeScannerDroid.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using XFCALQR.Droid;
using System.Threading.Tasks;

[assembly: Xamarin.Forms.Dependency(typeof(QrCodeScanningService))]
namespace XFCALQR.Droid
{
    public class QrCodeScanningService : IQrCodeScanningService
    {
        public async Task<string> ScanAsync()
        {
            var options = new ZXing.Mobile.MobileBarcodeScanningOptions();
            options.PossibleFormats = new List<ZXing.BarcodeFormat>()
            {
                 ZXing.BarcodeFormat.QR_CODE,
            };

            var scanner = new ZXing.Mobile.MobileBarcodeScanner();

            var scanResults = await scanner.Scan(options);
            if (scanner != null)
            {
                return scanResults.Text;
            }
            else
            {
                return null;
            }
        }
    }
}

iOS 實作 QRCodeScanneriOS

  • 在 Android 專案下,需要實作 QRCode 服務,請在 XFCALQR.iOS 專案節點上,滑鼠右擊,選擇 加入類別,在 加入新項目 - XFCALQR.Droid 對話窗下,點選 Apple > 類別,最後在下方名稱欄位,輸入 QRCodeScanneriOS
  • 請將底下 C# 程式碼複製到這個檔案內。

QRCodeScanneriOS.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UIKit;
using XFCALQR.iOS;

[assembly: Xamarin.Forms.Dependency(typeof(QrCodeScanningService))]
namespace XFCALQR.iOS
{
    public class QrCodeScanningService : IQrCodeScanningService
    {
        public async Task<string> ScanAsync()
        {
            #region 使用 ZXing.Mobile 的 MobileBarcodeScanner
            // http://stackoverflow.com/questions/34582728/how-to-find-current-uiviewcontroller-in-xamarin

            #region 取得最上層的 UIViewController
            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            while (vc.PresentedViewController != null)
            {
                vc = vc.PresentedViewController;
            }

            UIViewController appController = null;
            foreach (var foowindow in UIApplication.SharedApplication.Windows)
            {
                if (foowindow.RootViewController != null)
                {
                    appController = foowindow.RootViewController;
                    break;
                }
            }
            #endregion

            #region QRCode 掃描設定
            var options = new ZXing.Mobile.MobileBarcodeScanningOptions();
            options.PossibleFormats = new List<ZXing.BarcodeFormat>()
            {
                 ZXing.BarcodeFormat.QR_CODE,
            };
            #endregion

            #region 進行 QRCode 掃描
            var scanner = new ZXing.Mobile.MobileBarcodeScanner(vc)
            {
                TopText = "QRCode",
            };
            try
            {
                var scanResults = await scanner.Scan(options, true);
                if (scanner != null)
                {
                    return scanResults.Text;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
            #endregion

            #endregion
        }
    }
}

MainPage

  • 當上述準備工作已經就緒,接下來就可以開始在核心PCL專案內寫相關商業邏輯程式碼了。
  • 打開核心PCL 的 MainPage.xaml 檔案,將底下的 XAML 宣告定義複製到這個檔案內。
  • 這個頁面宣告內容相當簡單,兩個按鈕與兩個 Label
  • 第一個按鈕為啟動 QRCode 掃描的按鈕,當按下這個按鈕,則會出現 QRCode 掃描頁面
  • 第二個按鈕為選擇行事曆日期按鈕,當按下這個按鈕,則會出現選擇日期的頁面

MainPage.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:local="clr-namespace:XFCALQR"
             x:Class="XFCALQR.MainPage">

  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness" iOS="0, 40, 0, 0"  />
  </ContentPage.Padding>

  <StackLayout
    Orientation="Vertical">
    <Label x:Name="labelQRCode" LineBreakMode="WordWrap" />
    <Button x:Name="buttonScan" Text="掃描QRCode" Clicked="OnbuttonScanClicked" />

    <Label x:Name="labelDate" LineBreakMode="WordWrap" />
    <Button x:Name="buttonCalendar" Text="選擇日期" Clicked="OnbuttonCalendarClicked" />

  </StackLayout>
</ContentPage>
  • 請將下列 C# 程式碼複製到 MainPage.xaml.cs內。
  • 在建構式內,使用了 MessagingCenter 訂閱了一個訊息事件,也就是當使用者有選擇某個日期之後,其相對應的 callback 就會執行,在這裡,將會取出透過訊息中心所傳遞過來的日期參數,並且將其顯示在指定 Label 上。
  • 在 掃描QRCode 按鈕事件內, 使用了相依性注入服務 DependencyService 取得當時行動平台實作的 QRCode 掃描物件,並且呼叫 ScanAsync 這個方法,取得QRCode掃描結果,並且顯示到指定的Label
  • 在 選擇日期 按鈕事件內,則是透過 Navigation.PushModalAsync 方法,開啟一個對話窗頁面,該頁面為 CalendarPage

MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace XFCALQR
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            MessagingCenter.Subscribe<選擇日期訊息, DateTime>(this, "選擇日期", (sender, arg) => {
                labelDate.Text = arg.ToString();
            });
        }

        async void OnbuttonScanClicked(object sender, EventArgs e)
        {
            labelQRCode.Text = "";
            var fooQRCode = await DependencyService.Get<IQrCodeScanningService>().ScanAsync();
            if (string.IsNullOrEmpty(fooQRCode) == false)
            {
                labelQRCode.Text = fooQRCode;
            }
        }

        async void OnbuttonCalendarClicked(object sender, EventArgs e)
        {
            labelDate.Text = "";
            var CalendarPage = new CalendarPage();
            await Navigation.PushModalAsync(CalendarPage);
        }

    }
}

行事曆頁面 CalendarPage

  • 請在核心PCL節點使用滑鼠右擊該節點,選擇 加入 > 新增項目,當 加入新項目 - XFCALQR 對話窗出現後,點選 Visual C# > Cross-Platform > Forms Xaml Page,最後在下方名稱欄位的文字輸入盒內,輸入 CalendarPage
  • 請將底下 XAML 宣告定義複製到這個檔案內。
  • 在這個行事曆頁面中,新增了一個命名空間 (namespace) controls,它的定義為 "clr-namespace:XamForms.Controls;assembly=XamForms.Controls.Calendar",它的定義是使用分號作為分隔,前半部標示出這個組件的命名空間(在C#內),後半部則是標明這個組件的名稱。
    日後,當您要在 XAML 內引用其他組件 (Assembly) 內的控制項或者內容,可以可這樣的使用方式。
  • 這個頁面使用 StackLayout 版面配置來定位相關控制項要出現的位置,這裡是使用垂直排列的方式。
  • 想要顯示行事曆套件的控制項,可以使用這個方式來宣告 <controls:Calendar />
  • 最後,宣告兩個按鈕,分別讓使用者確定選取某日期或者取消這次選取動作。

CalendarPage.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:controls="clr-namespace:XamForms.Controls;assembly=XamForms.Controls.Calendar"
             x:Class="XFCALQR.CalendarPage">

  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" WinPhone="20,20,20,20" />
  </ContentPage.Padding>

  <Grid>
    <StackLayout
      Orientation="Vertical"
      >
      <controls:Calendar
        x:Name="cal"
        Padding="10,30,10,0"
        StartDay="Monday"
        SelectedBorderWidth="4"
        DisabledBorderColor="Black"
        />

      <StackLayout
        Orientation="Horizontal"
        HorizontalOptions="CenterAndExpand"
      >
        <Button Text="OK" Clicked="OnOKClicked" />
        <Button Clicked="OnCancelClicked" Text="Cancel" />
      </StackLayout>
    </StackLayout>

  </Grid>
</ContentPage>

CalendarPage.cs

  • 在 CalendarPage 頁面的 code-behind 程式碼中,分別定義了這兩個按鈕的處理事件。
  • 對於 OK 按鈕的處理事件中,先判對使用者是否有點選行事曆上的某個日期,若有,則透過訊息中心(MessagingCenter),將使用者所點選的日期,透過訊息中心傳送出去;當有訂閱該訊息事件的程式碼則會收到該訊息通知,也就會知道使用者選擇了哪個日期。
    不論選擇日期是否有存在,都會使用 await Navigation.PopModalAsync(); 返回到首頁頁面
  • 對於 Cancel 按鈕,是很簡單的使用 await Navigation.PopModalAsync(); 返回到首頁頁面
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace XFCALQR
{
    public partial class CalendarPage : ContentPage
    {
        public DateTime? 選擇日期 { get; set; } = null;
        public CalendarPage()
        {
            InitializeComponent();
        }

        async void OnOKClicked(object sender, EventArgs e)
        {
            if (cal.SelectedDate.HasValue == true)
            {
                選擇日期 = cal.SelectedDate;
                MessagingCenter.Send<選擇日期訊息, DateTime>(new 選擇日期訊息(), "選擇日期", 選擇日期.Value);
            }
            await Navigation.PopModalAsync();

        }

        async void OnCancelClicked(object sender, EventArgs e)
        {
            選擇日期 = null;
            await Navigation.PopModalAsync();
        }
    }
}

選擇日期訊息

  • 在這裡建立一個空的類別,作為訊息中心的主要通訊類別
  • 請在核心PCL專案節點使用滑鼠右擊,選擇 加入 > 類別,接著在名稱欄位旁的文字輸入盒中,填入選擇日期訊息。最後,請將底下程式碼複製到剛剛產生的檔案內。

選擇日期訊息.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XFCALQR
{
    public class 選擇日期訊息
    {
    }
}

實際執行畫面

Android 執行結果

請在方案總管內,滑鼠右擊 XFCALQR.Droid 專案,選擇 設定為起始專案,接著按下 F5 開始執行。
Android執行結果1
Android執行結果2
Android執行結果3
Android執行結果4

佈署注意事項

iOS 執行結果

請在方案總管內,滑鼠右擊 XFCALQR.iOS 專案,選擇 設定為起始專案,接著按下 F5 開始執行。
iOS執行結果1
iOS執行結果2
iOS執行結果3
iOS執行結果4

佈署注意事項

2 則留言:

  1. Hi, Vulcan Lee
    我在使用您所提供的QRCode範例碼進行實作時發現,在實體機器上iOS可正確掃描並產生QRCode,可是在Android機器上會閃退。
    請問在專案中或實體機器上是否有甚麼權限需要設定呢,謝謝!

    回覆刪除
  2. Hi Wilson

    您可以參考底下官方套件文件,在Android平台下,需要加入適當的使用權限。

    https://github.com/Redth/ZXing.Net.Mobile

    回覆刪除