Xamarin.Forms Data Binding介紹

很多時(shí)候我們需要將某個(gè)View1 值的改變顯示在另外一個(gè)View2 上,對(duì)View1的對(duì)應(yīng)事件編碼可實(shí)現(xiàn)我們想要的效果,如果只是想處理值的改變,可以通過(guò)連接兩個(gè)View的對(duì)應(yīng)屬性即可,稱為Data Binding。Data Binding在Model-View-ViewModel (MVVM)設(shè)計(jì)模式中起著重要作用。
Data Binding中設(shè)計(jì)兩個(gè)概念SourceTarget。當(dāng)Source的值發(fā)生改變時(shí)Data Binding會(huì)自動(dòng)將這個(gè)新的值更新到Target。對(duì)Target和Source有特殊要求,Target必須繼承BindableProperty類(VisualElement通過(guò)繼承Element繼承了BindableObject,所以Xamarin.Forms中視圖的大部分屬性都是BindableProperty類型),Source必須實(shí)現(xiàn)INotifyPropertyChanged接口提供一種通知機(jī)制監(jiān)聽(tīng)Source值的改變(BindableObject實(shí)現(xiàn)了INotifyPropertyChanged接口)。

簡(jiǎn)單的Data Binding使用

本示例以Slider的Value屬性作Source,Label的Opacity屬性作Target,實(shí)現(xiàn)拖動(dòng)滑塊影響Label透明度的效果。

代碼方式設(shè)置Data Binding:

核心代碼設(shè)置Target對(duì)象的BindingContext屬性(BindableObject類型)。再調(diào)用Target 對(duì)象的SetBinding方法設(shè)置綁定屬性關(guān)系,第一個(gè)參數(shù)targetProperty為BindableProperty類型,表示目標(biāo)屬性。第二個(gè)參數(shù)string類型,表示BindingContext的哪個(gè)屬性為Source。本例調(diào)用的是5個(gè)參數(shù)的方法,后三個(gè)參數(shù)為默認(rèn)值。

代碼運(yùn)行效果:

XAML方式設(shè)置Data Binding:

<StackLayout>
     <Label Text="Opacity Binding Demo"
            FontSize="Large"
            VerticalOptions="CenterAndExpand"
            HorizontalOptions="Center"
            BindingContext="{x:Reference Name=slider}"
            Opacity="{Binding Path=Value}" />

     <Slider x:Name="slider" VerticalOptions="CenterAndExpand" />
</StackLayout>

查看Label定義,BindingContext屬性通過(guò)x:Reference指定,Opacity為目標(biāo)屬性通過(guò)Binding擴(kuò)展標(biāo)記的Path設(shè)置。Path不僅可以是Property也可以是SubProperty或 Indexer.如Content.Children[4].Value.

在整個(gè)視圖樹(shù)中子View是會(huì)繼承父布局的BindingContext屬性。如子View沒(méi)有單獨(dú)設(shè)置BindingContext屬性,會(huì)查找上級(jí)視圖若發(fā)現(xiàn)BindingContext賦值會(huì)直接繼承,如果上級(jí)視圖同樣沒(méi)有BindingContext賦值且存在上級(jí)視圖會(huì)繼續(xù)搜索上級(jí)視圖BindingContext的賦值。本例修改XAML布局代碼將Label的BindingContext刪除添加到StackLayout中,同樣會(huì)實(shí)現(xiàn)我們想要的效果。

<StackLayout BindingContext="{x:Reference Name=slider}">
     <Label Text="Opacity Binding Demo"
            FontSize="Large"
            VerticalOptions="CenterAndExpand"
            HorizontalOptions="Center"
            Opacity="{Binding Path=Value}" />

     <Slider x:Name="slider" VerticalOptions="CenterAndExpand" />
</StackLayout>

同樣可以使用屬性節(jié)點(diǎn)定義方式設(shè)置Data Binding相關(guān)屬性

<Label Text="Opacity Binding Demo"
       FontSize="Large"
       VerticalOptions="CenterAndExpand"
       HorizontalOptions="Center">
       <Label.BindingContext>
            <x:Reference Name="slider" />
        </Label.BindingContext>
    <Label.Opacity>
            <Binding Path="Value" />
        </Label.Opacity>
</Label>

Reference對(duì)應(yīng)的C#類為ReferenceExtension,Binding對(duì)應(yīng)的類為BindingExtension。兩個(gè)類的定義都指定了Content Property,分別為Name和Path,所以可以簡(jiǎn)化代碼:

BindingContext="{x:Reference slider}"
Opacity="{Binding Value}" 

前面是通過(guò)BindingContext指定Data Binding的Source,還可以通過(guò)Binding指定Source。對(duì)應(yīng)的C#代碼為SetBinding兩個(gè)參數(shù)的方法:

BindingBase為abstract類,F(xiàn)orms提供了Binding類該類繼承了BindingBase。
通過(guò)Binding指定Source,再將Binding對(duì)象作為參數(shù)傳入SetBinding方法。

Binding 提供了重載的構(gòu)造函數(shù)和靜態(tài)方法Create<TSource>來(lái)創(chuàng)建Binding對(duì)象,不作介紹。


構(gòu)造函數(shù)示例

Create<TSource>示例

同樣X(jué)AML定義方式為,刪除BindingContext屬性賦值,修改Binding擴(kuò)展標(biāo)記。

根據(jù)內(nèi)容屬性簡(jiǎn)化XAML代碼:

Opacity="{Binding Value , Source={x:Reference slider}}"

擴(kuò)展標(biāo)記定義在一對(duì)大括號(hào)內(nèi)且大括號(hào)內(nèi)不應(yīng)出現(xiàn)雙引號(hào),指定多個(gè)屬性值時(shí)通過(guò)逗號(hào)分隔。
關(guān)于內(nèi)容屬性定義的簡(jiǎn)化寫(xiě)法《Creating Mobile Apps with Xamarin.Forms》中有提到“Even though BindingExtension defines Path as its content property, the argument name can be eliminated only when that argument is the first among multiple arguments.”大概意思是要省略內(nèi)容屬性的參數(shù)名稱必須將其放在第一個(gè)參數(shù),但是測(cè)試發(fā)現(xiàn)Opacity="{Binding Source={x:Reference slider} , Value}"這種寫(xiě)法同樣可以。

那么問(wèn)題來(lái)了,如果我們給BindingContext賦值的同時(shí)也為Binding的Source賦值,應(yīng)該將哪個(gè)屬性對(duì)應(yīng)的對(duì)象作為數(shù)據(jù)源。符合就近原則Source的優(yōu)先級(jí)高于BindingContext,即指定Source時(shí)不在考慮BindingContext。且Source使用更加靈活,如一個(gè)對(duì)象的多個(gè)屬性使用不同對(duì)象作為數(shù)據(jù)源只能通過(guò)Source方式指定。


Binding Mode 介紹

現(xiàn)在要通過(guò)Data Binding實(shí)現(xiàn)兩個(gè)Slider的Value相互影響。滑動(dòng)一個(gè)Slider的同時(shí)另一個(gè)有相同變化。

愚蠢的辦法是分別將兩個(gè)Slider作為另一個(gè)的Source,即同時(shí)為兩個(gè)Slider設(shè)置Data Binding。上一個(gè)Slider和Label的示例可以理解為Source影響Target,最簡(jiǎn)單的辦法就是可以使Source和Target相互影響。BindingMode枚舉可以幫助我們定義target 和 source之間的綁定模式。
BindingMode有四個(gè)枚舉值:
? Default
? OneWay — Source 的改變影響Target的值(通常是這種情況).
? OneWayToSource — Target的改變影響Source的值.
? TwoWay — Source和Target值改變會(huì)相互影響.

對(duì)于可讀寫(xiě)的BindableProperty對(duì)象默認(rèn)BindingMode為OneWay,只讀的BindableProperty對(duì)象默認(rèn)BindingMode為OneWayToSource。
大多數(shù)BindableProperty對(duì)象BindingMode默認(rèn)值為OneWay,以下控件的Property的BindingMode方式默認(rèn)是TwoWay:

由于Slider的Value默認(rèn)BindingMode為T(mén)woWay,所以實(shí)現(xiàn)兩個(gè)Slider連動(dòng)XAMlL定義為:

<StackLayout>
    <Slider x:Name="slider" VerticalOptions="CenterAndExpand" />
    <Slider BindingContext="{x:Reference Name=slider}" Value="{Binding Path=Value}" VerticalOptions="CenterAndExpand" />
</StackLayout>

通過(guò)XAML明確指定BindingMode的值Value="{Binding Path=Value Mode=TwoWay}"。通過(guò)C#代碼指定BindingMode的值slider.SetBinding(Slider.ValueProperty,"Value",BindingMode.TwoWay);


Binding StringFormat 介紹

再次把Slider作為Source,Label作為T(mén)arget。將Slider的Value值綁定到Label的Text。Value值ToString后直接顯示到Label上可能不是我們期望的,Binding類提供了StringFormat屬性表示.NET格式化字符串。

StringFormat效果

XAML中StringFormat使用,因?yàn)镾tringFormat本身會(huì)包含一對(duì)大括號(hào),所以StringFormat賦值時(shí)要包含一對(duì)單引號(hào):

C#代碼設(shè)置StringFormat:

label.SetBinding(Label.TextProperty, "Value", stringFormat: "Slider Value Is {0:F3}");

Binding IValueConvert 介紹

目前示例Target需要的數(shù)據(jù)為string,默認(rèn)轉(zhuǎn)換或StringFormat可以實(shí)現(xiàn)效果。但是Data Binding的Target接受數(shù)據(jù)類型為一個(gè)對(duì)象時(shí)如何處理?我們可以通過(guò)value converter類完成Source到Target的類型轉(zhuǎn)換,需要實(shí)現(xiàn)IValueConverter接口,接口有ConvertConvertBack兩個(gè)方法。

IValueConverter定義

當(dāng)數(shù)據(jù)由Source轉(zhuǎn)換到Target時(shí)調(diào)用Convert方法。Convert方法中value表示Source傳遞的值,你可以通過(guò)GetType來(lái)確定它的類型,也可以默認(rèn)一種類型來(lái)處理。targetType表示Target需要的數(shù)據(jù)類型,Convert方法返回的類型應(yīng)與targetType相同。parameter在Binding 中會(huì)用到,culture為CultureInfo類型需要和地域文化相關(guān)的轉(zhuǎn)換時(shí)會(huì)用到。

ConvertBack方法會(huì)在數(shù)據(jù)由Target轉(zhuǎn)換到Source 時(shí)調(diào)用,只有Binding Mode為T(mén)woWay 或者 OneWayToSource時(shí)才有必要實(shí)現(xiàn)該方法,否則直接返回null即可。value參數(shù)表示target傳遞的值,targetType表示source的Type類型。

示例實(shí)現(xiàn)效果,一個(gè)Entry 和一個(gè)Button,當(dāng)Entry中內(nèi)容為空時(shí)Button不可用,點(diǎn)擊Button清空Entry。

定義IntToBoolConverter實(shí)現(xiàn)IValueConverter,本示例中Entry為Source,Entry的Text.Length為Path,Button為T(mén)arget,IsEnabled為綁定的屬性。所以自定義的Converter應(yīng)有Int轉(zhuǎn)換為bool的能力(Convert方法,本例中ConvertBack可直接返回null)。

IntToBoolConverter類定義

XAML中使用Converter要先在Resources字典中定義IntToBoolConverter對(duì)象,指定key值,在Binding時(shí)通過(guò)StaticResource賦值。其中Resources相關(guān)內(nèi)容在Style中介紹。

XAML使用IValueConverter

如果Converter只使用一次,不必在Resources中定義,直接在Binding中通過(guò)屬性節(jié)點(diǎn)定義即可。


《Creating Mobile Apps with Xamarin.Forms》中提供了一個(gè)bool轉(zhuǎn)泛型的Converter類,可以將bool值轉(zhuǎn)換為我們想要的值。定義如下:

BoolToObjectConverter定義

在XAML定義時(shí),通過(guò) x:TypeArguments指定我們需要的類型,并設(shè)置TrueObject和FalseObject屬性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容