WPF数据绑定-第1部分(译文)
By S.F.
本文链接 https://www.kyfws.com/news/wpf-data-binding-part-1/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 10 分钟阅读 - 4530 个词 阅读量 0WPF数据绑定-第1部分(译文)
原文地址:https://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1
原文作者:Joel Ivory Johnson
译文由本站翻译
前言
WPF数据绑定简介.
介绍
通常,在Windows Forms和ASP.NET应用程序中,数据绑定主要用于在屏幕上填充信息.有用于显示单个值的简单数据绑定,以及用于显示和格式化数据集合的复杂数据绑定.数据绑定的好处在于,您可以在编写界面时填充很少的代码甚至不编写任何代码.使用WPF中的数据绑定,您可以从任何对象的几乎任何属性获取数据,并将其绑定到另一个对象的几乎所有其他依赖项属性. 在这两个系列的第一部分中,我将介绍WPF中数据绑定的基础.在第二部分中,将介绍更高级的数据绑定概念,例如模板,并根据数据的特征更改信息显示的方式.对于这两个部分,您都需要对WPF有基本的了解.
先决条件
本文假定读者已经对以下WPF概念有基本了解:
基本
数据绑定以其最基本的形式将值从源对象复制到目标对象上的属性. source属性可以是任何东西.目标将是依赖项属性.绑定到作为源的WPF元素属性和绑定到另一个对象类型的主要区别在于WPF对象具有更改通知.一旦以WPF元素属性为源进行绑定,则当源发生更改时,目标属性将自动更新.让我们看几个非常简单的WPF数据绑定表达式.
<Window x:Class="Example00.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid >
<Grid.RowDefinitions >
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="mySourceElement" Grid.Row="0" >Hello World</TextBox>
<TextBlock Grid.Row="1">
<TextBlock.Text>
<Binding ElementName="mySourceElement" Path="Text" />
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding ElementName=mySourceElement,Path=Text }" Grid.Row="2" />
</Grid>
</Window>
您可能已经猜到了,当您运行此示例时,它将显示" Hello Word".文本显示三遍.显示文本的第一个实例,因为它是分配给文本框的文字值.接下来的两个实例在文本框中,是数据绑定的结果.这两个文本框都使用名称为" mySourceElement"的文本框作为源,而其" Text"属性作为目的地来创建绑定.虽然两个文本块绑定到相同的源并产生相同的结果,但使用的语法不同.第一个文本块使用扩展的语法,而第二个文本块使用标记扩展来表示相同的绑定.在本文档中,我将使用扩展标记,但请记住,在大多数情况下,都可以使用这两种语法.这是一个更有趣的数据绑定示例:
<Window x:Class="Example01.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Example 01" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions >
<RowDefinition Height="40px" />
<RowDefinition Height="40px" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Slider Name="fontSizeSlider" Minimum="5"
Maximum="100" Value="10" Grid.Row="0" />
<TextBox Name="SizeTextBox"
Text="{Binding ElementName=fontSizeSlider, Path=Value}" Grid.Row="1"/>
<TextBlock Text="Example 01"
FontSize="{Binding ElementName=SizeTextBox, Path=Text}" Grid.Row="2"/>
</Grid>
</Window>
在此示例中,将三个元素绑定在一起. “文本"框的"文本"属性绑定到"滑块"的"值"属性,而"文本"框的” FontSize"属性绑定到"文本"框的文字
属性.如果您运行该程序,您将看到这种连锁关系的影响.更改滑块将导致在文本框中显示数字,并适当调整字体大小. WPF能够在更改滑块时应用更改,因为元素属性实现了" INotifyPropertyChanged"接口.顾名思义,当属性值更改时,WPF就会知道并知道将值重新应用于目标属性.
另外,请注意,如果单击文本框并手动更改其值,则在从文本框中跳出后,滑块和文本块都会同时更新.在此示例中,数据似乎双向流动(从滑块到文本框,从文本框到滑块).数据绑定的这种行为是通过绑定模式控制的.
装订模式
绑定模式设置方向数据流向的方向.数据通常从源流向目的地.有五种数据绑定模式. 首先," OneWayToSource"模式显得多余.乍一看,由于它反转了复制数据的方向,因此人们可能会认为,通过反转绑定表达式所附加的元素,可以实现相同的功能.由于目标元素必须是依赖项属性,因此并非总是这样.当我们需要使用依赖项属性的值设置非依赖项属性时,您将需要使用" OneWayToSource"模式.
控制何时发生绑定
向前发生的绑定(从源到目标)会立即发生.更改滑块,文本框中的数字值或组合框值时,更改将立即在文本块中发生.但是,您可能已经注意到,当您在文本框中输入数值时,滑块将不会更新,直到文本框失去焦点.默认情况下,反向绑定不会立即发生. UpdateSourceTrigger属性可用于设置此行为.
绑定到不是元素的对象上的公共属性
到目前为止,我们已经研究了绑定到WPF元素.当绑定到不是元素的属性时,绑定表达式的" ElementName"属性必须设置为以下值之一. *-实际上没有运行时搜索发生. DataContext属性继承其值. 如果在绑定表达式中未定义任何源,则假定该源是数据上下文.当几个元素绑定到同一个源对象时,数据上下文的隐式使用可以节省很多键入操作.代替在绑定到同一源的所有元素上指定数据源,我们可以将源对象分配给某个父对象的数据上下文.
<Window.Resources>
<local:Employee
x:Key="MyEmployee" EmployeeNumber="123" FirstName="John"
LastName="Doe" Department="Product Development" Title="QA Manager"
/>
</Window.Resources>
<Grid DataContext="{StaticResource MyEmployee}">
<TextBox Text="{Binding Path=EmployeeNumber}"></TextBox>
<TextBox Text="{Binding Path=FirstName}"></TextBox>
<TextBox Text="{Binding Path=LastName}" />
<TextBox Text="{Binding Path=Title}"></TextBox>
<TextBox Text="{Binding Path=Department}" />
</Grid>
绑定到收藏集
依赖项属性支持单值绑定.为了绑定到对象集合,您的目标对象必须派生自" ItemsControl". ItemsControl类中的三个属性对于数据绑定很重要.
您可以绑定到任何实现" IEnumerable"接口的集合. .NET Collection类和Array类实现此接口.使用" IEnumerable",您将能够执行只读绑定.为了能够编辑数据,必须满足其他要求.
对于此示例,代码绑定到Employee
对象的集合. “列表"控件(继承自” ItemsControl")显示员工姓名的列表.选择其中一名员工将使他们的信息显示在列表旁边的详细信息区域中.为了处理详细信息的显示,我们将利用上一节中提到的DataContext对象.详细信息区域中的所有字段都将绑定到同一对象(但属性不同).所有详细信息字段都在同一网格控件中.网格控件上的数据上下文绑定到员工列表中的选定项目.
<TextBox Text="{Binding Path=FirstName}" />
<TextBox Text="{Binding Path=LastName}" />
<TextBox Text="{Binding Path=Title}" />
<TextBox Text="{Binding Path=Department}" />
更改列表中的所选项目后,数据上下文将自动更新.当DataContext
被更新时,绑定到DataContext
的细节区域中的字段也被更新.
如果绑定后以编程方式将元素插入集合或从集合中移除,则界面不会更新.为了使列表自动响应插入和删除,该接口需要绑定到实现" INotifyCollectionChanged"接口的列表. WPF的ObservableCollection类实现了INotifyCollectionChanged.对于我们的示例,将数据集合从" List" 更改为" ObservableCollection` 是确保在将员工插入列表或从列表中删除员工时接口更新所需的唯一更改.示例5使用了" ObservableCollection"而不是通用的" List",并且具有用于输入新员工信息的表单.当新员工被添加到列表中时,“列表"框将自动更新以显示新员工.
绑定到数据表
绑定到表类似于绑定到列表.在WPF中,您不能直接绑定到” DataTable",而必须绑定到" DataView"(这没什么大不了的,因为所有表在" DefaultView"属性中都有一个视图). Windows窗体中也存在此限制,但对开发人员是不可见的.由于DataTable实现了IBindingList接口,因此WPF能够检测何时添加新行.删除项目时,请确保不要通过从数据集中删除一行来实现删除功能.而是通过调用Row.Delete()
将一行标记为已删除(这将导致该行不被视图返回).
价值转换
有时,您需要对绑定的数据进行更改,以使其更适合在屏幕上显示.例如,如果您的源数据具有包含图像路径的属性,则可能要显示图像而不是图像路径.值转换器负责执行此任务.
要创建一个值转换器,请声明一个继承自IValueConverter的类.该类将需要应用" ValueConversion"属性.该属性带有两个参数,即源数据的类型和将其转换为的类型.实现Convert
和Convert
Back方法来执行转换.对于图像转换器,我们将不需要将图像转换回其加载路径,因此除了实现Convert
Back之外,我们还可以抛出NotImplemented
异常. WPF将抑制异常(稍后将详细介绍WPF异常抑制).
IValueConverter`s可以以一些颇具创造性的方式使用.假设您要显示销售数字列表,并且想要通过更改文本的颜色来突出显示高于或低于特定水平的数字.此处可以使用值转换器将值转换为适当的颜色.对于以下代码示例,值转换器用于转换华氏温度并更改温度文本的颜色以指示温度是冷还是热.摄氏温度和华氏温度均使用相同的转换器.为了使转换器适用于两个数值范围,转换器都具有允许设置冷热限制的属性.
<Window.Resources>
<local:FarenheitToCelciusConverter x:Key="myTemperatureConverter" />
<local:TemperatureToColorConverter x:Key="myFRangeIndicator"
HotTemperature="80" ColdTemperature="65" />
<local:TemperatureToColorConverter x:Key="myCRangeIndicator"
HotTemperature="26.6" ColdTemperature="18.8" />
</Window.Resources>
<TextBox Name="txtFarenheit"
Foreground="{Binding Path=Text, ElementName=txtFarenheit,
Converter={StaticResource myFRangeIndicator}}"
/>
<TextBox Name="txtCelcius"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Path=Text,ElementName=txtFarenheit,
Converter={StaticResource myTemperatureConverter}}"
Foreground="{Binding Path=Text, ElementName=txtCelcius,
Converter={StaticResource myCRangeIndicator}}"
/>
[ValueConversion(typeof(double), typeof(double))]
public class FarenheitToCelciusConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
string sourceValue = value.ToString();
double decimalValue = 0;
if (Double.TryParse(sourceValue, out decimalValue))
{
return (decimalValue - 32.0) * (5.0 / 9.0);
}
return value;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
string sourceValue = value.ToString();
double decimalValue = 0;
if (Double.TryParse(sourceValue, out decimalValue))
{
return (decimalValue * (9.0 / 5.0)) + 32.0;
}
return value;
}
#endregion
}
验证方式
为了防止用户输入无效数据,您的类可能包含防止设置无效值的逻辑.但是,如果在设置无效值时引发异常,则用户将不会收到解释该值无效的反馈. WPF将自动抑制绑定期间发生的异常.为了向用户提供有关验证问题的反馈,WPF具有名为"验证规则"的功能.内置验证规则之一是" ExceptionValidationRule".下面显示了应用此规则的标记.应用时,将通知用户绑定期间发生的异常.
<Binding Path="EventDate" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
您可以从" ValidationRule"类继承并覆盖" Validate"方法来创建自己的验证规则.如果值有效,则应返回" ValidationResult"对象,并将其" IsValid"属性设置为" true".否则,该值应设置为" false",并且其" ErrorContent"属性应包含失败的详细信息.验证发生在转换之前,因此您必须针对未转换的值编写验证逻辑. 如果要获取在绑定过程中遇到验证错误的所有元素的列表,请遍历表单层次结构,测试每个元素的" HasError"属性.如果元素有错误,则可以通过调用其GetErrors()方法来获取该元素遇到的错误的集合.
在下一节中…
对于这个由两部分组成的系列文章,有关数据绑定的下一部分,我将介绍验证和规则的更多细节,并将介绍数据模板(用于自定义" ItemControl"中信息的显示方式),数据源(允许数据访问). (可从其他来源获取)和数据视图(用于数据的排序,过滤和分组).在继续进行本文的第二部分之前,我建议您对WPF数据绑定进行更多的练习.
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# XML Windows .NET Dev ADO.NET WPF Beginner Intermediate 新闻 翻译