XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2016/11/02

Xamarin.Forms 如何在資料繫結中,使用數值轉換器將來源資料經過轉換傳到目標

數值轉換器 (Value COnverter) 在開發以 XAML 為基礎的專案,並且在進行資料繫結的時候,是相當重要的一個應用,在這份筆記中,將會使用數值轉換器,針對所綁定的字串來源,經過數值轉換器之後,就可以轉換成為 Color 物件,並且綁定到目標控制項的屬性上。

如何開發數值轉換器的功能

  1. 建立新類別,使用 IValueConverter 來實作兩個方法
  2. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 這個方法是最為重要的,通常,也只會實作這個方法。
    在這個方法內,資料繫結功能,會將來源資料傳入到 value 這個參數內,此時,只需要判斷這個物件的值是否為所需要的型別,並且值的內容為何,此時,就可以根據傳入的值,來回傳適當的顏色。
  3. 在頁面內,需要在 ResourceDictionary 內,宣告要產生這個數值轉換器,使用這個方式來宣告。
    <local:ColorTypeToColorConverter x:Key="ColorTypeToColorConverter" />
    如此,在這個頁面之內,若需要使用這個數值轉換器,就可以使用 x:Key 所定義的值,引用這個數值轉換器。
  4. 在 BoxView 的 Color 屬性中,定義其資料繫結,如下所示:
    Color="{Binding ColorType, Converter={StaticResource ColorTypeToColorConverter}}"
    在上方的 XAML 資料繫結宣告中,會將 Color 這個屬性與 ViewModel 內的 ColorType 的屬性綁定在一起,這個 ColorType 屬性為 string 類型,但是,經過了 Convert 轉換之後,就會回傳 Color 的物件。
IValueConverter

ColorTypeToColorConverter.cs

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

namespace XFIValueConverter
{
    class ColorTypeToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var foo = value as string;
            if (foo == "1")
            {
                return Color.Red;
            }
            else if (foo == "2")
            {
                return Color.Blue;
            }
            else if (foo == "3")
            {
                return Color.Green;
            }
            else
            {
                return Color.Black;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             xmlns:local="clr-namespace:XFIValueConverter"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="XFIValueConverter.Views.MainPage"
             Title="MainPage">

    <ContentPage.Resources>
        <ResourceDictionary>
            <local:ColorTypeToColorConverter x:Key="ColorTypeToColorConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <Label Text="輸入數值,可以變換顏色" />
        <Entry Text="{Binding ColorType, Mode=TwoWay}" />
        <BoxView Color="{Binding ColorType, Converter={StaticResource ColorTypeToColorConverter}}"
             WidthRequest="150" HeightRequest="150" />
    </StackLayout>
</ContentPage>

MainPageViewModel.cs

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XFIValueConverter.ViewModels
{
    public class MainPageViewModel : BindableBase, INavigationAware
    {
        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        #region ColorType
        private string colorType;
        /// <summary>
        /// ColorType
        /// </summary>
        public string ColorType
        {
            get { return this.colorType; }
            set { this.SetProperty(ref this.colorType, value); }
        }
        #endregion

        public MainPageViewModel()
        {

        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            if (parameters.ContainsKey("title"))
                Title = (string)parameters["title"] + " and Prism";
        }
    }
}

2016/11/01

更新到 Xcode 8.1 之 Visual Studio 開發 Xamarin.Forms 注意事項

  • 這份文件是於 2016/11/01 撰寫,描述一般當 Mac 電腦上的 Xcode 要更新的時候,Windows & Mac 電腦上,也需要進行更新內容的步驟。
  • 請將 Windows 下的 Visual Studio 2015 內的 Xamarin Toolkit 更新到最新版本
    Xamarin 4.2.0.719
    Xamarin.Android 7.0.1.6
    Xamarin.iOS 10.2.0.4
  • 請更新 iOS Simulator for Windows 更新到 10/28 的最新版本
  • 請更新 Mac 電腦上的 Xcode 到 8.1 版本,於更新完成後,請記得要開啟 Xcode,此時,會出現底下對話窗,請點選 Install
    Install additional required components?
    Xcode requires additional components to support running and debugging. Choose install to add required components.
    XcodeInstallRequire
    XcodeInstallComponents
    更新後的 Xcode 版本為 8.1 (8B62)
    Xcode81
  • 接著,請更新 Mac 電腦上的 Xamarin Studio 到最新版本
    Xamarin Studio 6.1.1 (Build 17)
    Xamarin.Android 7.0.1.3
    Xamarin.iOS 10.2.0.4
  • 開啟 VS2015,建立一個新的 Prism 專案
    當要進行 Xamarin Mac Agent 連線的時候,請記得先將 Visual Studio 輸出視窗切換為 Xamarin,再進行 Xamarin Mac Agent的連線登入;當在進行連線的時候,您會在輸出視窗中,看到有更新的動作,此時,要稍等一下,就會完成與 Mac 電腦的連線。
    VS2015的輸出
    在 iOS 專案上,使用滑鼠右鍵,選擇 建置,建置這個 iOS 專案,若沒有發現錯誤,此時,就可以切換為 iOS Simulator,接著在模擬器上執行看看。
    當 iOS Simulator 開始執行的時候,也會有更新
    iOSSimulator更新
  • 建置 iOS 專案的結果
1>------ 已開始全部重建: 專案: PrismUnityApp9, 組態: Debug Any CPU ------
1>  PrismUnityApp9 -> C:\Vulcan\GitBook\Temp\PrismUnityApp9\PrismUnityApp9\PrismUnityApp9\bin\Debug\PrismUnityApp9.dll
2>------ 已開始全部重建: 專案: PrismUnityApp9.iOS, 組態: Debug iPhoneSimulator ------
2>  Generated session id: a8b64c786fe72fb61874d0cabb16ad64
2>  Generated build app name: PrismUnityApp9.iOS
2>  Connecting to Mac server 192.168.1.110...
2>  PrismUnityApp9.iOS -> C:\Vulcan\GitBook\Temp\PrismUnityApp9\PrismUnityApp9\PrismUnityApp9.iOS\bin\iPhoneSimulator\Debug\PrismUnityApp9.iOS.exe
2>  Detected signing identity:
2>    Bundle Id: com.yourcompany.PrismUnityApp9
2>    App Id: com.yourcompany.PrismUnityApp9
========== 全部重建: 2 成功、0 失敗、 0 略過 ==========

2016/10/24

Xamarin.Forms 開發一個可以反覆使用的使用者控制項 User Control

在這份筆記中,將會開發一個可以反覆使用的使用者控制項 User Control的專案範例。
當在頁面中使用這個擴充使用者控制項的時候,可以透過 ViewModel 來定義這個使用者控制項的類型與商業處理邏輯,例如,在底下的範例中,將設定這個使用者自訂控制項為一個可以輸入身分證字號的文字輸入盒,當尚未輸入任何身分證字號的時候,會自動出現 請輸入身分證字號 這個浮水印文字,當正在輸入身分證字號的時候,只要輸入的文字不符合身分證字號規範,此時,最右方會出現紅色底的 X 警告提示;若輸入的文字就是一個身分證字號,那麼,最右方會出現綠色底的 V 打勾視覺,表示現在這個欄位輸入一個符合規定的內容。
下面動畫圖片,呈現了應用程式執行的時候,實際操作的過程。
這個是範例專案
想要做出這樣的效果,您需要先建立一個 ContentPage 頁面,並且修正根節點為 ContentView,記得,Code Behind 的部分,要修改繼承類別,從 ContentPage 為 ContentView。
由於這個範例專案,不論在 XAML 與 C# ViewModel 上,都有加入適當的註解,所以,就不再多做解釋;若您對於這個範例專案做法有問題,歡迎提出來討論。

使用者控制項 - 具有輸入格式檢查能力

這個使用者控制項的 XAML 定義如下

MyEntry.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="UserCtrlEntry.UserControls.MyEntry">
    <Grid
        RowSpacing="0" ColumnSpacing="0"
        >
        <!--這個 Grid 切割成兩個垂直列,前者佔據剩下所有空間,後者佔據 50單位-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="50" />
        </Grid.ColumnDefinitions>

        <!--使用者輸入文字地方的底色設定-->
        <BoxView 
            Grid.Row="0" Grid.Column="0"
            VerticalOptions="Fill" HorizontalOptions="Fill"
            Color="#44f4dda6"
            />

        <!--使用者輸入文字的控制項,會自動依類型顯示浮水印文字-->
        <Entry
            Grid.Row="0" Grid.Column="0"
            Placeholder="{Binding MyPlaceholder}" 
            Text="{Binding MyEntryText}"
            />

        <!--提示輸入內容是否正確的顏色方框,紅色,輸入錯誤,綠色,輸入格是正確-->
        <BoxView 
            Grid.Row="0" Grid.Column="1"
            WidthRequest="50"
            VerticalOptions="Fill"
            Color="{Binding ValueCorrectBoxBackground}"
            />

        <!--標是這個文字輸入是否正確,正確:V 不正確:X-->
        <Label
            Grid.Row="0" Grid.Column="1"
            HorizontalOptions="Center" VerticalOptions="Center"
            FontAttributes="Bold"
            FontSize="Large"
            TextColor="White"
            Text="{Binding ValueCorrectSymbol}" />
    </Grid>
</ContentView>
這個使用者控制項用的 ViewModel 如下

MyEntryViewModel.cs

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Xamarin.Forms;

namespace UserCtrlEntry.ViewModels
{
    public class MyEntryViewModel : BindableBase
    {

        #region MyPlaceholderType
        private string _MyPlaceholderType;
        /// <summary>
        /// 這個文字輸入盒的類型,指定不同的類型,文字輸入盒會有不同的能力表現出來
        /// </summary>
        public string MyPlaceholderType
        {
            get { return this._MyPlaceholderType; }
            set { this.SetProperty(ref this._MyPlaceholderType, value); }
        }
        #endregion

        #region MyPlaceholder
        private string _MyPlaceholder;
        /// <summary>
        /// 文字輸入盒沒有任何文字輸入的時候,要顯示甚麼浮水印文字
        /// </summary>
        public string MyPlaceholder
        {
            get { return this._MyPlaceholder; }
            set { this.SetProperty(ref this._MyPlaceholder, value); }
        }
        #endregion

        #region ValueCorrect
        private bool _ValueCorrect;
        /// <summary>
        /// 輸入文字的格式驗證是否正確
        /// </summary>
        public bool ValueCorrect
        {
            get { return this._ValueCorrect; }
            set { this.SetProperty(ref this._ValueCorrect, value); }
        }
        #endregion

        #region ValueCorrectSymbol
        private string _ValueCorrectSymbol;
        /// <summary>
        /// 要顯示格式是否正確的文字,要更漂亮,可以使用圖片或者 Font Awesome取代
        /// </summary>
        public string ValueCorrectSymbol
        {
            get { return this._ValueCorrectSymbol; }
            set { this.SetProperty(ref this._ValueCorrectSymbol, value); }
        }
        #endregion

        #region MyEntryText
        private string _MyEntryText;
        /// <summary>
        /// 儲存使用者輸入文字的地方
        /// </summary>
        public string MyEntryText
        {
            get { return this._MyEntryText; }
            set
            {
                this.SetProperty(ref this._MyEntryText, value);
                if (MyPlaceholderType == "Name")
                {
                    if (MyEntryText.Length < 6)
                    {
                        ValueCorrect = false;
                    }
                    else
                    {
                        ValueCorrect = true;
                    }
                    更新文字格式驗證符號與背景顏色();
                }
                else if (MyPlaceholderType == "ID")
                {
                    ValueCorrect = 驗證身份證字號格式(MyEntryText);
                    更新文字格式驗證符號與背景顏色();
                }
                else if (MyPlaceholderType == "Email")
                {
                    ValueCorrect = 驗證電子郵件格式(MyEntryText);
                    更新文字格式驗證符號與背景顏色();
                }
            }
        }
        #endregion

        #region ValueCorrectBoxBackground
        private Color _ValueCorrectBoxBackground;
        /// <summary>
        /// 使用者輸入文字格式是否正確的背景顏色定義
        /// </summary>
        public Color ValueCorrectBoxBackground
        {
            get { return this._ValueCorrectBoxBackground; }
            set { this.SetProperty(ref this._ValueCorrectBoxBackground, value); }
        }
        #endregion


        public MyEntryViewModel()
        {
            // 進行建構式的屬性初始化
            ValueCorrect = false;
            ValueCorrectSymbol = "X";
            MyPlaceholderType = "";
            MyEntryText = "";
            ValueCorrectBoxBackground = Color.Red;
        }

        public void 更新文字格式驗證符號與背景顏色()
        {
            if (ValueCorrect == false)
            {
                ValueCorrectSymbol = "X";
                ValueCorrectBoxBackground = Color.Red;
            }
            else
            {
                ValueCorrectSymbol = "V";
                ValueCorrectBoxBackground = Color.Green;
            }
        }
        public void 更新文字輸入盒的浮水印文字設定(string pMyPlaceholderType)
        {
            MyPlaceholderType = pMyPlaceholderType;
            if (MyPlaceholderType == "Name")
            {
                MyPlaceholder = "請輸入姓名";
            }
            else if (MyPlaceholderType == "ID")
            {
                MyPlaceholder = "請輸入身分證字號";
            }
            else if (MyPlaceholderType == "Email")
            {
                MyPlaceholder = "請輸入電子郵件信箱";
            }
            else
            {
                MyPlaceholder = "";
            }
        }

        public bool 驗證身份證字號格式(string arg_Identify)
        {
            var d = false;
            if (arg_Identify.Length == 10)
            {
                arg_Identify = arg_Identify.ToUpper();
                if (arg_Identify[0] >= 0x41 && arg_Identify[0] <= 0x5A)
                {
                    var a = new[] { 10, 11, 12, 13, 14, 15, 16, 17, 34, 18, 19, 20, 21, 22, 35, 23, 24, 25, 26, 27, 28, 29, 32, 30, 31, 33 };
                    var b = new int[11];
                    b[1] = a[(arg_Identify[0]) - 65] % 10;
                    var c = b[0] = a[(arg_Identify[0]) - 65] / 10;
                    for (var i = 1; i <= 9; i++)
                    {
                        b[i + 1] = arg_Identify[i] - 48;
                        c += b[i] * (10 - i);
                    }
                    if (((c % 10) + b[10]) % 10 == 0)
                    {
                        d = true;
                    }
                }
            }
            return d;
        }

        public bool 驗證電子郵件格式(string strIn)
        {
            // Return true if strIn is in valid e-mail format.
            return Regex.IsMatch(strIn,
                   @"^(?("")("".+?""@)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])@))" +
                   @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$");
        }
    }
}

使用剛剛建立的使用者控制項頁面

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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             xmlns:UserControls="clr-namespace:UserCtrlEntry.UserControls"
             x:Class="UserCtrlEntry.Views.MainPage"
             Title="MainPage">
    <StackLayout 
        Margin="20,0"
        HorizontalOptions="Center" VerticalOptions="Center">
        <Label Text="使用者控制項與商業邏輯包裝範例" HorizontalOptions="Center" />
        <!--宣告一個使用者控制項,並且綁定 ViewModel 內的 Name Property,設定這個文字輸入盒,具有輸入字串長度檢查能力-->
        <UserControls:MyEntry BindingContext="{Binding Name}"/>
        <!--宣告一個使用者控制項,並且綁定 ViewModel 內的 ID Property,設定這個文字輸入盒,具有輸入身分證字號檢查能力-->
        <UserControls:MyEntry BindingContext="{Binding ID}"/>
        <!--宣告一個使用者控制項,並且綁定 ViewModel 內的 Email Property,設定這個文字輸入盒,具有輸入電子郵件格式檢查能力-->
        <UserControls:MyEntry BindingContext="{Binding Email}"/>
    </StackLayout>
</ContentPage>

MainPageViewModel.cs

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;

namespace UserCtrlEntry.ViewModels
{
    public class MainPageViewModel : BindableBase, INavigationAware
    {
        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }


        #region Name
        private MyEntryViewModel _Name;
        /// <summary>
        /// 這個屬性,將會用於綁定到 姓名 欄位輸入
        /// </summary>
        public MyEntryViewModel Name
        {
            get { return this._Name; }
            set { this.SetProperty(ref this._Name, value); }
        }
        #endregion


        #region ID
        private MyEntryViewModel _ID;
        /// <summary>
        /// 這個屬性,將會用於綁定到 身分證字號 欄位輸入
        /// </summary>
        public MyEntryViewModel ID
        {
            get { return this._ID; }
            set { this.SetProperty(ref this._ID, value); }
        }
        #endregion


        #region Email
        private MyEntryViewModel _Email;
        /// <summary>
        /// 這個屬性,將會用於綁定到 電子郵件 欄位輸入
        /// </summary>
        public MyEntryViewModel Email
        {
            get { return this._Email; }
            set { this.SetProperty(ref this._Email, value); }
        }
        #endregion


        public MainPageViewModel()
        {
            // 進行三個文字輸入盒要綁定的 ViewModel 的物件初始化
            Name = new MyEntryViewModel();
            Name.更新文字輸入盒的浮水印文字設定("Name");
            ID = new MyEntryViewModel();
            ID.更新文字輸入盒的浮水印文字設定("ID");
            Email = new MyEntryViewModel();
            Email.更新文字輸入盒的浮水印文字設定("Email");
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            if (parameters.ContainsKey("title"))
                Title = (string)parameters["title"] + " and Prism";
        }
    }
}