WPF中的数据绑定提供了很强大的功能。与普通的WinForm程序相比,其绑定功能为我们提供了很多便利,例如Binding对象的自动通知/刷新,Converter,Validation Rules,Two Way Binding等功能,省去了很多维护的繁琐工作。另外对于WPF中提供的数据模板功能,让我们可以轻松定制可以被复用的控制呈现的模块—但这是以数据绑定为前提来做到轻松易用的效果的。数据提供者例如XmlDataProvider和ObjectDataProvider更是简化了将对象以特定方式绑定并呈现的过程。可以说,数据绑定是WPF中让我们真正能够开始体现其便利性的特征之一,而对以数据驱动的应用来讲,其重要性不言而喻。

数据绑定的关键是System.Windows.Data.Binding对象,它会把两个对象(UI对象与UI对象之间,UI对象与.NET数据对象之间)按照指定的方式粘合在一起,并在他们之间建立一条通信通道,绑定一旦建立,接下来的应用生命周期中它可以自己独立完成所有的同步工作。根据其应用场合的不同我们将在本文中从以下几个部分分别讨论:

·         对象间的绑定

·         绑定到集合

·         数据模板

·         向绑定添加规则和转换器

1.     UI对象间的绑定

UI对象间的绑定,也是最基本的形式,通常是将源对象Source的某个属性值绑定 (拷贝) 到目标对象Destination的某个属性上。源属性可以是任意类型,但目标属性必须是依赖属性(Dependency Property)。通常情况下我们对于UI对象间的绑定源属性和目标属性都是依赖属性 (有些属性不是) ,因为依赖属性有垂直的内嵌变更通知机制,WPF可以保持目标属性和源属性的同步。

看个简单的例子是如何在XAML中实现数据绑定的:

<Window x:Class="Allan.WpfBinding.Demo.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Basic Bindings" Height="400" Width="700" Style="{StaticResource windowStyle}">

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="40" />

<RowDefinition Height="*" />

<RowDefinition Height="40" />

</Grid.RowDefinitions>

<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">

<Button x:Name="btnBasicBinding" Content="Basic" Style="{StaticResource buttonStyle}"></Button>

<Button x:Name="btnCollectionBinding" Content="Collection" Style="{StaticResource buttonStyle}"></Button>

<Button x:Name="btnDataTemplate" Content="Data Template" Style="{StaticResource buttonStyle}"></Button>

<Button x:Name="btnAdvanceBindings" Content="Advance" Style="{StaticResource buttonStyle}"></Button>

<Button x:Name="btnExit" Content="Exit" Style="{StaticResource buttonStyle}"></Button>

</StackPanel>

<StackPanel Grid.Row="1" HorizontalAlignment="Left">

<TextBox x:Name="txtName" Margin="5" Width="400" BorderThickness="0" Height="50" Text="Source Element"></TextBox>

<TextBlock x:Name="tbShowMessage" Margin="5" Width="400" Height="50" Text="{Binding ElementName=txtName,Path=Text }" />

</StackPanel>

</Grid>

</Window>

·         XAML绑定语法:

上边的代码我们将名为txtName的对象的Text属性作为源对象分别绑定给了两个TextBlock的Text属性。这里我们用了Binding关键字并指定了ElementName和Path,这两个就是指定源对象(Source)和源属性(Source Property). 通常我们在设定绑定时都用与StaticResource标记类似的语法{Binding… }并设置ElementName和Path属性:

Text=”{Binding ElementName=SourceObjectName, Path=SourceProperty}”

·         用Coding(C#)添加Binding

而对于C#里和绑定相关的代码,则看起来会罗嗦很多。但它们都同样的使用了Binding对象,然后指定PropertyPath的一个实例为源属性,然后可以有两个方法来加载绑定规则:

1.       调用FrameworkElement 或FrameworkContentElement对象的SetBinding方法

2.       调用BindingOperations.SetBinding静态方法

以下代码实现了和上边XAML文件类似的功能:

Binding binding = new Binding();

//设置源对象

binding.Source = txtName;

//设置源属性

binding.Path = new PropertyPath("Text");

//添加到目标属性

this.tbShowMessage.SetBinding(TextBlock.TextProperty, binding);

//or

//BindingOperations.SetBinding(tbShowMessage, TextBlock.TextProperty, binding);

 

·         用Coding(C#)移除Binding

当你在应用程序中某个地方添加了绑定,而在某个时候又不想这个绑定在接下来继续有效时,你可以有两种方式来断开这个绑定:

1.       用BindingOperations.ClearBinding静态方法。

例如BindingOperations.ClearBinding(currentTextBlock, TextBlock.TextProperty); BindingOperations同时还提供了ClearAllBindings方法,只需要传入要清除绑定的目标对象的名称,它就会将所有这个对象的绑定移除。

2.       简单的将目标属性设置为一个新的值。

这个简单的方法同样有效,可以断开与前边设置的binding的连接。简单的设置为任何值即可:如:currentTextBlock.Text = “it’s a new value.”;

·         Binding对象的属性

Property

Description

Converter

转换器

ElementName

绑定的源对象

FallbackValue

绑定无法返回有效值时的默认显示。

Mode

绑定方式

Path

属性

RelativeSource

常用于自身绑定或者数据模板中来指定绑定的源对象。

Source

源对象

StringFormat

格式化表达式

UpdateSourceTrigger

Sets the events on which binding will occur.

ValidationRules

验证规则

 

总结:对于对象间的绑定,绑定源为ElementName,Path为绑定源属性。ElementName必须为以下可选项之一:

DataContext

DataContext是WPF最后才试图查找的源。一旦RelativeSource和Source对象都没有被设置,则会在逻辑树种向上搜寻。

RelativeSource

用来标识和当前控件关联的对象,通常用于自我引用或数据模板。

Source

数据提供者/对象

2.     绑定到集合

 

·         利用ItemsSource来绑定数据源

常用标记:{Binding Path =””}    ItemSource    DisplayMemberPath

通常来说这是我们在做以数据驱动为主的应用时最经常用到的绑定方式。WPF支持任何类型的.NET对象作为数据源绑定到WPF对象。对于所有的ItemsControl对象都有一个ItemsSource依赖属性,这是专门为数据绑定而准备的。ItemsSource的类型是IEnumerable,所以对于我们几乎所有的集合类型我们都可以轻易的改变成ItemsSource的源对象。通过以下语句我们可以将一个名为photos的集合赋予ListBox对象,并以显示Name属性的值:

<ListBox x:Name=”pictureBox” DisplayMemberPath=”Name”

ItemsSource=”(Binding {DynamicResource photos}”

我们知道,依赖属性内建的垂直通知功能让UI对象间的绑定可以自己负责同步处理,但是对于.NET集合/对象来讲,它不具备这样的能力。为了让目标属性与源集合的更改保持同步,源集合必须实现一个叫INotifyCollectionChanged的接口,但通常我们只需要将集合类继承于ObservableCollection类即可。因为ObservableCollection实现了INotifyPropertyChanged和INotifyCollectionChanged接口。示例代码中我们这么去定义Photos集合类:

public class Photos : ObservableCollection<Photo>

·         利用DataContext来作为共享数据源

常用标记:{Binding Path=””}   DataContext

顾名思义,DataContext就是数据上下文对象,它是为了避免多个对象共享一个数据源时重复的对所有对象显式地用binding标记每个Source/RelativeSource/ElementName,而把同一个数据源在上下文对象的某个范围内共享,这样当一个绑定没有显式的源对象时,WPF会便利逻辑数找到一个非空的DataContext为止。

例如我们可以通过以下代码给ListBox和Title设置绑定:

<StackPanel Orentation=”Vertical” Margin=”5” DataContext=”{DynamicResource photos}”>

<Label x:Name=”TitleLabel” Content=”{Binding Path=Count}” DockPanel.Dock=”Bottom” />

<ListBox x:Name=”pictureBox” DisplayMemeberPath=”Name” ItemSource=”{Binding}” />

</StackPanel>

对于这些简单的绑定我们可以很灵活的组合他们的应用来达到我们的要求,这也是我们通常使用的方法。例如:

<Window.Resources>

<local:Employee

x:Key="MyEmployee"EmployeeNumber="123"FirstName="John"

LastName="Doe"Department="Product Development"Title="QA Manager"/>

</Window.Resources>

<GridDataContext="{StaticResource MyEmployee}">

<TextBoxText="{Binding Path=EmployeeNumber}"></TextBox>

<TextBoxText="{Binding Path=FirstName}"></TextBox>

<TextBoxText="{Binding Path=LastName}"/>

<TextBoxText="{Binding Path=Title}"></TextBox>

<TextBoxText="{Binding Path=Department}"/>

</Grid>

总结:对于集合的绑定,通常会需要用到以下几个标记:

DisplayMemberPath

指定源对象中被显示的属性。ToString()方法会被默认调用。

ItemsSource

指定要显示的数据源

ItemsTemplate

指定以什么样的格式来显示数据(类似于符合控件,可以在数据模板中利用多种控件来控制展现方式)

Path

数据源对象中的属性—控制显示

DataContext

共享数据源

3.     数据模板 – Data Template

当源属性和目标属性为兼容的数据类型,且源所显示的东西正是你需要显示的东西时,数据绑定确实很简单,你只需要向Section 1中讲的来匹配对象关系即可。而通常情况下我们对数据绑定都要做一些定制,特别对于.NET对象的绑定,你需要将数据源按照不同的方式分割显示。Data Template就负责来完成这样的功能:按照预想的数据展现模式将数据源的不同部分显示,而其作为可以被复用的独立结构,一旦定义可以被添加到一个对象内部,将会创建一个全新的可视树。

数据模板通常会被应用到以下几类控件来填充其类型为DataTemplate的属性:

·         内容控件(Content Control):ContentTemplate属性,控制Content的显示

·         项控件(Items Control) : ItemTemplate属性,应用于每个显示的项

·         头控件(Header Content Control) : HeaderTemplate属性,控制Header的展现。

每个数据模板的定义都是类似的方式,你可以像设计普通的窗体一样来设计其展现的方式,而且他们共享数据模板父空间所赋予的绑定源。例如下边的代码我们用一个图片来替代ListBox中的每一项:

<ListBox x:Name="pictureBox" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">

<ListBox.ItemTemplate>

<DataTemplate>

<Image Source="{Binding Path=FullPath}" Margin="3,8" Height="35">

<Image.LayoutTransform>

<StaticResource ResourceKey="st"/>

</Image.LayoutTransform>

<Image.ToolTip>

<StackPanel>

<TextBlock Text="{Binding Path=Name}"/>

<TextBlock Text="{Binding Path=DateTime}"/>

</StackPanel>

</Image.ToolTip>

</Image>

</DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

最终的ListBox中每一项的展现将按照我们在数据模板中设定的样式以图片来显示:

通常数据模板是不需要被内联声明的,它可以被定义成一个资源存放在Application.Resources这样的全局资源辞典中,或者单独的Resource Dictionary中在多个元素间共享。

4.     向绑定添加规则和转换器

·         使用值转换器Value Converter

无论你的绑定XAML写得多么漂亮,所有的绑定值毫无疑问你都可以得到,但是它不总是可以满足你不经过任何程序变化显示出来就能满足要求的。例如对于本文示例代码的照片总数的显示,我们还想显示得更为智能一些:对于一些符合某种要求的数据我们将其背景显示为黄色,而对于有多于一条记录时我们显示15 Items,仅有一条时显示1 Item。这时Value Converter就派上用场了。

要定义一个Value Converter需要声明一个类让其继承于System.Windows.Data.IValueConverter接口,并实现其中的两个方法Convert和ConvertBack方法。

public class RawCountToDescriptionConverter : IValueConverter

{

public object Convert(object value, Type targetType, object parameter,

CultureInfo culture)

{

// Let Parse throw an exception if the input is bad

int num = int.Parse(value.ToString());

return num + (num == 1 ? " item" : " items");

}

public object ConvertBack(object value, Type targetType, object parameter,

CultureInfo culture)

{

throw new NotSupportedException();

}

}

在XAML中声明资源,然后将其通过静态资源引用的方式赋予Binding对象的Converter属性。

<Window.Resources>

<local:CountToBackgroundConverter x:Key="myConverter"/>

<local:RawCountToDescriptionConverter x:Key="myConverter2"/>

</Window.Resources>

<TextBlock x:Name="filePath" DockPanel.Dock="Top" Style="{StaticResource titleStyle}"

Text="{Binding Count, Converter={StaticResource myConverter2}}"></TextBlock>

同样,我们可以对输入进行转换。如果数据的输入是被验证规则(如果有的话)标记为有效的,那么值转换器将会被调用,来对输入进行转换后反应出来。(参考附件代码中的BindingConverter窗体)

·         向绑定添加规则

每个Binding对象都有一个ValidationRules属性,可以被设置为一个或多个派生自ValidationRule的对象,每个规则都会检查特定的条件并更具结果来标记数据的有效性。就像我们在ASP.NET中应用RequiredValidator, CustomValidator一样,你只需要定义自己的规则,WPF会在每次调用数据时(通常是TextBox等输入控件失去焦点)会调用验证检查。这些是在值转换器之前发生的,如果数据无效,它会标记此次更新无效,并将数据标记为无效—这是通过设置目标元素的Validation.HasError属性为true并触发Validation.Error事件(ValidationResult会被返回,并且其IsValid属性为false)。我们可以通过一个触发器来设定当数据无效时对用户的提示。例如下边的代码我们就通过定义一个JpgValidationRule,当数据无效时通过tooltip来提示用户输入无效。

public class JpgValidationRule : ValidationRule

{

public override ValidationResult Validate(object value, CultureInfo cultureInfo)

{

string filename = value.ToString();

// Reject nonexistent files:

if (!File.Exists(filename))

{

return new ValidationResult(false, "Value is not a valid file.");

}

// Reject files that don’t end in .jpg:

if (!filename.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase))

{

return new ValidationResult(false, "Value is not a .jpg file.");

}

else

{

return new ValidationResult(true, null);

}

}

}

上边的代码定义了我们验证的规则。接下来在XAML中来应用这个规则。我们将这个规则用来检测输入框中的数据是否合法:

<TextBox Style="{StaticResource validateTextBoxStyle}">

<TextBox.Text>

<Binding UpdateSourceTrigger="PropertyChanged" Path="Department">

<Binding.ValidationRules>

<local:JpgValidationRule/>

</Binding.ValidationRules>

</Binding>

</TextBox.Text>

</TextBox>

当数据不合法时我们以什么样的方式来告诉用户呢?这里有两个方法可以做,一个是定义你自己的ErrorTemplate,另外一个是根据Trigger来设置一些可见信息。通常我们都可以来自己定义一些Error Provider和可以复用的ErrorTemplate,这个话题我们会在下一篇文章中讲。这里我们只让背景做改变并用tooltip来提示用户—显示的是ValidationRule返回的出错信息。因为都是控制显示的,所以定义成共用的Style:

<Style x:Key="validateTextBoxStyle" TargetType="{x:Type TextBox}">

<Setter Property="Width" Value="300" />

<Style.Triggers>

<Trigger Property="Validation.HasError" Value="True">

<Setter Property="Background" Value="Red"/>

<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>

</Trigger>

</Style.Triggers>

</Style>

总的来说,对于验证,我们常用一下几个属性来定义错误验证规则和错误展现方式:

  • Errors – 错误信息集合
  • HasError – 是否有错误出现.
  • ErrorTemplate – 错误提示的展现方式.
  • Binding.ValidationRules 绑定验证规则

Coming Next:

本文我们了解了有关Binding以及和绑定有关的附加验证规则,转换器等。附加验证规则我们将在下一篇中了解更多自定义Error Provider,Error Template等。附加的Demo里提供了所有本文中的实例。在下一篇中我们会了解以下几个问题:

·         Validation Rules

·         Triggers

转载地址:http://www.cnblogs.com/zlgcool/archive/2008/10/22/1316605.html

WPF学习之数据绑定相关推荐

  1. WPF学习笔记(数据绑定篇3)

    接上回的<WPF学习笔记(数据绑定篇2)>,继续 BindValidation 此示例演示了: 如何使用错误模板: 使用样式显示错误信息: 如何在校验发生异常时执行回调: 首先,你可以看见 ...

  2. WPF学习拾遗(二)TextBlock换行

    原文:WPF学习拾遗(二)TextBlock换行 下午在帮组里的同事解决一个小问题,为了以后方便,把就把它收集一下吧. 新建一个TextBlock作为最基础的一个控件,他所携带的功能相对于其他的控件要 ...

  3. WPF入门:数据绑定

    原文:WPF入门:数据绑定 上一篇我们将XAML大概做了个了解 ,这篇将继续学习WPF数据绑定的相关内容 数据源与控件的Binding Binding作为数据传送UI的通道,通过INotityProp ...

  4. WPF学习12:基于MVVM Light 制作图形编辑工具(3)

    本文是WPF学习11:基于MVVM Light 制作图形编辑工具(2)的后续 这一次的目标是完成 两个任务. 本节完成后的效果: 本文分为三个部分: 1.对之前代码不合理的地方重新设计. 2.图形可选 ...

  5. 【WPF学习】第五十章 故事板

    正如上一章介绍,WPF动画通过一组动画类(Animation类)表示.使用少数几个熟悉设置相关信息,如开始值.结束值以及持续时间.这显然使得它们非常适合于XAML.不是很清晰的时:如何为特定的事件和属 ...

  6. ComponentOne For WPF学习心得-实时数据

    ComponentOne For WPF学习心得-实时数据 写在前面 不断更新的数据 数据源绑定 写在前面 这是ComponentOne For WPF学习的第三篇文章,我对比着读了前两篇文章,感觉还 ...

  7. .NET-7.WPF学习2. 知识总结

    WPF学习2. 知识总结 前言 一.面试 二.代码片段 三.查看链接 前言 对wpf 的知识总结. 一.面试 1. 跨线程操作(Dispatcher)2. template(模板类型[控件模板.数据模 ...

  8. Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定)

    原文:Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定) ------------------------------ ...

  9. WPF学习开发客户端软件-任务助手(下 2015年2月4日代码更新)

    时光如梭,距离第一次写的 WPF学习开发客户端软件-任务助手(已上传源码)  已有三个多月,期间我断断续续地对该项目做了优化.完善等等工作,现在重新向大家介绍一下,希望各位可以使用,本软件以实用性为主 ...

  10. 【翻译】WPF中的数据绑定表达式

    有很多文章讨论绑定的概念,并讲解如何使用StaticResources和DynamicResources绑定属性.这些概念使用WPF提供的数据绑定表达式.在本文中,让我们研究WPF提供的不同类型的数据 ...

最新文章

  1. 2010年5月八达岭野生公园 51CTO老虎围观团强力插入
  2. ueditor初始化
  3. 服务器网站目录,服务器网站目录
  4. promise ajax 队列,ES6中的promise,从使用promise封装ajax说起
  5. 异步socket优雅的关闭-CancelIO和SO_LINGER
  6. lingo与matlab部分常用函数总结
  7. Kaggle 数据清洗挑战 Day 3 - 快速解析日期(date)数据
  8. url-pattern配置技巧
  9. vue $slot基本用法
  10. Exchange 混合部署—Office 365 注册及预览
  11. 软考 系统分析师教程 免费拿走不谢
  12. 使用you-get下载blbl视频
  13. 蓝牙中的三种音频编码:Apt-X、SBC、AAC,请问分别有什么区别?
  14. 改进平滑滚动,修改音量调节级数实现音量微调【编译自XDA 适用于大部分设备】
  15. 利用单片机给直流升压
  16. 录入年、月、日,判断日期的合法性
  17. 数组题目:全局倒置与局部倒置
  18. 【NLP】第11章 让你的数据说话:故事、问题和答案
  19. Waledac的分析
  20. 计算机注销命令,电脑的注销命令是什么?

热门文章

  1. 【协同任务】基于matlab遗传算法考虑分配次序的多无人机协同任务分配【含Matlab源码 143期】
  2. 【光学】基于matlab多缝夫琅禾费衍射【含Matlab源码 061期】
  3. 人工智能+智能运维解决方案_人工智能驱动的解决方案可以提升您的项目管理水平
  4. 仅使用Python代码从零开始进行Logistic回归
  5. 不平衡数据集_我们的不平衡数据集
  6. 计算机excel函数lookup,详细介绍数据查找VLookup函数在Excel中的应用举例
  7. c语言回文串试题,最短回文串 -- C语言
  8. range函数--python内置函数
  9. (VS2013)MFC对话框中用多个按钮创建多个子对话框实现选项卡效果(自己有修改)
  10. 填坑唯品会分布式调度Saturn