C# Wpf Binding (元素绑定)使用详解
简单的说 数据绑定 是一个关系,该关系告诉WPF 从源对像提取一些信息,并用这些信息设置目标对像的属性。
在目标对像中,被设置绑定的属性必须是 依赖项属性,通常在WPF 元素中。
注意:
尽管从元素到元素的绑定是最简单的方,但是在真正的项目开发中,大量的数据绑定是将元素x绑定到数据对像。从而可以显示 从 数据库或者文件 中提取的数据。
从元素到元素的绑定通常应用于元素的交互方式自动化,用户自定义控件。
一、 绑定到元素
1 绑定表达式
使用 xaml 标记拓展(也就是用{}语法)。
- 1
2 弄清 源对像、目标对像 、目标属性
<TextBox x:Name="sourceTBox" />
<TextBlock x:Name="tb" Text="{Binding ElementName=sourceTBox,Path=Text}" />
- 1
- 2
- 3
在上面的代码中,我们就用到了绑定。但是我个人建议,为了代码简洁,默认的属性和默认的属性配制不需要在代码中重写设置。
所有上面的代码可以优化成下面代码
<TextBox x:Name="sourceTBox" />
<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox}" />
- 1
- 2
绑定表达试中 Path 属性被省略, Path=Text 简写成 Text。为什么可以这样简写,是因为 Binding 类中有一个 带Path 的构造方法。
- 源对像
指 绑定表达式中 ElementName 属性所批向的元素,即 TextBox 为源对像(注:Binding的Source属性 和 Binding的RelativeSource属性指向的对像,也是源对像) - 目标对像
指 包含绑定表达式的元素 ,即 TextBlok 为目标对像 - 目标属性
指 属性的值设置了绑定表达式,这个属性就是 目标属性
3 绑定错误
Wpf 不会引发异常来通知与数据绑定相关的问题,如果绑定的属性不存在,不会收到任何提示,也不能在目标属性显示任何值。
WPF 绑定失败细节的跟踪信息会在 输出窗口 显示
WPF 绑定失败细节的跟踪信息会在 输出窗口 显示
WPF 绑定失败细节的跟踪信息会在 输出窗口 显示
如果要在控件中 显示 参考 Binding 的 TargetNullValue 属性
<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay},TargetNullValue='has error'" />
- 1
4 绑定模式
当设置 Binding.Mode 属性时。WPF 允许使用 5 个 system.windows.data.BindingMode 枚举值中的任何一个。代码如下,其中的 Mode=TwoWay 就是设置绑定模式
<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay}" />
- 1
system.windows.data.BindingMode 枚举值:
枚举名称 | 说明 |
---|---|
OneWay | 当源属性变化时更新目标属性 |
TwoWay | 当源属性变化时更新目标属性 ,当目标属性 变化时更新源属性 |
OneTime | 最初设定源属性时更新目标属性,后期的变化都被忽略 ,如果知道源属性不会变化,可以使用这种模式,降低开销 |
OneWayToSource | 与OneWay 类似,但方向相反,当目标属性变化时更新源属性,有点向后传递,但目标属性永远不会被更新 |
Default | 此类绑定依赖于目标属性,即可以是双向(对于用户可以设置的属性,如TextBlock.Text,) 也可以是单向,除非明确指明一种模式,否则都采用此种模式 |
5 CS 代码中使用 Binding
Binding binding = new Binding();
binding.Source = this.sourceTBox; //指向源对像 this 指向的当前的Window 对像 的 sourceTBox 对像 ,sourceTBox 是对像的名称,即(x:Name="**")
//binding.Source = this; //指向源对像 this 指向的当前的Window 对像 ,也可
binding.Path = new PropertyPath("Text");
this.tb.SetBinding(TagProperty, binding);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
使用 BindingOperations
Binding binding = new Binding();
binding.Path = new PropertyPath("Text");
//BindingOperations.SetBinding(this, TitleProperty, binding);
BindingOperations.SetBinding(this,TitleProperty,binding);
- 1
- 2
- 3
- 4
使用代码 获取绑定
Binding binding = BindingOperations.GetBinding(this.tb, TextBlock.TextProperty);
//获取绑定以后可以修改 bindig 的属性
//binding.Mode = BindingMode.TwoWay;
//...
//修改过后可以重先设置绑定
//BindingOperations.SetBinding(this.tb,TextProperty,binding);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
更实用的 BindingExpression 对像
BindingExpression expression = BindingOperations.GetBindingExpression(this.tb, TextBlock.TextProperty);
//get source element 获取源对像
TextBox textBox =(TextBox) expression.ResolvedSource;
//get any data you need from the source element
string name = textBox.Name;
// expression.ResolvedSource 获取绑定对像的引用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
BindingExpression 更加实用的信息,去源码里查看
6 多绑定 (多属性绑定)
<CheckBox x:Name="cb1"
Visibility="Collapsed"
IsChecked="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="50" Height="30"
BorderThickness="0"
IsVisibleChanged="cb1_IsVisibleChanged" />
<TextBox x:Name=“sourceTBox” />
<TextBlock x:Name=“tb”
Text=“{Binding Text,ElementName=sourceTBox,Mode=TwoWay}”
Tag=“{Binding Content ,ElementName=cb1,Mode=OneWay}”
Visibility=“{Binding IsChecked,ElementName=cb1,Converter={StaticResource Boolean2VisibilityConverter}}”
/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- TextBlock 的 Text 、Tag 、Visibility 属性都设置绑定表达式,这就是多绑定
- Visibility 属性民的绑定表达式 用到了 转换器,转换器的相关知识,另行学习。
7 绑定更新
使用代码
<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay,UpdateSourceTrigger=Default}"
- 1
UpdateSourceTrigger 枚举值
名称 | 说明 |
---|---|
PropertyChanged | 属性发生变化时更新 |
LostFous | 失去焦点时更新 |
Explicit | 调用 BindingExpress.UpdateSource()时更新 |
Defeat | 根据属性的元数据确定更新行为(FrameworkPropertyMetadata.DefaultUpdateSourceTriggere) 大多数默认行为 是PropertyChanged 但TextBox.Text 默认行为是 LostFocus |
8 延迟绑定
作用 :避免过分频繁的触发操作,导致频繁更新UI。
使用 Binding 对像的 Delay 属性。等待数毫秒之后再提交更新。
使用代码
<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay,Delay=500}" />
- 1
500 毫秒后再更新。
二、 绑定到非元素对像
在项目开发中,大量的数据绑定是将元素达到到数据对像。从而可以显示 从 数据库或者文件 中提取的数据,从而需要绑定到非元素的对象中。
- 绑定到非元素的对象时,需要 放弃 TargetElement 属性。
- 绑定到非元素的对象时,使用以下属性的其中一个。
– Soure:该属性是指向源对像的引用(数据的对像)
– RelativeSource:这是引用,使用RelativeSource 对像指向源对像 RelativeSource是一种特殊的工具,在编写控件模板和数据模板是很方便。
– DataContent:如果没有使用Source和RelativeSource 指定源,Wpf就从当前元素开始在元素树中向上查找。检查每一个元素的DataContent属性,并使用 第一个非空的DataContent 属性。当我们在要同一个对像绑定不同元素时 DataContent属性 是非常有用的。
Source 属性
使用代码
<TextBlock Text="{Binding Source,Source={x:Static SystemFonts.CaptionFontFamily}}"/>
- 1
第一个 Source 是 Path 属性的值
RelativeSource 属性
RelativeSource 属性可以根据相对目标对像的关系指向源对像,例如,可以使用RelativeSource 属性将元素绑定 自身 或 父元素。
设置 Binding.RelativeSource 属性 需要 RelativeSource 对像。语法会变得更加复杂。
- 绑定到父元素 Mode=FindAncestor
<TextBlock >
<TextBlock.Text>
<Binding Path="Title">
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor" AncestorType="{x:Type Window}" AncestorLevel="1" />
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>
<!--以上代码可以简写成如下-->
<TextBlock Text="{Binding Title,RelativeSource={RelativeSource Mode=FindAncestor,AncestorLevel=1,AncestorType={x:Type Window}}}" />
<!--一般一个窗口只有一个Window 对像,可以再次简写-->
<TextBlock Text="{Binding Title,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}" />
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 绑定到自身 Mode="Self"
<TextBlock >
<TextBlock.ToolTip>
<Binding Path="Text">
<Binding.RelativeSource>
<RelativeSource Mode="Self" />
</Binding.RelativeSource>
</Binding>
</TextBlock.ToolTip>
</TextBlock>
<!--以上代码可以简写成如下-->
<TextBlock Text="{Binding Text,RelativeSource={RelativeSource Mode=Self}}" />
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 绑定到列表数据中的前一个数据项 Mode="PreviousData"
<TextBlock >
<TextBlock.ToolTip>
<Binding Path="Text">
<Binding.RelativeSource>
<RelativeSource Mode="PreviousData" />
</Binding.RelativeSource>
</Binding>
</TextBlock.ToolTip>
</TextBlock>
<!--以上代码可以简写成如下-->
<TextBlock Text="{Binding Text,RelativeSource={RelativeSource Mode=PreviousData}}" />
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
此时 Path=Text ,Text 是数据列表中的前一个对像的属性,不一定是依赖项属性,这种模式只会在列表中使用
以上代码不是真实使用时的代码,只是说明 写法。
- 绑定到应用模板元素 Mode="TemplatedParent"
只有当绑定位于控件模板() 或 数据模板() 内部时,这种模式才能工作。
<Button x:Name="Btn1" Content="Button" Background="Blue" Tag="/themes/img/snend.png">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="PART_border" Background="{Binding Background,RelativeSource={RelativeSource Mode=TemplatedParent}}">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Source="{Binding Tag,RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
<TextBlock x:Name="txt" Text="{Binding Content,RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
TemplateBindingExtension 对像
我们可以如下理解:
- TemplateBindingExtension 对像 是 RelativeSource.Mode=TemplatedParent ,简化写法拓展。
- TemplateBinding 和 binding 有一个共同的祖先 MarkupExtension,只是在使用
TemplateBindingExtension 对像是 结尾的Extension 是可以省去。这只是一个命名的问题,binding
继承 BindingBase ,BindingBase 又继承自 MarkupExtension,binding 对像的命名也可以命名成 - BindingExtension。 TemplateBinding 和 binding 都是用来做数据绑定的。
- TemplateBindingExtension 就是 TemplateBinding 。
<Button x:Name="Btn1" Content="Button" Background="Blue" Tag="/themes/img/snend.png">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="PART_border" Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Source="{TemplateBinding Tag}"/>
<TextBlock x:Name="txt" Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
绑定表达式的代码明显简洁了很多。
DataContent属性
<TextBlock Text="{Binding Version}" />
- 1
Wpf就从当前元素开始在元素树中向上查找。检查每一个元素的DataContent属性,并使用 第一个非空的DataContent 属性 所引用的对像的 Version 属性。
如果 TextBlock的Text 直到 Window 对像DataContent 属性引用一个对像不为空,TextBlock的Text 就绑定到 Window 对像DataContent 属性引用对像的 Version 属性上。
//Window 对像DataContent 属性引用对像 SystemVersion
public class SystemVersion{
public int Code { get; set; }
public String Version { get; set; }
}
//设置 Window 对像DataContent 属性
this.DataContext = new SystemVersion()
{
Code = 103,
Version = “V1.0.3”
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
绑定代码
<TextBlock Text="{Binding Version}" />
- 1
三、 总结
- 文章中许多话语是根据自己在项目开发时使用的理解,可能表述不当或者错误,希望得到指出。
- 数据绑定是开发一个应用必不可少的东西。不用数据绑定也可开发一些应用程序。但在有些情况,不用数据绑定可以就不好实现,甚至无法实现 ,如:列表渲染,自定义控件,组件开发等。