前言
貌似最近來問我XAML這塊的東西的人挺多的。有時候看他們寫XAML這塊覺著也挺吃力的,所謂基礎(chǔ)不牢,地動山搖蛾找。XAML這塊雖說和HTML一樣屬于標記語言,但是世界觀相對更加龐大一點赵誓。
今天講講XAML中的Binding打毛。沒啥技術(shù)含量柿赊,全當是快速閱讀。
Binding作為MVVM模式的一個相對核心的功能幻枉,一直是有爭議的碰声。使用數(shù)據(jù)綁定可以將我們的View和Model解耦,但是如果一旦出現(xiàn)Bug熬甫,我們將很難調(diào)試胰挑,還有一個問題就是數(shù)據(jù)綁定會帶來過大的內(nèi)存開銷。
跟多數(shù)的技術(shù)一樣椿肩,都有自己的兩面性瞻颂,具體運用場景如何抉擇,應(yīng)該充分考慮郑象。
作為XAML的一部分贡这,Binding的功能也隨著XAML版本的變更著。目前為止厂榛,XAML一共有以下幾個版本:
- 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。
開始之前
要想講明白Binding這個東西正歼,我們先要從Binding類的繼承層次開始講。
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萄唇。
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; }
}
微軟設(shè)計了一個Binding的基礎(chǔ)模型,蘊含了接口分離原則(ISP)的思想拔妥,又提供了一個IBindingBase的空接口忿危,如果你想實現(xiàn)自己的Binding模型,可以繼承這個接口没龙,這樣可以和.NET類庫風格統(tǒng)一铺厨。
講講IBinding
既然我們已經(jīng)了解了Binding的大概的層次結(jié)構(gòu)缎玫,那我們開始一個個講講這些都是怎么用的。
IBinding中的Path
Path是我們相對用的比較多的解滓,多數(shù)情況下赃磨,我們可以這樣寫
Text="{Binding}"
XAML會取綁定源的ToString的值,所以我們可以重寫Override方法來實現(xiàn)我們的需要的綁定洼裤。
我們也可以綁定具體的屬性邻辉,比如:
Text="{Binding Name}"
如果我們綁定了一個集合,那我們也可以嘗試這樣寫:
Text="{Binding MyList[1].Name}"
除了上述比較常用的逸邦,我們還有一個叫做ICustomPropertyProvider的接口恩沛,當你的類實現(xiàn)了這個接口中的
string GetStringRepresentation()
XAML就會去取這個函數(shù)返回的值。
所以總結(jié)下我們的Path的綁定方式:
默認情況:
target Text="{Binding}"
source ToString()
//你可以重寫ToString方法來改變值
屬性綁定:
target Text="{Binding Name}"
source public String Name { get;}
索引器綁定:
target Text="{Binding MyList[1].Name}"
實現(xiàn)ICustomPropertyProvider的綁定:
target Text="{Binding}"
source String GetStringRepresentation()
//實現(xiàn)方法獲取值
IBinding中的Mode
Mode一共有三種缕减,OneTime雷客,OneWay,TwoWay桥狡〗寥梗看字面的意思就很容易理解。
//OneTime
Text="{Binding, Mode=OneTime}"
//OneWay
Text="{Binding, Mode=OneWay}"
//TwoWay
Text="{Binding, Mode=TwoWay}"
在OneWay和TwoWay中裹芝,如果想要對象的值變更時讓綁定目標也變化部逮,需要注意一下兩點
- 對于普通的屬性,需要類實現(xiàn)INotifyPropertyChanged嫂易,并且對象值變化時手動通知變更兄朋。
- 對于依賴屬性,當觸發(fā)SetValue方法后怜械,PropertyChangedCallBack會通知變更颅和,所以無需我們手動操作。
在UWP系統(tǒng)中缕允,Mode的默認值為OneWay峡扩。
IBinding中的RelativeSource
RelativeSource是一種相對關(guān)系找數(shù)據(jù)源的綁定。目前有兩種: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教届。
IBinding中的ElementName
ElementName也是我們最常用的一種綁定方式,使用這個我們需要注意兩點:
- 指定的ElementName必須在當前XAML名稱范圍里驾霜。
- 如果綁定目標位于數(shù)據(jù)模板或控件模板中案训,則為模板化父級的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>
IBinding中的Source
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三者是互斥的猜旬,指定多余一種的綁定方式會引發(fā)異常脆栋。
IBinding中的Converter
我們很難保證我們的對象值和我們綁定目標的類型一直倦卖,所以轉(zhuǎn)換器可以將類型就行轉(zhuǎn)換。
使用轉(zhuǎn)換器我們要實現(xiàn)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();
}
}
如果你的值需要轉(zhuǎn)換回去椿争,你也可以繼續(xù)實現(xiàn)ConvertBack方法怕膛。
<Page>
<Page.Resources>
<local:BoolVisibilityConverter x:Key="BoolVisibilityConverter"/>
</Page.Resources>
<Grid>
<Border Visibility="{Binding Busy, Converter={StaticResource BoolVisibilityConverter}}"/>
</Grid>
</Page>
IBinding中的ConverterParameter和ConverterLanguage
這兩個參數(shù)不能綁定,只能指定常量值秦踪。
<Border Visibility="{Binding Busy, Converter={StaticResource BoolVisibilityConverter},
ConverterParameter=One, ConverterLanguage=en-US}"/>
IBinding中的參數(shù)基本上覆蓋了我們多數(shù)的需求褐捻。盡管相對于WPF缺少了多值綁定等等,但我們也能夠通過自定義一些附加屬性來實現(xiàn)這些功能椅邓。
IBinding2
IBinding2中的參數(shù)就相對使用的比較少了柠逞。
IBinding2中的FallbackValue
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>
IBinding2中的TargetNullValue
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>
IBinding2中的UpdateSourceTrigger
UpdateSourceTrigger的值有三種:Default板壮,PropertyChanged,Explicit合住。
多數(shù)情況下大多數(shù)依賴項屬性的默認值都為 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;
}
有關(guān)uwp的Binding就說到這里僚害。謝謝~
參考資料
被誤解的MVC和被神化的MVVM
Extensible Application Markup Language
RelativeSource 標記擴展
UpdateSourceTrigger enumeration