数据绑定概述

Windows Presentation Foundation 中的数据绑定(WPF)为应用提供一种简单且一致的方法来呈现和交互数据。 元素可以绑定到各种数据源中的数据,格式为 .NET 对象和 XML。 任何 ContentControl 此类 Button 项和任意 ItemsControl项(如 ListBoxListView)都有内置功能,可灵活设置单个数据项或数据项集合的样式。 可以在数据顶部生成排序、筛选和分组视图。

WPF 中的数据绑定与传统模型相比有许多优势,包括广泛属性对数据绑定的固有支持、灵活的数据用户界面表现形式,以及业务逻辑与用户界面的清晰分离。

本文首先讨论 WPF 数据绑定的基本概念,然后介绍类的用法 Binding 和数据绑定的其他功能。

什么是数据绑定?

数据绑定是在应用 UI 与它显示的数据之间建立连接的过程。 如果绑定具有正确的设置,并且数据提供正确的通知,当数据更改其值时,绑定到数据的元素会自动反映更改。 数据绑定还意味着,如果元素中的数据的外部表示形式发生更改,则可以自动更新基础数据以反映更改。 例如,如果用户编辑元素中的 TextBox 值,基础数据值将自动更新以反映该更改。

数据绑定的典型用途是将服务器或本地配置数据放入窗体或其他 UI 控件。 在 WPF 中,此概念已扩展为包括将各种属性绑定到不同类型的数据源。 在 WPF 中,元素的依赖属性可以绑定到 .NET 对象(包括 ADO.NET 对象或与 Web 服务和 Web 属性关联的对象)和 XML 数据。

基本数据绑定概念

无论要绑定什么元素和数据源的性质,每个绑定始终遵循下图所示的模型。

显示基本数据绑定模型的关系图。

如图所示,数据绑定本质上是绑定目标与绑定源之间的桥梁。 下图演示了以下基本 WPF 数据绑定概念:

  • 通常,每个绑定都有四个组件:

    • 绑定目标对象。
    • 目标属性。
    • 绑定源。
    • 要使用的绑定源中值的路径。

    例如,如果您将TextBox的内容绑定到Employee.Name属性,则可以按照下表设置绑定:

    设置 价值
    目标 TextBox
    目标属性 Text
    源对象 Employee
    源对象值路径 Name
  • 目标属性必须是依赖属性。

    大多数 UIElement 属性都是依赖属性,大多数依赖属性(只读属性除外)默认支持数据绑定。 只有派生自 DependencyObject 的类型可以定义依赖属性。 所有 UIElement 类型派生自 DependencyObject.

  • 绑定源不限于自定义 .NET 对象。

    虽然图中未显示,但应指出绑定源对象不限于自定义 .NET 对象。 WPF 数据绑定支持 .NET 对象、XML 甚至 XAML 元素对象形式的数据。 为了提供一些示例,绑定源可以是 UIElement、任何列表对象、ADO.NET 或 Web Services 对象或包含 XML 数据的 XmlNode。 有关详细信息,请参阅 绑定源概述

请务必记住,在建立绑定时,需要将绑定目标 绑定到 绑定源。 例如,如果你在 ListBox 中使用数据绑定显示一些基础的 XML 数据,那么就是将 ListBox 绑定到 XML 数据。

若要建立绑定,请使用该 Binding 对象。 本文的其余部分讨论了与对象相关的许多概念以及某些属性和用法 Binding

数据上下文

在 XAML 元素上声明数据绑定时,它们通过查看其即时 DataContext 属性解析数据绑定。 数据上下文通常是绑定源对象用于评估绑定源值路径的。 可以在绑定中重写此行为,并设置特定的 绑定源对象 值。 如果未设置DataContext属性,则会检查承载绑定的对象的父元素的DataContext属性,依次向上检查,直到 XAML 对象树的根节点为止。 简而言之,用于解析绑定的数据上下文继承自父级,除非对对象显式设置。

可以将绑定配置为使用特定对象进行解析,而不是使用数据上下文进行绑定解析。 直接指定源对象用于在例如将一个对象的前景色绑定到另一个对象的背景色时使用。 数据的上下文不是必需的,因为绑定会在这两个对象之间自动解析。 相反,未绑定到特定源对象的绑定使用数据上下文解析。

DataContext当属性发生更改时,将重新评估可能受数据上下文影响的所有绑定。

数据流的方向

如上图中的箭头所示,绑定的数据流可以从绑定目标流向绑定源(例如,当用户编辑 TextBox 的值时,源值会发生更改)和/或从绑定源流向绑定目标(例如,当绑定源中的更改发生时,TextBox 的内容也会相应更新),前提是绑定源提供正确的通知。

你可能希望应用允许用户更改数据并将其传播回源对象。 或者你可能不希望用户更新源数据。 可以通过设置 . Binding.Mode. 来控制数据流。

下图说明了不同类型的数据流:

数据绑定数据流

  • OneWay 绑定会导致对源属性的更改自动更新目标属性,但目标属性的更改不会传播回源属性。 如果绑定的控件是隐式只读的,则此类型的绑定是合适的。 例如,可以绑定到源,如股票行情显示器,或者目标属性未提供控制接口以进行更改,例如表的数据绑定背景色。 如果不需要监视目标属性的变化,使用 OneWay 绑定模式可以避免 TwoWay 绑定模式的开销。

  • TwoWay 绑定会导致对源属性或目标属性的更改自动更新另一个属性。 这种类型的绑定适用于可编辑的表单或其他完全交互式 UI 方案。 大多数属性默认为 OneWay 绑定,但某些依赖属性(通常是用户可编辑控件的属性,如 TextBox.TextCheckBox.IsChecked 默认为 TwoWay 绑定)。

    默认情况下,确定依赖属性绑定单向还是双向的编程方式是使用 DependencyProperty.GetMetadata 获取属性元数据。 此方法的返回类型, PropertyMetadata不包含有关绑定的任何元数据。 但是,如果该类型可以转换为 FrameworkPropertyMetadata 的派生类型,则可以检查 FrameworkPropertyMetadata.BindsTwoWayByDefault 属性的布尔值。 下面的代码示例演示如何获取属性的 TextBox.Text 元数据:

    public static void PrintMetadata()
    {
        // Get the metadata for the property
        PropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));
    
        // Check if metadata type is FrameworkPropertyMetadata
        if (metadata is FrameworkPropertyMetadata frameworkMetadata)
        {
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:");
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}");
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}");
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}");
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}");
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}");
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}");
        }
    
        /*  Displays:
         *  
         *  TextBox.Text property metadata:
         *    BindsTwoWayByDefault: True
         *    IsDataBindingAllowed: True
         *          AffectsArrange: False
         *          AffectsMeasure: False
         *           AffectsRender: False
         *                Inherits: False
        */
    }
    
    Public Shared Sub PrintMetadata()
    
        Dim metadata As PropertyMetadata = TextBox.TextProperty.GetMetadata(GetType(TextBox))
        Dim frameworkMetadata As FrameworkPropertyMetadata = TryCast(metadata, FrameworkPropertyMetadata)
    
        If frameworkMetadata IsNot Nothing Then
    
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:")
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}")
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}")
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}")
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}")
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}")
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}")
    
            '  Displays:
            '
            '  TextBox.Text property metadata:
            '    BindsTwoWayByDefault: True
            '    IsDataBindingAllowed: True
            '          AffectsArrange: False
            '          AffectsMeasure: False
            '           AffectsRender: False
            '                Inherits: False
        End If
    
    
    End Sub
    
  • OneWayToSource 是绑定的 OneWay 反向;当目标属性发生更改时,它会更新源属性。 一个示例方案是,只需重新评估 UI 中的源值。

  • 图中未说明绑定 OneTime ,这会导致源属性初始化目标属性,但不会传播后续更改。 如果数据上下文更改或数据上下文中的对象发生更改,则更改 不会 反映在目标属性中。 如果当前状态的快照合适,或者数据确实是静态的,则此类型的绑定是合适的。 如果要使用源属性中的某些值初始化目标属性,并且数据上下文并不提前知道,则这种类型的绑定也很有用。 此模式实质上是一种更简单的 OneWay 绑定形式,在源值不会更改的情况下提供更好的性能。

若要检测源更改(适用于 OneWayTwoWay 绑定),源必须实现适当的属性更改通知机制,例如 INotifyPropertyChanged。 有关实现属性更改通知的示例,请参阅“如何:实现属性更改通知”(.NET Framework)。

Binding.Mode 属性提供有关绑定模式的详细信息,以及有关如何指定绑定方向的示例。

是什么触发源更新

TwoWayOneWayToSource位置的绑定会侦听目标属性的更改,并将其传播回源,这个过程称为更新源。 例如,可以编辑 TextBox 的文本以更改基础源值。

那么,在编辑文本期间还是在完成编辑文本后,控件失去焦点时,源值是否更新? 该 Binding.UpdateSourceTrigger 属性确定源更新的触发条件。 下图中向右箭头的点演示了Binding.UpdateSourceTrigger属性的作用。

显示 UpdateSourceTrigger 属性功能的关系图。

UpdateSourceTrigger的值为UpdateSourceTrigger.PropertyChanged时,TwoWay的右箭头指向的值或OneWayToSource绑定指向的值将在目标属性发生变化时立即更新。 但是,如果 UpdateSourceTrigger 值为 LostFocus,则仅当目标属性失去焦点时,才会使用新值更新该值。

与属性 Mode 类似,不同的依赖属性具有不同的默认值 UpdateSourceTrigger 。 大多数依赖属性的默认值是 PropertyChanged,这会导致源属性的值在目标属性值发生更改时立即更改。 即时更改适用于 CheckBox 和其他简单控件。 但是,对于文本字段,在每次击键后更新文本字段的值可能会降低性能,并使用户无法在提交新值之前有机会退格和修复输入错误。 例如,该 TextBox.Text 属性默认为 UpdateSourceTrigger 的值,即 LostFocus,这会导致源值仅在控件元素失去焦点时更改,而不是当 TextBox.Text 属性更改时。 UpdateSourceTrigger有关如何查找依赖属性的默认值的信息,请参阅属性页。

下表提供了一个使用 TextBox 作为示例的每个 UpdateSourceTrigger 值的示例方案。

UpdateSourceTrigger 值 更新源值时 TextBox 的示例方案
LostFocus (默认值 TextBox.Text 当 TextBox 控件失去焦点时。 与验证逻辑关联的 TextBox(请参阅下面的 数据验证 )。
PropertyChanged 当您在TextBox中键入时。 聊天室窗口中的 TextBox 控件。
Explicit 应用调用 UpdateSource时。 TextBox 控件采用可编辑的形式(仅当用户按下提交按钮时更新源值)。

有关示例,请参阅 如何:控制 TextBox 文本更新源的时间(.NET Framework)

数据绑定示例

有关数据绑定的示例,请查看 数据绑定演示中的以下应用 UI,其中显示了拍卖项目列表。

数据绑定示例屏幕截图

该应用演示了数据绑定的以下功能:

  • ListBox 的内容绑定到 AuctionItem 对象的集合。 AuctionItem 对象具有 DescriptionStartPriceStartDateCategorySpecialFeatures 等属性。

  • ListBox中显示的数据模板(AuctionItem 对象)用于显示每个物品的说明和当前价格。 模板是使用DataTemplate创建的。 此外,每个项目的外观取决于 AuctionItem 显示的 SpecialFeatures 值。 如果 AuctionItemSpecialFeatures 值为 Color,则该项具有蓝色边框。 如果值为 “突出显示”,则该项具有橙色边框和星形。 “数据模板化”部分提供有关数据模板化的信息。

  • 用户可以使用所提供的数据对数据 CheckBoxes 进行分组、筛选或排序。 在上图中,选择了“ 按类别 分组”和“ 按类别排序”和“日期CheckBoxes ”。 你可能已注意到,数据根据产品的类别分组,类别名称按字母顺序排列。 很难从图像中注意到,但项目也按每个类别中的开始日期进行排序。 排序是使用 集合视图完成的。 “绑定到集合”部分讨论集合视图。

  • 当用户选择某个项时,将显示 ContentControl 所选项的详细信息。 此体验称为 Master-detail 情景主从关系场景部分提供有关这种绑定方式的信息。

  • StartDate属性的类型是DateTime,它返回的日期包含到毫秒的时间。 在此应用中,已使用自定义转换器,以便显示较短的日期字符串。 “数据转换”部分提供有关转换器的信息。

当用户选择 “添加产品 ”按钮时,会出现以下窗体。

“添加产品列表”页

用户可以编辑表单中的字段,使用简短或详细的预览窗格预览产品列表,然后选择 Submit 添加新产品列表。 任何现有的分组、筛选和排序设置都将应用于新条目。 在这种情况下,在上图中输入的项目将显示为 计算机 类别中的第二项。

此图像中未显示“ 开始日期TextBox”中提供的验证逻辑。 如果用户输入无效的日期(格式无效或过去的日期),用户将收到一个 ToolTip 红色感叹号 TextBox旁边的通知。 “数据验证”部分讨论如何创建验证逻辑。

在了解上述数据绑定的不同功能之前,我们将首先讨论了解 WPF 数据绑定至关重要的基本概念。

创建绑定

若要重述前面部分讨论的一些概念,请使用对象建立绑定,每个绑定 Binding 通常具有四个组件:绑定目标、目标属性、绑定源和要使用的源值的路径。 本部分讨论如何设置绑定。

绑定源与 DataContext 元素的活跃源相关联。 元素如果未显式定义特定属性,则会自动继承它们的 DataContext

请考虑以下示例,其中绑定源对象是 SDKSample 命名空间中定义的名为 MyData 的类。 出于演示目的, MyData 具有名为 ColorName 的字符串属性,其值设置为“Red”。 因此,此示例生成一个带有红色背景的按钮。

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <DockPanel.DataContext>
        <Binding Source="{StaticResource myDataSource}"/>
    </DockPanel.DataContext>
    <Button Background="{Binding Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

有关如何在代码中设置绑定的绑定声明语法和示例的详细信息,请参阅 绑定声明概述

如果将此示例应用于基本关系图,则生成的图如下所示。 此图描述绑定 OneWay ,因为默认情况下 Background 属性支持 OneWay 绑定。

显示数据绑定 Background 属性的图示。

你可能想知道为什么这个绑定仍然有效,尽管 ColorName 属性是字符串类型,而 Background 属性是 Brush 类型。 此绑定使用默认类型转换,数据 转换 部分对此进行了讨论。

指定绑定源

请注意,在前面的示例中,绑定源是通过设置 DockPanel.DataContext 属性指定的。 Button从作为父元素的DockPanel继承DataContext值。 若要重申,绑定源对象是绑定的四个必需组件之一。 因此,如果没有指定绑定源对象,绑定将不执行任何操作。

可通过多种方式指定绑定源对象。 将多个属性绑定到同一源时,在 DataContext 父元素上使用该属性非常有用。 但是,有时在单个绑定声明上指定绑定源可能更合适。 对于上一个示例,可以直接在按钮的绑定声明中设置Binding.Source属性,而不是使用DataContext属性来指定绑定源,如以下示例所示。

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

除了直接在元素上设置 DataContext 属性外,从上级继承 DataContext 值(如第一个示例中的按钮),并通过在绑定上设置 Binding.Source 属性(如最后一个示例的按钮)显式指定绑定源,还可以使用该 Binding.ElementName 属性或 Binding.RelativeSource 属性来指定绑定源。 当你绑定到应用中的其他元素时(例如,使用滑块调整按钮的宽度时),该 ElementName 属性非常有用。 当绑定在ControlTemplateStyle中指定时,该RelativeSource属性非常有用。 有关详细信息,请参阅 绑定源概述

为值指定路径

如果绑定源是对象,则使用 Binding.Path 属性指定要用于绑定的值。 如果要绑定到 XML 数据,请使用 Binding.XPath 属性来指定值。 在某些情况下,即使数据为 XML,也可以使用该 Path 属性。 例如,如果要访问由于 XPath 查询结果而返回的 XmlNode 的 Name 属性,除了使用 XPath 属性外,还应使用 Path 属性。

有关详细信息,请参阅PathXPath属性。

尽管我们强调 Path 要使用的值是绑定的四个必需组件之一,但在要绑定到整个对象的方案中,要使用的值与绑定源对象相同。 在这些情况下,可以不指定Path。 请考虑以下示例。

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

上面的示例使用空绑定语法:{Binding}。 在这种情况下,ListBox 从其父 DockPanel 元素继承了 DataContext(在此示例中未显示)。 如果未指定路径,则默认值为绑定到整个对象。 换句话说,在此示例中,路径已被排除,因为我们正在将 ItemsSource 属性绑定到整个对象。 (有关深入讨论,请参阅 “绑定到集合 ”部分。

除了绑定到集合之外,当想要绑定到整个对象而不是仅绑定到对象的单个属性时,此方案也很有用。 例如,如果源对象的类型为类型 String,可能只想绑定到字符串本身。 另一种常见情况是,你想要将元素绑定到具有多个属性的对象。

可能需要应用自定义逻辑,以便数据对绑定的目标属性有意义。 如果默认类型转换不存在,则自定义逻辑可能采用自定义转换器的形式。 有关转换器的信息,请参阅 数据转换

Binding 和 BindingExpression

在了解数据绑定的其他功能和用法之前,介绍BindingExpression类是很有用的。 如前几节所述,该 Binding 类是绑定声明的高级类;它提供了许多属性,可用于指定绑定的特征。 相关类 BindingExpression是维护源与目标之间的连接的基础对象。 绑定包含可在多个绑定表达式之间共享的所有信息。 一个BindingExpression实例表达式,不能共享并包含Binding的所有实例信息。

请考虑以下示例,其中myDataObject类的 myBindingMyData实例是源Binding对象,并且MyData是包含名为 ColorName 的字符串属性的已定义类。 本示例将 myText 的文本内容绑定到 ColorName,其中 TextBlock 是一个实例。

// Make a new source
var myDataObject = new MyData();
var myBinding = new Binding("ColorName")
{
    Source = myDataObject
};

// Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding);
' Make a New source
Dim myDataObject As New MyData
Dim myBinding As New Binding("ColorName")
myBinding.Source = myDataObject

' Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding)

可以使用同一 myBinding 对象创建其他绑定。 例如,可以使用 myBinding 对象将复选框的文本内容绑定到 ColorName。 在这种情况下,将有两个 BindingExpression 共享 myBinding 对象的实例。

通过调用数据绑定对象的GetBindingExpression来返回BindingExpression对象。 以下文章演示了 BindingExpression 类的一些用法:

数据转换

“创建绑定 ”部分中,按钮为红色,因为它 Background 的属性绑定到值为“Red”的字符串属性。 此字符串值有效,因为类型转换器在类型上 Brush 存在用于将字符串值转换为 a Brush.

将此信息添加到 “创建绑定 ”部分中的图中,如下所示。

显示数据绑定默认属性的图。

但是,如果绑定源对象具有一种类型为 ColorColor 属性,而不是具有字符串类型的属性,该怎么办? 在这种情况下,若要使绑定正常工作,需要首先将 Color 属性值转换为属性接受的内容 Background 。 需要通过实现 IValueConverter 接口创建自定义转换器,如以下示例所示。

[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color As Color = CType(value, Color)
        Return New SolidColorBrush(color)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

有关详细信息,请参阅 IValueConverter

现在,使用自定义转换器而不是默认转换,我们的关系图如下所示。

显示自定义转换器数据绑定的关系图。

若要重申,默认转换可能可用,因为类型转换器存在于绑定到的类型中。 此行为将取决于目标中可用的类型转换器。 如有疑问,请创建自己的转换器。

下面是实现数据转换器有意义的一些典型方案:

  • 数据应以不同的方式显示,具体取决于文化。 例如,你可能希望根据特定文化中使用的约定实现货币转换器或日历日期/时间转换器。

  • 所使用的数据不一定用于更改属性的文本值,而是旨在更改其他一些值,例如图像的源或显示文本的颜色或样式。 转换器可以在此实例中使用,方法是将可能不适合的属性的绑定,例如将文本字段绑定到表单元格的 Background 属性。

  • 多个控件或多个控件属性绑定到相同的数据。 在这种情况下,主绑定可能只显示文本,而其他绑定处理特定显示问题,但仍使用与源信息相同的绑定。

  • 目标属性具有一个绑定集合,称为 MultiBinding。 对于 MultiBinding,使用自定义 IMultiValueConverter 从绑定的值生成最终值。 例如,颜色可以从红色、蓝色和绿色值计算,这些值可以是来自相同或不同的绑定源对象的值。 请参阅 MultiBinding 示例和信息。

绑定到集合

绑定源对象可以视为一个单个对象,其属性包含数据,或者视为一个多态对象的数据集合,这些对象通常组合在一起(例如数据库查询的结果)。 到目前为止,我们只讨论了绑定到单个对象。 但是,绑定到数据集是一个常见情况。 例如,常见方案是使用 ItemsControl 诸如 ListBoxListViewTreeView 显示数据收集,例如在 “什么是数据绑定 ”部分中显示的应用中。

幸运的是,我们的基本关系图仍然适用。 如果您将 ItemsControl 绑定到一个集合,示意图如下所示。

显示数据绑定的 ItemsControl 对象的图示。

如该图所示,若要将 ItemsControl 绑定到集合对象,ItemsControl.ItemsSource 属性是要使用的属性。 可以将 ItemsSource 看作是 ItemsControl 的内容。 绑定是OneWay,因为ItemsSource属性默认支持OneWay绑定。

如何实现集合

您可以枚举实现 IEnumerable 接口的任何集合。 但是,若要设置动态绑定,以便集合中的插入或删除会自动更新 UI,集合必须实现 INotifyCollectionChanged 接口。 此接口用于公开一个事件,该事件在基础集合发生变更时应被触发。

WPF 提供类 ObservableCollection<T> ,该类是公开 INotifyCollectionChanged 接口的数据收集的内置实现。 为了完全支持将数据值从源对象传输到目标,支持可绑定属性的集合中的每个对象也必须实现 INotifyPropertyChanged 接口。 有关详细信息,请参阅 绑定源概述

在实现自己的集合之前,请考虑使用ObservableCollection<T>或现有集合类之一,例如List<T>Collection<T>,以及BindingList<T>许多其他集合类。 如果你有一个高级方案并想要实现自己的集合,请考虑使用 IList,它提供一个非泛型对象集合,这些对象可由索引单独访问,从而提供最佳性能。

集合视图

ItemsControl绑定到数据收集后,可能需要对数据进行排序、筛选或分组。 为此,请使用集合视图,这些视图是实现接口的 ICollectionView 类。

什么是集合视图?

集合视图是绑定源集合顶部的一层,可用于基于排序、筛选和分组查询导航和显示源集合,而无需更改基础源集合本身。 集合视图还维护指向集合中当前项的指针。 如果源集合实现 INotifyCollectionChanged 接口,则事件引发 CollectionChanged 的更改将传播到视图。

由于视图不会更改基础源集合,因此每个源集合可以有多个与之关联的视图。 例如,你可能具有 Task 对象的集合。 使用视图时,可以通过不同的方式显示相同的数据。 例如,在页面左侧,你可能希望显示按优先级排序的任务,并在右侧按区域分组。

如何创建视图

创建和使用视图的一种方法是直接实例化视图对象,然后将其用作绑定源。 例如,请考虑数据绑定演示应用程序,该应用程序的示例展示在“什么是数据绑定”部分。 应用程序的实现方式使得ListBox绑定到数据集合上的视图,而不是直接绑定到数据集合。 以下示例从 数据绑定演示 应用中提取。 该 CollectionViewSource 类是继承自 CollectionView. 的类的 XAML 代理。 在此特定示例中, Source 视图绑定到当前应用对象的 AuctionItems 集合(类型 ObservableCollection<T>)。

<Window.Resources>
    <CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

然后,资源 listingDataView 作为应用程序中元素的绑定源,例如 ListBox

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

若要为同一集合创建另一个视图,可以创建另一个 CollectionViewSource 实例并为其指定其他 x:Key 名称。

下表显示了根据源集合类型创建的默认集合视图或通过CollectionViewSource创建的视图数据类型。

源集合类型 集合视图类型 注释
IEnumerable 基于内部类型 CollectionView 无法对项进行分组。
IList ListCollectionView 最快。
IBindingList BindingListCollectionView

使用默认视图

将集合视图指定为绑定源是创建和使用集合视图的一种方法。 WPF 还会为每个用作绑定源的集合创建默认集合视图。 如果直接绑定到集合,WPF 会绑定到其默认视图。 此默认视图由同一集合的所有绑定共享,因此对默认视图所做的更改由一个绑定控件或代码(如排序或更改当前项指针(稍后讨论)反映在对同一集合的所有其他绑定中。

若要获取默认视图,请使用 GetDefaultView 该方法。 有关示例,请参阅获取数据收集的默认视图(.NET Framework)。

使用 ADO.NET 数据表的集合视图

为了提高性能,针对 ADO.NET DataTableDataView 对象的集合视图将排序和筛选委托给 DataView,这会导致对数据源的所有集合视图共享排序和筛选。 若要使每个集合视图能够独立排序和筛选,请使用其自己的 DataView 对象初始化每个集合视图。

排序

如前所述,视图可以将排序顺序应用于集合。 由于它存在于基础集合中,因此你的数据可能会或可能不会具有相关的固有顺序。 通过集合视图,可以根据提供的比较条件来施加订单或更改默认顺序。 由于它是基于客户端的数据视图,一种常见方案是,用户可能希望根据列所对应的值对表格数据列进行排序。 使用视图,可以再次应用此用户驱动排序,而无需对基础集合进行任何更改,甚至无需重新查询集合内容。 有关示例,请参阅单击标题时对 GridView 列进行排序(.NET Framework)。

以下示例显示了“数据绑定”部分中应用 UI 的“按类别和日期排序”CheckBox的排序逻辑。

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{
    // Sort the items first by Category and then by StartDate
    listingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}
Private Sub AddSortCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    ' Sort the items first by Category And then by StartDate
    listingDataView.SortDescriptions.Add(New SortDescription("Category", ListSortDirection.Ascending))
    listingDataView.SortDescriptions.Add(New SortDescription("StartDate", ListSortDirection.Ascending))
End Sub

筛选

视图还可以将筛选器应用于集合,以便视图仅显示完整集合的某些子集。 可以筛选数据中的条件。 例如,正如应用在 “什么是数据绑定 ”部分中所做的那样,“仅显示讨价还价” CheckBox 包含筛选出成本为 25 美元或以上的项目的逻辑。 执行以下代码,以便在选中CheckBox时将ShowOnlyBargainsFilter设置为Filter事件处理程序。

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{
    if (((CheckBox)sender).IsChecked == true)
        listingDataView.Filter += ListingDataView_Filter;
    else
        listingDataView.Filter -= ListingDataView_Filter;
}
Private Sub AddFilteringCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    Dim checkBox = DirectCast(sender, CheckBox)

    If checkBox.IsChecked = True Then
        AddHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    Else
        RemoveHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    End If
End Sub

ShowOnlyBargainsFilter 事件处理程序具有以下实现。

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{
    // Start with everything excluded
    e.Accepted = false;

    // Only inlcude items with a price less than 25
    if (e.Item is AuctionItem product && product.CurrentPrice < 25)
        e.Accepted = true;
}
Private Sub ListingDataView_Filter(sender As Object, e As FilterEventArgs)

    ' Start with everything excluded
    e.Accepted = False

    Dim product As AuctionItem = TryCast(e.Item, AuctionItem)

    If product IsNot Nothing Then

        ' Only include products with prices lower than 25
        If product.CurrentPrice < 25 Then e.Accepted = True

    End If

End Sub

如果直接使用CollectionView类而不是CollectionViewSource类,则使用Filter属性来指定回调。 有关示例,请参阅在视图中筛选数据 (.NET Framework)

分组

除查看 IEnumerable 集合的内部类外,所有集合视图都支持 分组,这允许用户将集合视图中的集合分区为逻辑组。 组可以是显式的,即用户提供组的列表,或者是隐式的,即根据数据动态生成组。

以下示例显示了“按类别分组” CheckBox的逻辑。

// This groups the items in the view by the property "Category"
var groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
' This groups the items in the view by the property "Category"
Dim groupDescription = New PropertyGroupDescription()
groupDescription.PropertyName = "Category"
listingDataView.GroupDescriptions.Add(groupDescription)

有关另一个分组示例,请参阅 ListView 中实现 GridView(.NET Framework)的组项

当前项目指针

视图还支持“当前项目”的概念。 可以在集合视图中导航对象。 导航时,将移动一个项指针,以便检索集合中该特定位置存在的对象。 有关示例,请参阅 在数据 CollectionView (.NET Framework)中浏览对象

由于 WPF 仅通过使用视图(您指定的视图或集合的默认视图)绑定到集合,因此对集合的所有绑定都具有当前项指针。 绑定到视图时,值中的 Path 斜杠(“/”)字符指定视图的当前项。 在以下示例中,数据上下文是集合视图。 第一行绑定到集合。 第二行绑定到集合中的当前项。 第三行绑定到 Description 集合中当前项的属性。

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />

还可以堆叠斜杠和属性语法,以遍历集合层次结构。 以下示例绑定到名为Offices的集合中的当前项,而该集合是源集合中当前项的一个属性。

<Button Content="{Binding /Offices/}" />

当前项指针可能会受到应用于集合的任何排序或筛选的影响。 排序将保留选定最后一项上的当前项指针,但集合视图现在围绕它进行了重构。 (也许所选项位于列表的开头,但现在所选项可能位于中间的某个位置。如果所选内容在筛选后保留在视图中,则筛选将保留所选项。 否则,当前项指针将设置为筛选集合视图的第一项。

主详细信息绑定方案

当前项的概念不仅可以用于在集合中导航项,还可以用于主从详情绑定场景。 再次考虑 “什么是数据绑定 ”部分中的应用 UI。 在该应用中,ListBox 内的选择决定在 ContentControl 中显示的内容。 换句话说,当选择某个 ListBox 项时,ContentControl 会显示所选项的详细信息。

只需将两个或多个控件绑定到同一视图即可实现主从关系模型。 以下示例来自数据绑定演示,展示了“什么是数据绑定”部分中应用程序界面上的ListBox标记和ContentControl显示。

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}" 
                Margin="9,0,0,0"/>

请注意,这两个控件都绑定到同一源 listDataView 静态资源(请参阅 “如何创建视图”部分中的此资源的定义)。 此绑定的工作原理是,当对象( ContentControl 在本例中)绑定到集合视图时,它会自动绑定到 CurrentItem 视图。 对象 CollectionViewSource 会自动同步货币和选择。 如果列表控件未绑定到 CollectionViewSource 此示例中的对象,则需要将其 IsSynchronizedWithCurrentItem 属性设置为 true 使此作正常工作。

有关其他示例,请参阅 根据选择(.NET Framework)绑定到集合并显示信息 ,以及 将主详细信息模式与分层数据(.NET Framework)配合使用

你可能已注意到上述示例使用模板。 事实上,如果不使用模板(显式使用 ContentControl 模板和隐式使用的 ListBox模板),数据就不会按我们希望的方式显示。 现在,我们将在下一部分转向数据模板化。

数据模板化

如果不使用数据模板,我们应用的 UI 在数据绑定示例部分中将如下所示:

没有数据模板的数据绑定演示

如上一节中的示例所示, ListBox 控件和 ContentControl 控件都绑定到 AuctionItems 的整个集合对象(或更具体地说是集合对象的视图)。 如果没有有关如何显示数据收集的特定说明,则 ListBox 显示基础集合中每个对象的字符串表示形式,以及 ContentControl 它绑定到的对象的字符串表示形式。

为了解决此问题,应用定义了 DataTemplates。 如上一部分中的示例所示, ContentControl 显式使用 detailsProductListingTemplate 数据模板。 控件 ListBox 在集合中显示 AuctionItem 对象时隐式使用以下数据模板。

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="86"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
                       Text="{Binding Path=Description}"
                       Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType"
                           Text="{Binding Path=CurrentPrice}" 
                           Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
            <DataTrigger.Setters>
                <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
                <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
                <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
                <Setter Property="Padding" Value="5" TargetName="border" />
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

使用这两个 DataTemplate 时,生成的 UI 是“ 什么是数据绑定 ”部分中所示的 UI。 正如你从该屏幕截图中看到的,除了允许将数据放在控件中之外,DataTemplates 还允许你为数据定义引人注目的视觉对象。 例如,在上述DataTemplate中使用DataTrigger,这样具有SpecialFeatures值为HighLightAuctionItem就会以橙色边框和星形显示。

有关数据模板的详细信息,请参阅数据模板概述(.NET Framework)。

数据验证

大多数采用用户输入的应用都需要有验证逻辑,以确保用户已输入预期信息。 验证检查可以基于类型、范围、格式或其他特定于应用的要求。 本部分讨论数据验证在 WPF 中的工作原理。

将验证规则与绑定关联

WPF 数据绑定模型允许你将ValidationRulesBinding对象关联。 例如,以下示例将一个 TextBox 绑定到一 StartPrice 个名为的属性,并将一个 ExceptionValidationRule 对象添加到该 Binding.ValidationRules 属性。

<TextBox Name="StartPriceEntryForm" Grid.Row="2"
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

对象 ValidationRule 检查属性的值是否有效。 WPF 有两种类型的内置 ValidationRule 对象:

还可以通过派生自 ValidationRule 类并实现 Validate 方法来创建自己的验证规则。 以下示例显示了来自“什么是数据绑定”部分的“添加产品列表”的“开始日期”使用的规则。

public class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Test if date is valid
        if (DateTime.TryParse(value.ToString(), out DateTime date))
        {
            // Date is not in the future, fail
            if (DateTime.Now > date)
                return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            // Date is not a valid date, fail
            return new ValidationResult(false, "Value is not a valid date.");
        }

        // Date is valid and in the future, pass
        return ValidationResult.ValidResult;
    }
}
Public Class FutureDateRule
    Inherits ValidationRule

    Public Overrides Function Validate(value As Object, cultureInfo As CultureInfo) As ValidationResult

        Dim inputDate As Date

        ' Test if date is valid
        If Date.TryParse(value.ToString, inputDate) Then

            ' Date is not in the future, fail
            If Date.Now > inputDate Then
                Return New ValidationResult(False, "Please enter a date in the future.")
            End If

        Else
            ' // Date Is Not a valid date, fail
            Return New ValidationResult(False, "Value is not a valid date.")
        End If

        ' Date is valid and in the future, pass
        Return ValidationResult.ValidResult

    End Function

End Class

StartDateEntryFormTextBox 使用此 FutureDateRule,如以下示例所示。

<TextBox Name="StartDateEntryForm" Grid.Row="3"
         Validation.ErrorTemplate="{StaticResource validationTemplate}" 
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
                 Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

由于UpdateSourceTrigger的值是PropertyChanged,绑定引擎会在每次击键时更新源值,这意味着它也会在每次击键时检查ValidationRules集合中的每条规则。 我们将在“验证过程”部分中进一步讨论这一点。

提供视觉反馈

如果用户输入了无效值,则可能需要提供有关应用 UI 上错误的一些反馈。 提供此类反馈的一种方法是将 Validation.ErrorTemplate 附加属性设置为自定义 ControlTemplate。 如上一个子节所示, StartDateEntryFormTextBox 使用 ErrorTemplate 名为 validationTemplate。 下面的示例演示 validationTemplate 的定义。

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

AdornedElementPlaceholder 元素指定应放置被修饰控件的位置。

此外,还可以使用 a ToolTip 来显示错误消息。 StartDateEntryFormStartPriceEntryFormTextBoxes 都使用样式 textStyleTextBox,这会创建一个ToolTip显示错误消息的样式。 以下示例显示了 textStyleTextBox 的定义。 附加属性 Validation.HasErrortrue 当绑定元素的属性上的一个或多个绑定出错时。

<Style x:Key="textStyleTextBox" TargetType="TextBox">
    <Setter Property="Foreground" Value="#333333" />
    <Setter Property="MaxLength" Value="40" />
    <Setter Property="Width" Value="392" />
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

使用自定义ErrorTemplateToolTip时,当StartDateEntryFormTextBox出现验证错误时,其效果如下所示。

日期的数据绑定验证错误

Binding如果具有关联的验证规则,但未在绑定控件上指定ErrorTemplate,则默认值ErrorTemplate将用于在出现验证错误时通知用户。 默认值 ErrorTemplate 是一个控件模板,用于定义装饰器层中的红色边框。 使用默认的ErrorTemplateToolTipStartPriceEntryFormTextBox的UI在出现验证错误时如下所示。

价格的数据绑定验证错误

有关如何提供用于验证对话框中所有控件的逻辑的示例,请参阅 对话框概述中的“自定义对话框”部分。

验证过程

将目标值传输到绑定源属性时,通常会进行验证。 此传输发生在 TwoWayOneWayToSource 绑定上。 若要重申,导致源更新的原因取决于属性的值 UpdateSourceTrigger ,如 “触发源更新” 部分中所述。

以下项描述了 验证 过程。 如果验证错误或其他类型的错误在此过程期间随时发生,则进程将停止:

  1. 绑定引擎检查是否存在定义了自定义ValidationRule对象,并且这些对象的ValidationStep为该Binding设置为RawProposedValue,在这种情况下,它会对每个ValidationRule调用Validate方法,直到其中一个对象遇到错误或所有对象均成功为止。

  2. 然后,绑定引擎将调用转换器(如果存在)。

  3. 如果转换器成功,绑定引擎会检查是否定义了任何自定义对象,该对象的ValidationStep是否设置为ConvertedProposedValue用于Binding,在这种情况下,绑定引擎会对每个ValidationRule调用Validate方法,这些ValidationRule对象的ValidationStep设置为ConvertedProposedValue,直到其中一个遇到错误或全部通过为止。

  4. 绑定引擎设置源属性。

  5. 绑定引擎会检查是否存在定义为自定义ValidationRule对象且ValidationStep设置为UpdatedValueBinding的情况,在这种情况下,它将对每个ValidationRule对象调用Validate方法,前提是这些对象的ValidationStep设置为UpdatedValue,直到其中一个出现错误或所有对象通过为止。 如果与DataErrorValidationRule绑定关联且其ValidationStep设置为默认值,UpdatedValueDataErrorValidationRule则此时会检查该绑定。 此时,将检查任何设置为 trueValidatesOnDataErrors 绑定。

  6. 绑定引擎会检查是否存在定义了任何自定义ValidationRule对象,这些对象的ValidationStep是否被设置为CommittedValue,在这种情况下,它会对每个BindingValidationStep设置为CommittedValueValidationRule对象调用Validate方法,直到其中一个遇到错误或所有对象都顺利通过为止。

如果在任何阶段不能通过,绑定引擎将创建一个 ValidationError 对象并将其添加到绑定元素的 Validation.Errors 集合中。 在绑定引擎在任何给定步骤中运行ValidationRule对象之前,它会移除在该步骤期间添加到绑定元素Validation.Errors的附加属性中的任何ValidationError对象。 例如,如果ValidationRuleValidationStep设置为UpdatedValue并且操作失败,则下次验证过程发生时,绑定引擎会在调用任何ValidationRule之前立即删除该ValidationError,此时ValidationRuleValidationStep设置为UpdatedValue

如果 Validation.Errors 不是空,则 Validation.HasError 元素的附加属性设置为 true。 此外,如果将 BindingNotifyOnValidationError 属性设置为 true,绑定引擎将在元素上引发附加的 Validation.Error 事件。

请注意,在目标到源或源到目标的任一方向进行有效的值传输时,都会清除Validation.Errors附加属性。

如果绑定具有与 ExceptionValidationRule 关联,或者 ValidatesOnExceptions 属性被设置为 true 并在绑定引擎设置源时引发异常,绑定引擎将检查是否存在 UpdateSourceExceptionFilter。 可以使用 UpdateSourceExceptionFilter 回调提供用于处理异常的自定义处理程序。 如果Binding中未指定UpdateSourceExceptionFilter,绑定引擎将创建一个ValidationError以响应异常,并将其添加到绑定元素的Validation.Errors集合中。

调试机制

可以在与绑定相关的对象上设置附加属性 PresentationTraceSources.TraceLevel ,以接收有关特定绑定状态的信息。

另请参阅