【xamarin + MvvmCross 從零開始】四、MvvmCross 詳解 (2)


前言

上篇我們說明了幾個在MvvmCross中常用的對象,這次我們講一下MvvmCross的數據綁定。

什么是數據綁定

數據綁定是MvvmCross的至關重要的特性,通過代碼或XAML方式建立View與ViewModel之間的關聯,以達到數據呈現、交互的目的。

那什么是數據綁定呢?

  • 建立View和ViewModel中屬性之間的關聯
  • 可以指定BindingMode來控制數據的流向
  • 可以指定ValueConvert實現數據的雙向轉換,在使用ValueConvert時可以通過參數實現定制轉換
  • 可以指定FallbackValue,當數據綁定失敗時作為默認結果使用

ViewModel定義

我們來看一個典型的ViewModel是如何定義的

using System.Windows.Input;
using MvvmCross.Core.ViewModels;

namespace XamarinSample.ViewModels
{
    public class UserViewModel: MvxViewModel
    {
        private string _name;
        private int _age;
        private string _password;

        /// <summary>
        /// 用戶名
        /// </summary>
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                // 觸發屬性 Name 變更
                RaisePropertyChanged(() => Name);
            }
        }

        /// <summary>
        /// 年齡
        /// </summary>
        public int Age
        {
            get { return _age; }
            set
            {
                _age = value;
                RaisePropertyChanged(() => Age);
            }
        }

        /// <summary>
        /// 密碼
        /// </summary>
        public string Password
        {
            get { return _password; }
            set
            {
                _password = value;
                RaisePropertyChanged(() => Password);
            }
        }

        private ICommand _saveCommand;

        /// <summary>
        /// 保存用戶信息Command,供綁定保存操作使用
        /// </summary>
        public ICommand SaveCommand => _saveCommand ?? (_saveCommand = new MvxCommand(Save));

        private void Save()
        {
            // 保存User數據
        }
    }
}

在MvvmCross中所有的ViewModel需要實現IMvxViewModel接口,MvvmCross也實現了基本的ViewModel,所以只需要從MvxViewModel繼承就可以。

所有可以綁定數據必須是屬性,並且如果要實現數據變更通知,需要在數據變化時調用RaisePropertyChanged 方法。當給Name賦值時,將觸發INotityPropertyChange接口中的PropertyChanged事件,View層通過對PropertyChanged事件的接收而實現View層的響應。

Mvvm 不支持直接對方法進行綁定,若要實現對方法的綁定,需要將方法轉換為ICommand類型的屬性,再進行綁定。這里我們將Save方法封裝為SaveCommand后就可以將SaveCommand方法綁定到View 中。

綁定模式

One-Way

  • 單向綁定,由ViewModel向View單向的數據綁定。
  • 當ViewModel的數據發生變時,View顯示的數據也會同時發生變化。
  • 一般應用於數據展示,比如天氣預報數據顯示,ViewModel由服務器更新數據后,呈現界面自動更新。
  • 單向綁定在Xaml綁定為默認的綁定模式。

One-Way-To-Source

  • 由界面流向數據源的單向綁定。
  • 比如用戶填表,只需要采集用戶數據,不需要向用戶進行呈現。
  • 這種綁定的模式不經常使用。

Two-Way

  • 雙向數據綁定,當ViewModel發生變化時,View也相應發生變化;當用戶在界面更新數據或都輸入時,ViewModel數據也發生變化。
  • 一般應用於數據編輯等,如用戶信息修改,用戶在View修改信息時,ViewModel也相應的發生變化。

 

One-Time

  • 一次性綁定。數據的方向為由ViewModel向View綁定。
  • 一般只在界面第一次初始化綁定時獲取數據,以后不管ViewModel如何變化,View的呈現都不發生變化。
  • 這種模式不經常使用。一般在顯示固定內容時使用,如固定的標簽、不會發生變化的背景圖片等。

 

數據轉換(ValueConverter)

數據轉換是一個實現了IValueConverter接口的一個對象。我們來看一下接口的定義:

image

接口主要包括兩個方法:

Convert:實現將ViewModel中的數據轉換為支持或方便View呈現的數據。如數據的格式化、布爾型數據轉換為是/否等可方便識別的內容等。

ConvertBack:與Convert相反,將View中的數據轉換為方便持久化的數據。

通過接口中可以看到,轉換支持一個object類型的參數對象。表示在使用ValueConvert時,可以傳入一個自定義的參數,極大的提高了轉換的靈活性。如在將float轉換為字符串時,可以傳入一個整形的參數,用來標識轉換結果需要保存的小數位數。

接口可以根據需要進行實現,如一個One-Way綁定使用的轉換,就沒有必要實現ConvertBack部分。

數據轉換在MvvmCross中一個通用的特性。這就表示只用在PCL程序集中實現一次數據綁定接口,就可以在Android、iOS、WP等多個平台上使用。

備選值(Fallback Values)

有時ViewModel中的值不是一個可用的值,那這時就需要使用備選值。如顯示用戶所屬的部門數據時,正常情況下需要將關聯的部門顯示為部門的名稱,但當用戶並沒有設置關聯的部門信息時,顯示為空顯示不太友好,這時可以使用備選值,友好的顯示為[未設置部門信息]。

數據綁定(Data Binding)

數據綁定是MvvmCross的核心內容。隨着MvvmCross的不斷更新,綁定的方式也有很多種。

JSON 綁定

這種綁定方式做為較早的綁定方式,目前已經不推薦使用,這里我們就不做說明了。

Swiss 綁定

Swiss綁定是Json綁定的替代方式,就綁定的語法而言,較JSON綁定方式有極大的簡化。

Swiss綁定的語法:

$Target$ $SourcePath$
Target必須是View某個對象的直接屬性,比如:
  • Text,控件的Text屬性
  • IsChecked,可選控件的IsChecked屬性
  • Value,控件的值
  • ……

SourcePath為ViewModel中某個屬性或都子對象的某個屬性,也就是說可以根據需要指定多級關聯屬性,比如:

  • UserId
  • RememberMe
  • Password
  • Customer.FirstName
  • Customer.Address.City
  • Customer.Orders[0].Date
  • Customer.Orders[0].Total
  • Customer.Cards["Primary"].Expiry
  • Customer.Cards["Primary"].Number

SourcePath除了以上語法以外,還有一些特殊的用法

  • 如果直接使用 . (點)的話,則是說明綁定的對象為當前的整個ViewModel
  • 如果需要對綁定的ViewModel屬性進行轉換,可以添加一個Converter,語法如下:

         , Converter=$ConverterName$

  • 在使用Converter時,可根據需要使用ConverterParameter:

         , ConverterParameter=$ParameterValue$

        ConverterName值可以是以下的一種:

      • 使用單引號或雙引號限定,表示字符串
      • null代表C#中的null值
      • 整型值,MvvmCross將解釋為 長整型long
      • 浮點數,MvvmCross將解釋為 雙精度 數值 double
  • 可以根據需要添加FallbackValue:

          , FallbackValue=$FallbackValue$

         FallbackValue的賦值語法基本和ParameterValue一樣,再加上枚舉值的ToString()方法。

  • 如果需要改變BindMode,可以添加BindMode:

          ,Mode=$BindMode$

      • OneWay
      • OneWayToSource
      • TwoWay
      • OneTime
      • Default
  • 如果需要在同一個對象上綁定多個值,需要要每個不同的綁定值之間用分號(;)進行分隔。
  • 如果綁定的值是一個ICommand類型的屬性,可根據需要添加CommandParameter:

          , CommandParameter=$CPValue$

          在這里CPValue的取值與ParameterValue一樣

 

下面我們看幾個Swiss綁定的例子:

Text Customer.FirstName
將View中某個對象的Text屬性綁定到ViewModel中的Customer.FirstName屬性
 
Text Title, Converter=Length
將View中某個對象的Text屬性綁定以ViewModel中的Title屬性,並使用轉換器 Length 進行數據轉換
 
Text Order.Amount, Converter=Trim, ConverterParameter='£'
將View中某個對象的Text屬性綁定到ViewModel中的Order.Amount屬性,並使用轉換器 Trim,轉換器參數為字符串'£'
 
Value Count, Mode=TwoWay
將View中某個對象的Value屬性綁定到ViewModel中的Count屬性,設置綁定的模式為雙向綁定
 
Click DayCommand, CommandParameter='Thursday'
將View中的某個對象的Click事件綁定到ViewModel中的dayCommand 屬性,並使用命令參數 'Thursday'
 

 

Fluent 綁定

Fluent綁定做為一種新的綁定方式,可以通過C#代碼進行數據的綁定。由於在iOS或MacOS等平台上不方便使用Xaml方式的綁定,數據的綁定就使用Fluent綁定。

Fluent綁定方式在編碼時可以充分利用VS的智能提示功能,並且能寫出干凈整潔的代碼,方便閱讀。

 

Fluent綁定通常情況下是通過 CreateBindingSet<TView, TViewModel> 創建視圖與數據源的綁定關系。

綁定的語法包括:

  • Binding($BindObject$)

       指定綁定對象,$BindObject$是指View中的某個對象。

  • For(v => v.$ViewProperty$)
    $ViewProperty$指定綁定對象的屬性,這里只能是關聯對象的直接屬性。部分對象可以不指定For語句,則$ViewProperty$將使用默認屬性。如UIButton的默認屬性是Text屬性。
  • To(vm => vm.$ViewModelPath$)

    $ViewModelPath$是ViewModel中的屬性路徑,可以是多級屬性,如User.FirstName

  • 綁定模式
      OneWay()
      TwoWay()
      OneWayToSource()
      OneTime()
 
  • WithConversion($name$, $parameter$)

       指定數據轉換器。$name$是指定轉換器的名稱,$parameter$是指當前轉換器的參數

  • 使用Fluent綁定,在綁定結束后必須調用Apply()方法,否則綁定不會起作用。

 

我們看幾個例子:

 var set = this.CreateBindingSet<MyView, MyViewModel>();
 set.Bind(nameLabel)
    .For(v => v.Text)
    .To(vm => vm.Customer.FirstName);
 set.Bind(creditLabel)
    .For(v => v.Text)
    .To(vm => vm.Customer.Total)
    .WithConversion("CurrencyFormat", "$");
 set.Bind(cardLabel)
    .For(v => v.Text)
    .To(vm => vm.Customer.Cards["Primary"].Number)
    .WithConversion("LastFour")
    .OneWay()
    .FallbackValue("N/A");
 set.Bind(warningView)
    .For(v => v.Hidden)
    .To(vm => vm.Customer.Alert)
    .WithConversion("Not")
    .FallbackValue(true);
 set.Apply();

 

Tibet 綁定

Tibet綁定與Swiss綁定完全兼容,並且在Swiss綁定上進行了擴展,主要擴展以下幾個部分:

  • multi-binding
  • value combiners
  • literal-binding
  • binding macros
  • functional syntax for ValueConverters and ValueCombiners
  • nested value conversion

 

多值綁定 (multi-binding)

在Swiss綁定中,每次只能綁定一個ViewModel屬性,當需要綁定多個屬性時,就只能通過在ViewModel中添加屬性來實現,這樣很不友好。

private string _firstName;
public string FirstName
{
   get { return _firstName; }
   set
   {
      _firstName = value;
      RaisePropertyChanged(() => FirstName);
      RaisePropertyChanged(() => FullName);
   }
}

private string _lastName;
public string LastName
{
   get { return _lastName; }
   set
   {
      _lastName = value;
      RaisePropertyChanged(() => LastName);
      RaisePropertyChanged(() => FullName);
   }
}

public string FullName
{
   get { return _firstName + "" + _lastName; }
}

在示例代碼中,如果我們想要在View上顯示FullName,就需要在ViewModel中添加一個屬性FullName,這樣才能綁定到View中,在Tibet綁定中,我們可以這樣寫:

Text FirstName + ' ' + LastName
這樣,MvvmCross實際將這個綁定解析為三個綁定值 FirstName、 ' ' 、LastName, 當FirstName 或 LastName 發生變化時,相應的綁定對象的值都會發生變化。

值連接 (value combiners)

類似的值連接Tibet還實現了一些:
  • If(test, if_true, if_false) - 接受三個輸入值,類似於三元運算
    • 一個bool型的表達式
    • 當表達式為true時所返回的值
    • 當表達式為false時所返回的值
  • Format(format, args…) - 字符串格式化,同string.Format用法一致 
    • 字符串格式化模板
    • 0或多個字符串格式化值
  • Add(one,two) -  接受兩個參數。當兩個參數類型為字符串時,返回兩個字符串的連接值;當兩個值為數值時,返回兩個數值相加的結果。
  • GreaterThan(one, two) - 接受兩個參數進行大於運算,參數類型可以字符串、浮點型或是整型數據。

由於綁定字符串不需要編譯,而在支行時通過MvvmCross進行解釋執行,所以某些綁定不是支持所有平台。而且某些復雜的表達式在解釋時可能與所期望的結果不同。

語義綁定 (literal-binding)

綁定對很多簡單的表達式是可以執行的

  • Value 100 * Ratio

       綁定常量值或是運算表達式

  • Value ‘True’

       bool 型的屬性可以通 True 或 False 綁定常量值

  • Value  Format('Hello {1} - today is {0:ddd MMM yyyy}', TheDate, Name)

    綁定的Format表達式

Rio 綁定及其它

關於數據綁定,MvvmCross還會逐步增強,包括語法的簡化和功能增強,后期會增加宏綁定等內容。

 

小結

本篇內容主要講解了MvvmCross中數據綁定的方式。在實際的開發過程中主要使用Swiss和Fluent綁定。習慣通過XAML綁定的同學可以使用Swiss綁定,在不能使用Swiss或是習慣通過c#代碼進行數據綁定的同學可以使用Fluent綁定。

下篇我們講下ViewModel對象的生命周期以及如何通過ViewModel進行導航。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM