貌似最近来问我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