貌似最近來問我XAML這塊的東西的人挺多的。有時候看他們寫XAML這塊覺着也挺吃力的,所謂基礎不牢,地動山搖。XAML這塊雖然說和HTML同樣屬於標記語言,可是世界觀相對更加龐大一點。git
今天講講XAML中的Binding。沒啥技術含量,全當是快速閱讀。github
Binding做爲MVVM模式的一個相對核心的功能,一直是有爭議的。使用數據綁定能夠將咱們的View和Model解耦,可是若是一旦出現Bug,咱們將很難調試,還有一個問題就是數據綁定會帶來過大的內存開銷。
跟多數的技術同樣,都有本身的兩面性,具體運用場景如何抉擇,應該充分考慮。windows
做爲XAML的一部分,Binding的功能也隨着XAML版本的變動着。目前爲止,XAML一共有如下幾個版本:mvc
- WPF Version
- Silverlight 3 Version
- Silverlight 4 Version
- Windows 8 XAML/Jupiter(Windows Runtime XAML Framework) Version
其中WPF版本的Binding功能最強大,但也開銷最大。本文中,咱們主要講述最新版本,即__Windows 8 XAML/Jupiter__這個版本的XAML中的Binding。app
要想講明白Binding這個東西,咱們先要從Binding類的繼承層次開始講。mvvm
public class Binding : BindingBase, IBinding, IBinding2 { public Binding(); public IValueConverter Converter { get; set; } public System.String ConverterLanguage { get; set; } public System.Object ConverterParameter { get; set; } public System.String ElementName { get; set; } public BindingMode Mode { get; set; } public PropertyPath Path { get; set; } public RelativeSource RelativeSource { get; set; } public System.Object Source { get; set; } public System.Object FallbackValue { get; set; } public System.Object TargetNullValue { get; set; } public UpdateSourceTrigger UpdateSourceTrigger { get; set; } }
能夠看到Binding繼承了BindingBase類,還繼承了IBinding,IBinding2的接口。咱們再分別看下這三個類和接口。首先看下咱們的BindingBase。ide
public class BindingBase : DependencyObject, IBindingBase { public BindingBase(); }
BindingBase又繼承了DependencyObject和IBindingBase。DependencyObject這個咱們就很少講了,IBindingBase只是一個空接口。看來BindingBase沒有看到太多信息,咱們再來看下IBinding和IBinding2。函數
internal interface IBinding { IValueConverter Converter { get; set; } System.String ConverterLanguage { get; set; } System.Object ConverterParameter { get; set; } System.String ElementName { get; set; } BindingMode Mode { get; set; } PropertyPath Path { get; set; } RelativeSource RelativeSource { get; set; } System.Object Source { get; set; } }
internal interface IBinding2 { System.Object FallbackValue { get; set; } System.Object TargetNullValue { get; set; } UpdateSourceTrigger UpdateSourceTrigger { get; set; } }
微軟設計了一個Binding的基礎模型,蘊含了接口分離原則(ISP)的思想,又提供了一個IBindingBase的空接口,若是你想實現本身的Binding模型,能夠繼承這個接口,這樣能夠和.NET類庫風格統一。ui
既然咱們已經瞭解了Binding的大概的層次結構,那咱們開始一個個講講這些都是怎麼用的。this
Path是咱們相對用的比較多的,多數狀況下,咱們能夠這樣寫
Text="{Binding}"
XAML會取綁定源的ToString的值,因此咱們能夠重寫Override方法來實現咱們的須要的綁定。
咱們也能夠綁定具體的屬性,好比:
Text="{Binding Name}"
若是咱們綁定了一個集合,那咱們也能夠嘗試這樣寫:
Text="{Binding MyList[1].Name}"
除了上述比較經常使用的,咱們還有一個叫作ICustomPropertyProvider的接口,當你的類實現了這個接口中的
string GetStringRepresentation()
XAML就會去取這個函數返回的值。
因此總結下咱們的Path的綁定方式:
默認狀況: target Text="{Binding}" source ToString() //你能夠重寫ToString方法來改變值 屬性綁定: target Text="{Binding Name}" source public String Name { get;} 索引器綁定: target Text="{Binding MyList[1].Name}" 實現ICustomPropertyProvider的綁定: target Text="{Binding}" source String GetStringRepresentation() //實現方法獲取值
Mode一共有三種,OneTime,OneWay,TwoWay。看字面的意思就很容易理解。
//OneTime Text="{Binding, Mode=OneTime}" //OneWay Text="{Binding, Mode=OneWay}" //TwoWay Text="{Binding, Mode=TwoWay}"
在OneWay和TwoWay中,若是想要對象的值變動時讓綁定目標也變化,須要注意一下兩點
- 對於普通的屬性,須要類實現INotifyPropertyChanged,而且對象值變化時手動通知變動。
- 對於依賴屬性,當觸發SetValue方法後,PropertyChangedCallBack會通知變動,因此無需咱們手動操做。
在UWP系統中,Mode的默認值爲__OneWay__。
RelativeSource是一種相對關係找數據源的綁定。目前有兩種:Self和TemplatedParent
//Self <TextBlock Text="{Binding Foreground.Color.R, RelativeSource={RelativeSource Mode=Self}}" Foreground="Red"/> //TemplatedParent <DataTemplate> <Rectangle Fill="Red" Height="30" Width="{Binding Width, RelativeSource={RelativeSource Mode=TemplatedParent}}"> </DataTemplate>
RelativeSource綁定的方式咱們經常使用於控件模板。默認值通常爲null。
ElementName也是咱們最經常使用的一種綁定方式,使用這個咱們須要注意兩點:
- 指定的ElementName必須在當前XAML名稱範圍裏。
- 若是綁定目標位於數據模板或控件模板中,則爲模板化父級的XAML名稱範圍。
舉個例子:
<UserControl x:Name="Instance" Background="Red"> <Grid Background="{Binding Background, ElementName=Instance}"/> </UserControl> //or <Page x:Name="Instance"> <Grid> <ItemsControl ItemsSource="{Binding Users}"> <ItemsControl.ItemTemplate> <DataTemplate> <Button Command=" {Binding DataContext.TestCommand, ElementName=Instance}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Page>
Source也是咱們經常使用的一種方式。
<Page> <Page.Resources> <SolidColorBrush x:Key="MainBrush" Color="Orange"/> </Page.Resources> <Grid Background="{Binding Source={StaticResource MainBrush}}"/> </Page> //固然咱們也能夠簡寫爲 <Grid Background="{StaticResource MainBrush}"/>
通常狀況下,Source,ElementName和RelativeSource三者是互斥的,指定多餘一種的綁定方式會引起異常。
咱們很難保證咱們的對象值和咱們綁定目標的類型一直,因此轉換器能夠將類型就行轉換。
使用轉換器咱們要實現IValueConverter接口:
public class BoolVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { bool? result = value as Nullable<bool>; if(result == true) { return Visibility.Visible; } return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
若是你的值須要轉換回去,你也能夠繼續實現ConvertBack方法。
<Page> <Page.Resources> <local:BoolVisibilityConverter x:Key="BoolVisibilityConverter"/> </Page.Resources> <Grid> <Border Visibility="{Binding Busy, Converter={StaticResource BoolVisibilityConverter}}"/> </Grid> </Page>
這兩個參數不能綁定,只能指定常量值。
<Border Visibility="{Binding Busy, Converter={StaticResource BoolVisibilityConverter}, ConverterParameter=One, ConverterLanguage=en-US}"/>
IBinding中的參數基本上覆蓋了咱們多數的需求。儘管相對於WPF缺乏了多值綁定等等,但咱們也可以經過自定義一些附加屬性來實現這些功能。
IBinding2中的參數就相對使用的比較少了。
FallbackValue的用途是:當綁定對象不存在時,咱們就使用FallbackValue的值:
<Page> <Page.Resources> <x:String x:Key="ErrorString">Not Found</x:String> </Page.Resources> <Grid> <TextBlock Text="{Binding Busy, FallbackValue={StaticResource ErrorString}}"/> </Grid> </Page>
TargetNullValue的用途是:當綁定對象爲空時,咱們就使用TargetNullValue的值:
<Page> <Page.Resources> <x:String x:Key="ErrorString">Not Found</x:String> </Page.Resources> <Grid> <TextBlock Text="{Binding Busy, TargetNullValue={StaticResource ErrorString}}"/> </Grid> </Page>
UpdateSourceTrigger的值有三種:Default,PropertyChanged,Explicit。
多數狀況下大多數依賴項屬性的默認值都爲 PropertyChanged。可是Text屬性不是。
PropertyChanged的意思是當綁定目標屬性更改時,當即更新綁定源。而Explicit是隻有UpdateSource方法時才更新綁定源。
舉個例子:
<Grid> <TextBox x:Name="TitleTextBox" Text="{Binding Title, ElementName=Instance, UpdateSourceTrigger=Explicit, Mode=TwoWay}" /> <Button Click="Button_Click"/> </Grid> private void Button_Click(object sender, RoutedEventArgs e) { var current = this.Title; TitleTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource(); current = this.Title; }
有關uwp的Binding就說到這裏。謝謝~
被誤解的MVC和被神化的MVVM
Extensible Application Markup Language
RelativeSource 標記擴展
UpdateSourceTrigger enumeration