很多時(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è)概念Source
和 Target
。當(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ì)象,不作介紹见芹。
同樣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)化寫法《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}"
這種寫法同樣可以徘铝。
那么問(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ì)于可讀寫的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為TwoWay养晋,所以實(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作為Target。將Slider的Value值綁定到Label的Text姆泻。Value值ToString后直接顯示到Label上可能不是我們期望的零酪,Binding類提供了StringFormat
屬性表示.NET格式化字符串冒嫡。
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
接口,接口有Convert
和ConvertBack
兩個(gè)方法峻呛。
當(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為TwoWay 或者 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為Target罗侯,IsEnabled為綁定的屬性器腋。所以自定義的Converter應(yīng)有Int轉(zhuǎn)換為bool的能力(Convert方法,本例中ConvertBack可直接返回null)钩杰。
XAML中使用Converter要先在Resources
字典中定義IntToBoolConverter對(duì)象蒂培,指定key值,在Binding時(shí)通過(guò)StaticResource
賦值榜苫。其中Resources相關(guān)內(nèi)容在Style中介紹护戳。
如果Converter只使用一次,不必在Resources中定義垂睬,直接在Binding中通過(guò)屬性節(jié)點(diǎn)定義即可媳荒。
《Creating Mobile Apps with Xamarin.Forms》中提供了一個(gè)bool轉(zhuǎn)泛型的Converter類抗悍,可以將bool值轉(zhuǎn)換為我們想要的值。定義如下:
在XAML定義時(shí)钳枕,通過(guò) x:TypeArguments
指定我們需要的類型缴渊,并設(shè)置TrueObject和FalseObject屬性。