XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2019/06/18

如何使用 On_PropertyName_Changed 與 附加屬性 Attached Properties 和行為 Behavior,來得知 Entry 與 Editor 已經輸入了多少文字

如何使用 On_PropertyName_Changed 與 附加屬性 Attached Properties 和行為 Behavior,來得知 Entry 與 Editor 已經輸入了多少文字

當在進行 Xamarin.Forms 專案開發的時候,有些時候需要得知 Entry 或者 Editor 這兩種檢視或者稱為控制向,使用者已經輸入了多少內容或者字數,通常可以透過這兩個控制項的 Entry.TextChanged 事件或者 Editor.TextChanged 事件來得知使用者已經輸入了甚麼內容,這樣就可以計算出使用者已經輸入多少文字內容,可是,若要使用事件的話,就需要使用該頁面的 Code Behind 的方式來撰寫 C# 事件委派方法,讓使用者輸入文字的時候,可以觸發這個事件,以便進行計算字數工作。
在使用 Prism 這類 MVVM 開發框架的設計模式的時候,幾乎所有的商業邏輯都會撰寫到 View (頁面) 的 ViewModel (檢視模型) 上,如此,要如何得知使用者已經在 Entry / Editor 檢視中輸入甚麼內容呢?這裡將會提供底下方式來解決此一需求。
這篇文章的範例程式原始碼,可以從 GitHub 取得。

PropertyChanged.Fody 套件提供的 屬性 變更觸發事件

在 PropertyChanged.Fody 的網頁中,將會有一篇關於 On_PropertyName_Changed 文章,告訴 Xamarin.Forms 開發者如何在 ViewModel 中來撰寫出當所綁定的屬性值有異動的時候,將會觸發一個 ViewModel 上的委派方法。
在此應用中,將會在頁面中使用底下 XAML 進行宣告一個 Entry 檢視,其中,當使用者輸入的文字內容,將會自動綁定到 ViewModel 類別內的 C# Property 屬性 EntryChangedInputText 上,而 ViewModel 類別內的 C# Property 屬性 EntryChangedInputTextLength 將會是該輸入文字的字數數值。
xaml
        <Label Text="{Binding EntryChangedInputTextLength}"/>
        <Entry
            Text="{Binding EntryChangedInputText}"/>
現在,請在該頁面的檢視模型 ViewModel 中,定義一個方法,其方法名稱必須為 On屬性名稱Changed ,在這裡將會定義一個 OnEntryChangedInputTextChanged 方法,當這個 Xamarin.Forms 專案執行的時候,使用者在該 Entry 輸入任何文字,將會觸發 OnEntryChangedInputTextChanged 方法執行,在此方法內,將會撰寫符合需求的計算字數商業邏輯程式碼,計算完成之後,就可以儲存到 EntryChangedInputTextLength 屬性內,如此,就可以在螢幕上看到該輸入字數數量了。
        public string EntryChangedInputText { get; set; }
        public int EntryChangedInputTextLength { get; set; } 
        public void OnEntryChangedInputTextChanged()
        {
            if(string.IsNullOrEmpty(EntryChangedInputText))
            {
                EntryChangedInputTextLength = 0;
            }
            else
            {
                EntryChangedInputTextLength = EntryChangedInputText.Length;
            }
        }

使用 Prism 提供的事件 To 命令的行為擴充功能

Prism 這個 MVVM 開發框架,提供了一個 Xamarin.Forms Behavior 行為的物件,其中一個加值行為就是可以設定當某個事件被觸發的時候,需要執行某個命令;如此,使用這個方法將會把相關商業處理邏輯寫道 ViewModel 上,而不在需要撰寫 Code Behind 程式碼了。
首先,請先在該 ContentPage 節點上宣告一個新的命名空間 xmlns:behavior="clr-namespace:Prism.Behaviors;assembly=Prism.Forms" ,這裡將會宣告一個前置詞 Prefix , behavior ,指向 Prims 行為擴充功能組件上,現在,可以在這個頁面上,加入一個 Label 與 Entry,其中 Entry 需要使用 Event.Behaviors 的屬性項目 Property Element 方式,指定新加入的行為物件;這裡將會使用 behavior:EventToCommandBehavior 這個型別,來使用 EventName 這個屬性來指定觸發事件的名稱與 Command 來指定要綁定到 ViewModel 中的一個 DelegateCommand 屬性物件。
xaml
        <Label Text="{Binding EntryBehaviorInputTextLength}"/>
        <Entry
            Text="{Binding EntryBehaviorInputText}">
            <Entry.Behaviors>
                <behavior:EventToCommandBehavior
                    EventName="TextChanged" Command="{Binding EntryChangedCommand}"/>
            </Entry.Behaviors>
        </Entry>
在 ViewModel 內,會在建構函式內建立一個 DelegateCommand 型別的物件到 EntryChangedCommand 屬性上,在建立這個 EntryChangedCommand 屬性的時候,將會傳入一個委派方法,這個委派方法將會用來轉寫計算使用者輸入字數的相關程式碼。
        public string EntryBehaviorInputText { get; set; }
        public int EntryBehaviorInputTextLength { get; set; }
        public DelegateCommand EntryChangedCommand { get; set; }
        public MainPageViewModel(INavigationService navigationService)
        {
            this.navigationService = navigationService;
            EntryChangedCommand = new DelegateCommand(() =>
            {
                EntryBehaviorInputTextLength = EntryBehaviorInputText is null ? 0 : EntryBehaviorInputText.Length;
            });
        }

使用附加屬性來計算使用輸入的文字數量

對於想要針對某個控制項加入可以綁定的屬性,讓 ViewModel 可以根據 View 的屬性異動,進行相關的商業邏輯處理,第一個就是使用原來檢視上的屬性來做處理,若有特殊需求,需要額外的屬性,可以建立一個新類別,繼承原來使用的檢視類別,就可以在這個新控制項中加入一個新的可綁定屬性;第二個方式,就是使用附加屬性,針對想要使用該附加屬性來附加這些附加屬性。
最後一個,就是要建立兩個附加屬性,第一個是 EnableCharCount ,這是用來設定是否要啟用自動計算字數的附加屬性,第二個是 EditorInputTextLength 用來儲存使用者輸入字數的數量值,底下為設計出來的附加屬性 XAML 用法。
由於等下要設計的附加屬性將會限制這兩個附加屬性僅能夠套用到 Entry 或者 Editor 上,當這兩個附加屬性附加到其他檢視的時候,將不會有任何效果的。
這裡有一點要特別注意,對於 CharCountAttachedProperty.CharNumber 這個附加屬性需要在資料綁定延伸標記語法內,要使用 Mode=TwoWay ,這樣才會形成雙向綁定運作方式。
xaml
        <Label Text="{Binding EditorInputTextLength}"/>
        <Label Text="{Binding EntryInputTextLength}"/>
        <Editor
            Text="{Binding EditorInputText}"
            AutoSize="TextChanges"
            local:CharCountAttachedProperty.EnableCharCount="True"
            local:CharCountAttachedProperty.CharNumber="{Binding EditorInputTextLength, Mode=TwoWay}"
            />
        <Entry
            Text="{Binding EntryInputText}"
            local:CharCountAttachedProperty.EnableCharCount="True"
            local:CharCountAttachedProperty.CharNumber="{Binding EntryInputTextLength, Mode=TwoWay}"/>
現在,可以建立一個類別,在此類別上建立兩個附加屬性,在此,建立的類別名稱為 CharCountAttachedProperty,接著,在此類別內,使用程式碼片段 xfAttachedProperty 自動產生附加屬性的相關程式碼。
第一個附加屬性為 EnableCharCountProperty ,在這裡附加屬性將會用來控制是否要進行輸入文字內容的計算工作,在呼叫 BindableProperty.CreateAttached 方法的時候,將會使用 declaringType 來指定這個附加屬性可以附加到那些 XAML 項目上,這裡指定的是 typeof(View),代表任何使用者控制項都可以使用這個附加屬性。
在 EnableCharCountProperty 內最為重要的工作那就是要定義 propertyChanged 這個參數值,傳入一個委派方法 OnEnableCharCountChanged,在此委派方法終將會使用 if (bindable is Entry || bindable is Editor) 來檢查現在該附加屬性是否用在 Entry 或者 Editor 這兩個檢視上,若不是的話,則不會做任何處理。所附加的檢視為 Entry 或者 Editor ,將會檢查是否要啟用這個自動計算字數的屬性值,若為 True,則會訂閱 Entry.TextChanged 事件或者 Editor.TextChanged 事件,否則若為 False,則會取消 TextChanged 的事件訂閱。
記得,當訂閱視覺項目的事件的時候,一定要規劃解除訂閱事件的程式碼,否則,會有可能產生 記憶體遺失 Memory Leak 的問題。
在 OnEntryTextChanged 方法內,將會進行該自動計算使用者輸入文字的字數計算,將結果儲存到 CharNumberProperty 這個附加屬性上。
最後,還是使用相同的程式碼片段, xfAttachedProperty 自動建立 CharNumberProperty 附加屬性程式碼,而在這個 CharNumberProperty 附加屬性上,並不需要加入額外的其他城市碼。
public class CharCountAttachedProperty
{
    #region EnableCharCount 附加屬性 Attached Property
    public static readonly BindableProperty EnableCharCountProperty =
           BindableProperty.CreateAttached(
               propertyName: "EnableCharCount",   // 屬性名稱 
               returnType: typeof(bool), // 回傳類型 
               declaringType: typeof(View), // 宣告類型 
               defaultValue: false, // 預設值 
               propertyChanged: OnEnableCharCountChanged  // 屬性值異動時,要執行的事件委派方法
           );

    public static void SetEnableCharCount(BindableObject bindable, bool entryType)
    {
        bindable.SetValue(EnableCharCountProperty, entryType);
    }
    public static bool GetEnableCharCount(BindableObject bindable)
    {
        return (bool)bindable.GetValue(EnableCharCountProperty);
    }

    private static void OnEnableCharCountChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is Entry || bindable is Editor)
        {
            bool isEnable = (bool)newValue;
            if (isEnable)
            {
                if (bindable is Entry)
                {
                    (bindable as Entry).TextChanged += OnEntryTextChanged;
                }
                else if (bindable is Editor)
                {
                    (bindable as Editor).TextChanged += OnEntryTextChanged;
                }
            }
            else
            {
                if (bindable is Entry)
                {
                    (bindable as Entry).TextChanged -= OnEntryTextChanged;
                }
                else if (bindable is Editor)
                {
                    (bindable as Editor).TextChanged -= OnEntryTextChanged;
                }
            }
        }
        else
        {

        }
    }
    #endregion
    private static void OnEntryTextChanged(object sender, TextChangedEventArgs e)
    {
        int foo = CharCountAttachedProperty.GetCharNumber(sender as BindableObject);
        int length = e.NewTextValue.Length;
        CharCountAttachedProperty.SetCharNumber(sender as BindableObject, length);
        (sender as BindableObject).SetValue(CharNumberProperty, length);
        int foo2 = CharCountAttachedProperty.GetCharNumber(sender as BindableObject);
    }

    #region CharNumber 附加屬性 Attached Property
    public static readonly BindableProperty CharNumberProperty =
           BindableProperty.CreateAttached(
               propertyName: "CharNumber",   // 屬性名稱 
               returnType: typeof(int), // 回傳類型 
               declaringType: typeof(View), // 宣告類型 
               defaultValue: 0, // 預設值 
               propertyChanged: OnCharNumberChanged  // 屬性值異動時,要執行的事件委派方法
           );

    public static void SetCharNumber(BindableObject bindable, int entryType)
    {
        bindable.SetValue(CharNumberProperty, entryType);
    }
    public static int GetCharNumber(BindableObject bindable)
    {
        return (int)bindable.GetValue(CharNumberProperty);
    }

    private static void OnCharNumberChanged(BindableObject bindable, object oldValue, object newValue)
    {
    }

    #endregion

}



沒有留言:

張貼留言