(4)WPF深入解析绑定
1、WPF绑定
1.1、简介
WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能。
WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信息来设置目标对象的目标属性。
目标属性总是依赖属性。但源对象可以是任何内容,可以是一个WPF元素、或ADO.NET数据对象或自定义的数据对象等。
1.2、三种常见用绑定方式
1)Source绑定
最简单的绑定方法,常用来绑定图片、资源字典的源对象。
<Image Source="../Image/zhenghe_uideal_mr_plaque_narrow.png" Stretch="Fill" Height="32" Width="30" HorizontalAlignment="Left" /> <ResourceDictionary Source="/UIH.Mcsf.CommonControlTheme;component/CommonControl/Public.xaml" />
2)绑定元素对象的实现
绑定元素对象的数据源,是一个WPF元素对象的依赖属性。由于依赖属性具有内置的更改通知支持,因此,当在源对象中改变依赖属性的值时,会立即更新目标对象中的绑定属性。
下面通过一个简单的例子来演示下如何绑定元素对象。具体的XAML代码(这里不需要后台代码)如下所示:
<StackPanel> <Slider Name="sliderFontSize" Margin="3" Minimum="1" Maximum="40" Value="10" TickFrequency="1" TickPlacement="TopLeft"/> <TextBlock Margin="10" Text="LearningHard" Name="lbtext" FontSize="{Binding ElementName=sliderFontSize, Path=Value}"></TextBlock> </StackPanel>
在上面XAML代码中,TextBlock的FontSize属性绑定时,使用ElementName指定源元素对象名,Path指定源元素的依赖属性。
TextBlock控件的FontSize属性绑定了Slider控件的Value属性,由于源属性Value是依赖属性,具体内置的更改通知功能,所以Slider控件Value值的改变,直接影响TextBlock控件FontSize的值。运行结果如下,当移动上图中Slider控件上的游标时,下面的文本字体大小也会跟着一起改变:

3)DataContext绑定方式
- 数据驱动的应用程序中,更常见DataContext绑定方式的情况。
- 绑定的信息必须存储在一个公有属性中。因为WPF绑定不能获取私有信息或公有字段。
- 绑定表达式中的RelativeSource属性,可以指定绑定源对象相对位置;
- 如果元素没有DataContext,也没有用RelativeSource指定源对象的相对位置,那么WPF会从当前元素开始在元素树中向上查找,使用第一个非空的DataContext属性
可以省去重复在多个控件中设置相同的DataContext属性)。
下面演示如何使用DataContext属性来绑定一个自定义对象的属性。
首先自定义一个实现了INotifyPropertyChanged接口的类。这个接口是为了发出属性更改的通知,当源对象的公共属性发生改变时,该属性的值会立即响应到界面上显式。具体的实现代码如下所示:

运行结果如下:

1.3、绑定模式(BindingMode)
- OneWay:源属性变就更新目标属性。
- TwoWay:源属性变就更新目标,并且目标属性变就更新源属性。
- OneTime:只根据源属性来设置目标属性,以后都不会变。
- OneWayToSource:与OneWay相反,目标属性变就更新源属性。
- Default:可以单向或双向,是靠被指定的源属性或目标属性是否有get或set来指定的。
1.4、绑定更新(UpdateSourceTrigger)
通过指定Binding.UpdateSourceTrigger属性,设置在目标属性变化时,源属性的更新时机(因此只针对TwoWay和OneWayToSource绑定模式)。
UpdateSourceTrigger有以下4种类型:

对于UpdateSourceTrigger.Explicit模式。此时就需要额外编写代码手动触发更新,此时可以添加一个Apply按钮,并在按钮的Click事件处理程序中调用BindingExpression.UpdateSource方法触发立即刷新并更新字体大小的操作。具体的实现代码如下所示:
//获得应用于文本框上的绑定 BindingExpression be = txtFontSize.GetBindingExpression(TextBox.TextProperty); //调用UpdateSource更新源对象 be.UpdateSource();
1.5、RelativeSource
在WPF绑定的时候,可以使用RelativeSource,指当前元素和绑定源的位置关系。
1)控件关联自身的属性——Self
<Window x:Class="RelativeSource.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel> <TextBox Height="30" Width="60" Name="Box1" Text="{Binding RelativeSource={RelativeSource Mode=self},Path=Name }"/> </StackPanel> </Grid> </Window>
2)控件关联其父级容器的属性——AncestorType
下面是AncestorType的例子,其中:
- AncestorLevel,它指的是以Bingding目标控件为起点的层级偏移量,S1的偏移量是1,G2的偏移量是2,G1是偏移量3,
- AncestorType指的是要找的目标对象的类型。
值得注意的是AncestorLevel必须参考AncestorType使用,如下设置了AncestorType={x:Type Grid},则Bingding在寻找时会忽略非Grid的控件,此时G2的偏移量是1,G1的偏移量是2,StackPanel被忽略。
<Window x:Class="RelativeSource.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid Name="G1"> <Grid Name="G2"> <StackPanel Name="S1"> <TextBox Height="30" Width="60" Name="Box1" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}, AncestorLevel=2},Path=Name }"/> </StackPanel> </Grid> </Grid> </Window>
2)控件关联模板的属性——TemplatedParent
TemplatedParent用于控件模板(ControlTemplate)绑定,会指定源属性是模板使用者,可以使用以下两种方式指定:
Background="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"
Background="{TemplateBinding Foreground}"
使用TemplateBinding有以下不同
- Binding的一个轻量级版本
- 数据绑定是单向的,从数据源到目标。
- 数据对象进行自动转换,数据源和目标的数据类型不同时候,需要自己写转换器。
<Window.Resources> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="Green"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid> <Ellipse> <Ellipse.Fill> <SolidColorBrush Color="{Binding Path=Background.Color,RelativeSource={RelativeSource TemplatedParent}}"/> </Ellipse.Fill> </Ellipse> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources>
4、提高列表性能
对于列表控件,如果绑定的数据源具有大量记录时,可能会出现性能问题,对此,WPF做了以下两种方式种方法:
- 初始化性能提升:UI虚拟化
- 滚动性能提升:项目容器再循环和延迟滚动
1)UI虚拟化
列表仅为当前显示的项创建容器对象的一种技术,例如,如果有一个具有5万条记录的列表,但是可见区域只能包含30条记录,ListBox控件只创建30个ListBoxItem对象。
WPF中UI虚拟化是通过VirtualizingStackPanel容器实现的。像ListBox、ListView和DataGrid都自动使用VirtualizingStackPanel面板布局它们的子元素,所以,这些控件都默认支持虚拟化功能。然而,ComboBox需要支持虚拟化支持,必须明确提供新的ItemPanelTemplate添加虚拟化支持,具体实现如下所示:
<ComboBox> <ComboBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel></VirtualizingStackPanel> </ItemsPanelTemplate> </ComboBox.ItemsPanel> </ComboBox>
TreeView控件也支持虚拟化,但它在默认情况下,关闭了该支持,你需要显式启用该特性,具体使用的启用代码如下所示:
<TreeView VirtualizingPanel.IsVirtualizing="True" />
2)项目容器再循环
WPF 3.5 SP1使用项目容器再循环改进了虚拟化。但是如果启用了项目容器再循环,ListBox控件会保存少量ListBoxItem对象存活,当滚动时,将新数据加到这些之前的ListBoxItem对象,从而重复使用它们。
项目容器再循环提供了滚动性能,并降低了内存消耗量。为了确保向后兼容,除了DataGrid之后的所有列表控件默认都禁用该特性,如需支持,需要显式启用。
<ListBox VirtualizingPanel.VirtualizationMode="Recycling"/>
3)延迟滚动
为了进一步提供滚动性能,可以开启延迟滚动功能。使用延迟滚动,当用户在滚动条上拖动滚动滑块时不更新列表显示,只有用户释放了滚动滑块时才刷新。
但当用户需要在滚动时查看目前滚动位置,则就可以不启用该特性。
如需支持,也需要显式启用。
<ListBox ScrollViewer.IsDeferredScrollingEnabled="True"/>
摘自
https://www.cnblogs.com/zhili/tag/WPF/

浙公网安备 33010602011771号