Xamarin.Forms數據綁定


 

生命周期

在 Android 上,若主活動的 [Activity()] 屬性缺少 ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,旋轉時及首次啟動應用程序時,將調用 OnStart 方法。

 

數據綁定

數據綁定在用戶界面和應用程序之間建立連接。

官網:https://docs.microsoft.com/zh-cn/xamarin/xamarin-forms/app-fundamentals/data-binding/

原理參考:Xamarin.From中的Data binding(數據綁定)(一)

基本綁定

數據綁定連接兩個對象,即源和目標。 源對象提供數據, 目標對象使用(並經常顯示)來自源對象的數據。 例如, Editor (目標對象) 通常會將Text屬性綁定到對象中string的屬性。 下圖說明了這種綁定關系:

數據綁定的主要優點是讓你無需再擔心視圖和數據源之間的數據同步。 底層的綁定框架源會將源對象中的更改自動推送到目標對象,且目標對象中的更改可選擇性地推送回源對象。

建立數據綁定的過程分為兩個步驟:

  • 目標對象的 BindingContext 屬性必須設置為源。
  • 必須在目標和源之間建立綁定。 

實現綁定有兩種方式:僅在xaml文件中或者僅在cs中,每種又分使用BindingContext與否。

1、在 XAML 中設置【常用方式】

不使用BingingContext通過使用 Binding 標記擴展實現。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeXamlBindingPage"
             Title="Alternative XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="{Binding Source={x:Reference slider},
                               Path=Value}" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>
View Code

使用BingingContext:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicXamlBindingPage"
             Title="Basic XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>
View Code

XAML標記擴展(例如x:Reference和Binding)可以定義內容屬性特性,對於XAML標記擴展,這意味着不需要顯示屬性名稱。

Name屬性是x:Reference的content屬性,Path屬性是Binding的content屬性,這意味着可以從表達式中消除它們:

<Label Text="TEXT"
       FontSize="80"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       BindingContext="{x:Reference slider}"
       Rotation="{Binding Value}" />
View Code

同時,源屬性是由 BindingExtension 的 Path 屬性指定的,它對應於 Binding 類的 Path 屬性(Binding 標記擴展的內容屬性是 Path,但是標記擴展的 Path= 部分只有在它是表達式中的第一個屬性時才能被刪除。)

總結最精簡做法:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeXamlBindingPage"
             Title="Alternative XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="{Binding Value, Source={x:Reference slider}}" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>
View Code

小結:

將Binding的Source屬性或者BindingContext屬性設置為x:Reference標記擴展,以引用頁面上的另一個視圖(源對象)。 這兩個屬性的類型為Object,可以將它們設置為任何包含適合於綁定源的屬性的對象。

如果兩者都已設置,則 Binding 的 Source 屬性優先於 BindingContext

2、只在cs中設定

需要設置以下

public BasicCodeBindingPage()
    {
        InitializeComponent();

        label.BindingContext = slider; label.SetBinding(Label.RotationProperty, "Value"); }

 

綁定上下文繼承

父布局中定義了源對象,子控件中都可以使用。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BindingContextInheritancePage"
             Title="BindingContext Inheritance">
    <StackLayout Padding="10">

        <StackLayout VerticalOptions="FillAndExpand"
                     BindingContext="{x:Reference slider}">

            <Label Text="TEXT"
                   FontSize="80"
                   HorizontalOptions="Center"
                   VerticalOptions="EndAndExpand"
                   Rotation="{Binding Value}" />

            <BoxView Color="#800000FF"
                     WidthRequest="180"
                     HeightRequest="40"
                     HorizontalOptions="Center"
                     VerticalOptions="StartAndExpand"
                     Rotation="{Binding Value}" />
        </StackLayout>

        <Slider x:Name="slider"
                Maximum="360" />

    </StackLayout>
</ContentPage>
View Code

綁定模式

默認綁定模式

使用 BindingMode 枚舉的成員指定綁定模式:

  • Default
  • TwoWay – 數據在源和目標之間雙向傳輸
  • OneWay – 數據從源到目標單向傳輸
  • OneWayToSource – 數據從目標到源單向傳輸
  • OneTime – 只有在 BindingContext 更改時,數據才從源到目標單向傳輸(Xamarin.Forms 3.0 新增功能)

每個可綁定屬性都有一個默認綁定模式,該模式在創建可綁定屬性時進行設置,並且可從 BindableProperty 對象的 DefaultBindingMode 屬性中獲得。 此默認綁定模式指示該屬性是數據綁定目標時有效的模式。

大多數屬性(如 RotationScale 和 Opacity)的默認綁定模式都是 OneWay。 如果這些屬性是數據綁定目標時,則從源設置目標屬性。

但是,Slider 的 Value 屬性的默認綁定模式為 TwoWay。 這意味着,如果 Value 屬性是數據綁定目標時,則通常從源設置目標,但源也可從目標設置。 這就是允許從初始 Opacity 值設置 Slider 的原因。

【<!--Slider.Value是twoWay模式,它的Value 是從Label的opacity處來,默認是1。 同時Label的opacity也是1,改變value時,Label的opacity也會改變-->】

這種雙向綁定似乎會創建一個無限循環,但這種情況不會發生。 除非屬性實際發生變化,否則可綁定屬性不會發出屬性更改的信號,這樣可以避免無限循環。

1、雙向綁定

大多數可綁定屬性的默認綁定模式都是 OneWay,但以下屬性的默認綁定模式為 TwoWay

  • DatePicker 的 Date 屬性
  • EditorEntrySearchBar 和 EntryCell 的 Text 屬性
  • ListView 的 IsRefreshing 屬性
  • MultiPage 的 SelectedItem 屬性
  • Picker 的 SelectedIndex 和 SelectedItem 屬性
  • Slider 和 Stepper 的 Value 屬性
  • Switch 的 IsToggled 屬性
  • SwitchCell 的 On 屬性
  • TimePicker 的 Time 屬性

這些特定屬性被定義為 TwoWay,理由非常充分:

當數據綁定與模型-視圖-視圖模型 (MVVM) 應用程序體系結構一起使用時,ViewModel 類是數據綁定源,而由 Slider 等視圖組成的 View 則是數據綁定目標。 MVVM 綁定更類似於“ReverseBindingPage”示例,而不是之前示例中的綁定 。 你很可能會想要使用 ViewModel 中相應屬性的值來初始化頁面上的每個視圖,但是視圖中的更改也將影響ViewModel屬性。

默認綁定模式為 TwoWay 的屬性 是最有可能在 MVVM 方案中使用的屬性。

2、OneWayToSource綁定

只讀可綁定屬性的默認綁定模式為 OneWayToSource【目標到源】。 

只有一個讀/寫可綁定屬性的默認綁定模式為 OneWayToSource

  • ListView 的 SelectedItem 屬性

其基本原理是,對 SelectedItem 屬性的綁定(改變)應該導致 重新設置綁定源。

3、一次性綁定

許多屬性(包括 Entry 的 IsTextPredictionEnabled 屬性)都具有 OneTime 的默認綁定模式。

只有在綁定上下文更改時,才會更新綁定模式為 OneTime 的目標屬性。 對於這些目標屬性的綁定,該模式簡化了綁定基礎結構,因為不必監視源屬性中的更改。

ViewModels and 屬性更改通知

ViewModel的使用,參考:C#使用Xamarin開發可移植移動應用(4.進階篇MVVM雙向綁定和命令綁定)附源碼

Data binding的核心思想:Data binding總是有一個源(source)和一個目標(target);源是某個對象的一個屬性,通常會在運行期間發生變化;當這個屬性變化時,Data binding將自動將這一變化更新到目標上,即另一個對象的一個屬性上。

有一點特別重要:Data binding中的目標必須是一個BindableProperty對象

在Xamarin.Form中,大多數的View、Layout和Page都是BindableObject對象。

對源則沒有這樣的要求,因此源可以是一個普通的C#屬性ViewModel 類是數據綁定源,在ViewModel中定義C#屬性。。

但在一般的數據綁定中,源的變化應當引起目標的變化,這種變化需要某種通知機制進行傳遞。這里有一個現成的機制—— INotifyPropertyChanged接口。

INotifyPropertyChanged接口的內容非常簡單,僅僅定義了個PropertyChanged事件,這個事件在屬性變化時會被觸發;

public interface INotifyPropertyChanged {
    event PropertyChangedEventHandler PropertyChanged; 
}

因此,Data binding中的源必須實現INotifyPropertyChanged接口

而BindableObject正好就實現了INotifyPropertyChanged接口,因此,只需將源定義成BindableObject對象即可,這樣它既可以成為源,也可以成為目標。

而只實現INotifyPropertyChanged就只能作為源,不過,這么做也有好處,就是在實現上更簡單。

覆蓋默認的綁定模式

如果目標屬性的默認綁定模式不適合特定的數據綁定,則可以通過將 Binding 的 Mode 屬性(或 Binding 標記擴展的 Mode 屬性)設置為 BindingMode 枚舉的其中一個成員來替代它。

但是,將 Mode 屬性設置為 TwoWay 並不總是像你預期的那樣有效。

替換掉默認的綁定模式的例子:SampleSettingsViewModel
一種非常有用的應用程序涉及 ListView 的 SelectedItem 屬性。,其默認綁定模式為 OneWayToSource(目標到源)
我們將其改為TwoWay。

字符串格式設置

在代碼中顯示字符串時,最強大的工具是靜態 String.Format 方法。 格式設置字符串包括特定於各種類型的對象的格式代碼,也可以包括其他文本以及正在進行格式設置的值。 有關字符串格式的詳細信息,請參閱設置 .NET 中類型的格式一文。

<Slider x:Name="slider" />
<Label Text="{Binding Source={x:Reference slider},
                      Path=Value,
                      StringFormat='The slider value is {0:F2}'}" />

Path的值會去替換占位符{0}。

綁定路徑

前面將Binding 類的 Path 屬性(或 Binding 標記擴展的 Path 屬性)已設置為單個屬性。 實際上,可以將 Path 設置為“子屬性”(屬性的屬性),也可以設置為集合的成員 。

示例中有一些很有用的說明。

如果綁定路徑中的屬性沒有實現 INotifyPropertyChanged,那么對該屬性的任何更改都將被忽略。 一些更改可能會使綁定路徑完全無效,所以應只在屬性和子屬性字符串永遠不會失效的情況下才使用這種技術。

綁定值轉換器

  • 說明

當源和目標屬性都是同一類型,或當一個類型可以隱式轉換為另一種類型時,這類傳遞都是非常簡單的。 如果不是這種情況,則必須執行類型轉換。

字符串格式設置一文已介紹如何使用數據綁定的 StringFormat 屬性將任意類型轉換為字符串 。 對於其他類型的轉換,需要在類中編寫一些專門的代碼以實現 IValueConverter 接口。 (通用 Windows 平台在 Windows.UI.Xaml.Data 命名空間中包含一個名為 IValueConverter 的類似的類,但此 IValueConverter 在 Xamarin.Forms 命名空間中。)實現 IValueConverter 的類被稱為“值轉換器”,但它們通常也被稱為“綁定轉換器”或“綁定值轉換器” 。

  • 實例

想要定義源屬性為類型 int 但目標屬性為 bool 的數據綁定。 想要此數據綁定在整數源等於 0 時生成 false 值,在其他情況則生成 true

public class IntToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value != 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1 : 0;
    }
}
View Code

將此類的實例設為 Binding 類的 Converter 屬性,此類即成為數據綁定的一部分。

當數據在 OneWay 或 TwoWay 綁定中由源移動到目標時,將調用 Convert 方法。 value 是來自數據綁定源的對象或值,該方法必須返回數據綁定目標類型的值。 此處所示的方法將 value 參數強制轉換為 int,然后將其與 0 比較並得到 bool 返回值。

當數據在 TwoWay 或 OneWayToSource 綁定中由目標移動到源時,將調用 ConvertBack 方法。 ConvertBack執行相反的轉換:它假定 value 參數是來自目標的 bool,然后將其轉換為源的 int 返回值。

xaml中使用:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.EnableButtonsPage"
             Title="Enable Buttons">
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:IntToBoolConverter x:Key="intToBool" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />

        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>
View Code

 

綁定回退

有時數據綁定會失敗,因為無法解析綁定源,或者因為綁定成功但返回 null 值。 雖然可以使用值轉換器或其他附加代碼處理這些情況,但是通過定義在綁定過程失敗時要使用的回退值,可以使數據綁定更加可靠。 這可以通過定義綁定表達式中的 FallbackValue 和 TargetNullValue 屬性來實現。 因為這些屬性位於 BindingBase 類中,它們可以與綁定、編譯綁定和 Binding 標記擴展一起使用。

命令接口

在“模型-視圖-視圖模型”(即 MVVM)體系結構中,數據綁定是在 ViewModel(通常是派生自 INotifyPropertyChanged 的類)中的屬性和視圖(通常為 XAML 文件)中的屬性之間定義的。 

有時,應用程序的需求超越了屬性綁定層面,它要求用戶啟動影響 ViewModel 中某些內容的命令。 這些命令通常通過點擊按鈕或手指敲擊觸發信號,往往是以下兩個事件的處理程序中的代碼隱藏文件中處理它們:Button 的 Clicked 事件或 TapGestureRecognizer 的 Tapped 事件。

命令接口提供了另一種實現命令的方法,這種方法更適合 MVVM 體系結構。 ViewModel 本身可以包含命令,這些命令是針對視圖中的特定活動(例如單擊 Button)而執行的方法。 在這些命令和 Button 之間定義了數據綁定。

為允許使用 Button 和 ViewModel 之間的綁定數據,Button 定義兩個屬性:

要使用命令接口,您需要定義一個針對Button的Command屬性的數據綁定,其中源是ViewModel中ICommand類型的屬性。 ViewModel包含與單擊按鈕時執行的與ICommand屬性關聯的代碼。 您可以將CommandParameter設置為任意數據,以區分多個按鈕(如果它們都綁定到ViewModel中的同一ICommand屬性)。

下列類也定義了 Command 和 CommandParameter 屬性:

SearchBar 定義一個 ICommand 類型的 SearchCommand 屬性和一個 SearchCommandParameter 屬性。 ListView 的 RefreshCommand 屬性也是 ICommand 類型。

可以在 ViewModel 中以不依賴視圖中的特定用戶界面對象的方式處理上述所有命令。

使用:

若要使用命令接口,ViewModel 需包含 ICommand 類型的屬性(用於處理View(eg 按鈕)的所有邏輯)

ICommand 接口【System.Windows.Input.ICommand】由兩個方法和一個事件組成:Execute、CanExecute、CanExecuteChanged。

而一般用位於Xamarin.Forms的的Command類來實現ICommand接口類型的屬性。

通過 Command 類的構造函數,你可以傳遞與 Execute 和 CanExecute 方法對應的 Action 和 Func<bool> 類型的參數。

參考實例:PersonEntryPage 【主要是按鈕的Command屬性的綁定】

它的工作原理如下:用戶首先按“新建”按鈕 。 這將啟用輸入窗體,但禁用“新建”按鈕 。 然后用戶輸入姓名、年齡和技能。 在編輯過程中,用戶隨時都可以按下“取消”按鈕重新開始 。 只有在輸入了姓名和有效年齡后,才啟用“提交”按鈕 。 按“提交”按鈕可將人員轉移到 ListView 顯示的集合中 。 按“取消”或“提交”按鈕后,會清除輸入窗體中的內容並再次啟用“新建”按鈕 。

注:如果使用命令接口,請勿使用 Button 的 IsEnabled 屬性。

1、Excute

用戶按下 Button 時,Button 調用綁定到 Button的Command 屬性的 ICommand 對象中的 Execute 方法,即上面的Action execute 無返回值的委托。

2、CanExcute

首次在 Button 的 Command 屬性上定義綁定時【即初始加載時】,以及數據綁定以某種方式更改時Button 調用 ICommand 對象中的 CanExecute 方法。 

如果 CanExecute 返回 false,則 Button 將禁用其自身。 這表示特定命令當前不可用或無效。

3、ChangeCanExecute 

每當發生任何可能更改 CanExecute 方法返回值【eg 按鈕是否可用】的事情時,ViewModel 都應該為 ICommand 屬性調用 ChangeCanExecute。【自定義的RefreshCanExecutes()方法中的設定】

調用 ChangeCanExecute方法 將導致 Command 類觸發 CanExecuteChanged 事件。 示例中Button 已為該事件附加了一個處理程序,並通過再次調用 CanExecute 進行響應,然后根據該方法的返回值啟用自身。

 

注:示例中的提交按鈕

因為最初單擊了New按鈕,執行了RefreshCanExecutes()方法,三個按鈕的CanExecuteChanged 事件均會附加處理程序。

每當編輯中的 PersonViewModel 對象中的屬性發生更改時,都會調用 SubmitCommand 的 canExecute 函數。即實時 判斷提交按鈕是否可用。。

 

 

已編譯的綁定

已編譯綁定的解析速度快於傳統綁定解析,因而可提升 Xamarin.Forms 應用程序中的數據綁定的性能 。

數據綁定存在兩個主要問題:

  1. 綁定表達式不具有編譯時驗證。 相反,綁定在運行時解析。 因此,在應用程序未按預期運行或出現錯誤消息時,直到運行時才會檢測到任何無效綁定。
  2. 它們不具有成本效益。 使用常規對象檢查(反射)在運行時解析綁定,並且執行此操作的開銷因平台而異。

已編譯的綁定通過在編譯時而不是運行時解析綁定表達式來提升 Xamarin.Forms 應用程序中的數據綁定性能。 此外,綁定表達式的這種編譯時驗證可以提供更好的開發人員故障排除體驗,因為無效綁定會被報告為生成錯誤。

使用已編譯的綁定的過程:

  1. 啟用 XAML 編譯。 有關 XAML 編譯的詳細信息,請參閱 XAML 編譯
  2. 將 VisualElement 上的 x:DataType 屬性設置為 VisualElement 及其子元素將綁定到的對象類型。 請注意,可以在視圖層次結構中的任意位置重新定義此屬性。

在 XAML 編譯時,會將任何無效綁定表達式報告為生成錯誤。 但是,XAML 編譯器僅報告遇到的第一個無效綁定表達式的生成錯誤。 

設置程序集級別的xaml編譯(AssemblyInfo.cs文件中):[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

無論是在 XAML 還是代碼中設置 BindingContext,都將編譯在 VisualElement 或其子元素上定義的任何有效綁定表達式。 編譯綁定表達式會生成編譯代碼,該代碼將從源上的屬性獲取值,並將在標記中指定的目標的屬性上對其進行設置 。 此外,根據綁定表達式,生成的代碼可能會觀察到源屬性值的更改並刷新目標屬性,並可能會將更改從目標推送回源 。

注:目前,定義 Source 屬性的任何綁定表達式都禁用了編譯綁定。 這是因為 Source 屬性始終使用 x:Reference 標記擴展進行設置,該設置在編譯時無法解析。

 


免責聲明!

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



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