【WPF.NET开发】WPF中的数据绑定

本文内容

  1. 什么是数据绑定
  2. 数据绑定基本概念
  3. 数据绑定的示例
  4. 创建绑定
  5. 数据转换
  6. 绑定到集合
  7. 数据模板化
  8. 数据验证
  9. 调试机制

Windows Presentation Foundation (WPF) 中的数据绑定为应用呈现数据并与数据交互提供了一种简单而一致的方法。 元素能够以 .NET 对象和 XML 的形式绑定到不同类型的数据源中的数据。 所有 ContentControl(例如 Button)以及所有 ItemsControl(例如 ListBox 和 ListView)都具有内置功能,使单个数据项或数据项集合可以灵活地进行样式设置。 可基于数据生成排序、筛选和分组视图。

WPF 中的数据绑定与传统模型相比具有几个优点,包括本质上支持数据绑定的大量属性、灵活的数据 UI 表示形式以及业务逻辑与 UI 的完全分离。

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

1、什么是数据绑定

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

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

2、数据绑定基本概念

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

basic-data-binding-diagram.png?view=netdesktop-8.0

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

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

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

2.1 数据上下文

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

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

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

2.2 数据流的方向

正如上图中的箭头所示,绑定的数据流可以从绑定目标流向绑定源(例如,当用户编辑 TextBox 的值时,源值会发生更改)和/或(在绑定源提供正确通知的情况下)从绑定源流向绑定目标(例如,TextBox 内容会随绑定源中的更改而进行更新)。

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

此图演示了不同类型的数据流:

databinding-dataflow.png?view=netdesktop-8.0

若要检测源更改(适用于 OneWay 和 TwoWay 绑定),则源必须实现合适的属性更改通知机制,例如 INotifyPropertyChanged。 请参阅如何:实现属性更改通知 (.NET Framework),获取 INotifyPropertyChanged 实现的示例。

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

2.3 触发源更新的因素

TwoWay 或 OneWayToSource 绑定侦听目标属性中的更改,并将更改传播回源(称为更新源)。 例如,可以编辑文本框的文本以更改基础源值。

但是,在编辑文本时或完成文本编辑后控件失去焦点时,源值是否会更新? Binding.UpdateSourceTrigger 属性确定触发源更新的因素。 下图中右箭头的点说明了 Binding.UpdateSourceTrigger 属性的角色。

data-binding-updatesource-trigger.png?view=netdesktop-8.0

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

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

下表以 TextBox 为例,提供每个 UpdateSourceTrigger 值的示例方案。

展开表

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

3、数据绑定的示例

有关数据绑定的示例,请参阅数据绑定演示(显示拍卖项的列表)中的以下应用 UI。

demo.png?view=netdesktop-8.0

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

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

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

3.1 Binding 和 BindingExpression

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

举例来说,假设 myDataObject 是 MyData 类的实例,myBinding 是源 Binding 对象,而 MyData 是包含名为 ColorName 的字符串属性的定义类。 此示例将 TextBlock 的实例 myText 的文本内容绑定到 ColorName

// 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);

可以使用同一 myBinding 对象来创建其他绑定。 例如,可使用 myBinding 对象将复选框的文本内容绑定到 ColorName。 在该方案中,将有两个 BindingExpression 实例共享 myBinding 对象。

通过对数据绑定对象调用 GetBindingExpression

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

demo-addproductlisting.png?view=netdesktop-8.0

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

“开始日期”TextBox 中提供的验证逻辑未在此图中显示。 如果用户输入一个无效日期(格式无效或日期已过),则会通过 ToolTip 和 TextBox 旁边显示的红色感叹号来通知用户。 数据验证一节讨论了如何创建验证逻辑。

在详细介绍数据绑定的上述不同功能之前,我们会先讨论对理解 WPF 数据绑定非常重要的基本概念。

4、创建绑定

前面部分中讨论的一些概念可以重申为:使用 Binding 对象建立绑定,且每个绑定通常具有四个组件:绑定目标、目标属性、绑定源以及指向要使用的源值的路径。 本节讨论如何设置绑定。

绑定源绑定到元素的活动 DataContext。 如果元素没有显式定义 DataContext,则会自动继承。

请考虑以下示例,其中的绑定源对象是一个名为 MyData 的类,该类在 SDKSample 命名空间中定义。 出于演示目的,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 绑定。

data-binding-button-background-example.png?view=netdesktop-8.0

你可能会想知道,此绑定为何在 ColorName 属性的类型为字符串而 Background 属性的类型为 Brush 的情况下也会起作用。 此绑定使用默认类型转换,这会在数据转换部分中进行讨论。

4.1 指定绑定源

请注意,在前面的示例中,通过设置 DockPanel.DataContext 属性指定绑定源。 然后,Button 从其父元素 DockPanel 继承 DataContext 值。 重申一下,绑定源对象是绑定的四个必需组件之一。 所以,如果未指定绑定源对象,则绑定将没有任何作用。

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

<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 属性非常有用。 在 ControlTemplate 或 Style 中指定绑定时,可以使用 RelativeSource 属性。 

4.2 指定指向值的路径

如果绑定源是一个对象,则使用 Binding.Path 属性指定要用于绑定的值。 如果要绑定到 XML 数据,则使用 Binding.XPath 属性指定值。 在某些情况下,使用 Path 属性(即使数据为 XML)可能更为合适。 例如,如果要访问返回的 XmlNode(作为 XPath 查询的结果)的 Name 属性,则除 XPath 属性外,还应使用 Path 属性。

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

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

以上示例使用空绑定语法:{Binding}。 在此示例中,ListBox 从父 DockPanel 元素继承 DataContext(此示例中未显示)。 未指定路径时,默认为绑定到整个对象。 换句话说,此示例中的路径已省略,因为要将 ItemsSource 属性绑定到整个对象。 (有关深入讨论,请参阅绑定到集合部分。)

  • 通常情况下,

  • 每个绑定具有四个组件:

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

    例如,如果将 TextBox 的内容绑定到 Employee.Name 属性,则可以类似如下所示设置绑定:

    展开表

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

    大多数 UIElement 属性都是依赖属性,而大多数依赖属性(只读属性除外)默认支持数据绑定。 只有从 DependencyObject 派生的类型才能定义依赖项属性。 所有 UIElement 类型从 DependencyObject 派生。

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

    尽管未在图中显示,但请注意,绑定源对象不限于自定义 .NET 对象。 WPF 数据绑定支持 .NET 对象、XML 甚至是 XAML 元素对象形式的数据。 例如,绑定源可以是 UIElement、任何列表对象、ADO.NET 或 Web 服务对象,或包含 XML 数据的 XmlNode。 有关详细信息,请参阅绑定源概述。

  • 通过 OneWay 绑定,对源属性的更改会自动更新目标属性,但对目标属性的更改不会传播回源属性。 如果绑定的控件为隐式只读,则此类型的绑定适用。 例如,可能会绑定到股票行情自动收录器这样的源,也可能目标属性没有用于进行更改的控件接口(例如表的数据绑定背景色)。 如果无需监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。

  • 通过 TwoWay 绑定,更改源属性或目标属性时会自动更新另一方。 此类型的绑定适用于可编辑窗体或其他完全交互式 UI 方案。 大多数属性默认为 OneWay 绑定,但某些依赖属性(通常为用户可编辑控件的属性,例如 TextBox.Text 和 CheckBox.IsChecked)默认为 TwoWay 绑定。

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

    public static void PrintMetadata()
    {// Get the metadata for the propertyPropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));// Check if metadata type is FrameworkPropertyMetadataif (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*/
    }
    
  • OneWayToSource 绑定与 OneWay 绑定相反;当目标属性更改时,它会更新源属性。 一个示例方案是只需要从 UI 重新计算源值的情况。

  • OneTime 绑定未在图中显示,该绑定会使源属性初始化目标属性,但不传播后续更改。 如果数据上下文发生更改,或者数据上下文中的对象发生更改,则更改不会在目标属性中反映。 如果适合使用当前状态的快照或数据实际为静态数据,则此类型的绑定适合。 如果你想使用源属性中的某个值来初始化目标属性,且提前不知道数据上下文,则此类型的绑定也有用。 此模式实质上是 OneWay 绑定的一种简化形式,它在源值不更改的情况下提供更好的性能。

  • ListBox 的内容已绑定到 AuctionItem 对象的集合。 AuctionItem 对象具有 Description、StartPrice、StartDate、Category 和 SpecialFeatures 等属性。

  • ListBox 中显示的数据(AuctionItem 对象)已进行模板化,以便显示每个项的说明和当前价格。 通过使用 DataTemplate 来创建模板。 此外,每个项的外观取决于要显示的 AuctionItem 的 SpecialFeatures 值。 如果 AuctionItem 的 SpecialFeatures 值为 Color,则该项具有蓝色边框。 如果值为 Highlight,则该项具有橙色边框和一个星号。 数据模板化部分提供了数据模板化的相关信息。

  • 用户可以使用提供的 CheckBoxes 对数据进行分组、筛选或排序。 在上图中,选中了“按类别分组”和“按类别和日期排序”CheckBoxes。 你可能已注意到,数据按产品类别分组,类别名称按字母顺序排序。 这些项还按每个类别中的开始日期排序,但难以从图中注意到这一点。 排序使用集合视图实现。 绑定到集合部分讨论了集合视图。

  • 当用户选择某个项时,ContentControl 显示所选项的详细信息。 此体验称为主-从方案。 主-从方案部分提供有关此绑定类型的信息。

  • StartDate 属性的类型为 DateTime,该类型返回一个包括精确到毫秒的时间的日期。 在此应用中,使用了一个自定义转换器,以便显示较短的日期字符串。 数据转换部分提供有关转换器的信息。

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

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

4.3 Binding 和 BindingExpression

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

举例来说,假设 myDataObject 是 MyData 类的实例,myBinding 是源 Binding 对象,而 MyData 是包含名为 ColorName 的字符串属性的定义类。 此示例将 TextBlock 的实例 myText 的文本内容绑定到 ColorName

// 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);

可以使用同一 myBinding 对象来创建其他绑定。 例如,可使用 myBinding 对象将复选框的文本内容绑定到 ColorName。 在该方案中,将有两个 BindingExpression 实例共享 myBinding 对象。

通过对数据绑定对象调用 GetBindingExpression 来返回 BindingExpression 对象。 

5、数据转换

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

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

data-binding-button-default-conversion.png?view=netdesktop-8.0

但是,如果绑定源对象拥有的不是字符串类型的属性,而是 Color 类型的 Color 属性,该怎么办? 在这种情况下,为了使绑定正常工作,首先需要将 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;}
}

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

data-binding-converter-color-example.png?view=netdesktop-8.0

重申一下,由于要绑定到的类型中提供了类型转换器,因此可以使用默认转换。 此行为取决于目标中可用的类型转换器。 如果无法确定,请创建自己的转换器。

6、绑定到集合

绑定源对象可以被视为其属性包含数据的单个对象,也可以被视为通常组合在一起的多态对象的数据集合(例如数据库查询的结果)。 目前为止,我们仅讨论了绑定到单个对象, 但绑定到数据集合也是常见方案。 例如,一种常见方案是使用 ItemsControl(例如 ListBox、ListView 或 TreeView)来显示数据集合,如在什么是数据绑定部分所示的应用中。

幸运的是,基本关系图仍然适用。 如果将 ItemsControl 绑定到集合,则关系图如下所示。

data-binding-itemscontrol.png?view=netdesktop-8.0

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

6.1 如何实现集合

你可以枚举实现 IEnumerable 接口的任何集合。 但是,若要设置动态绑定,以便集合中的插入或删除操作可以自动更新 UI,则集合必须实现 INotifyCollectionChanged 接口。 此接口公开一个事件,只要基础集合发生更改,就应该引发该事件。

WPF 提供 ObservableCollection<T> 类,该类是公开 INotifyCollectionChanged 接口的数据集合的内置实现。 若要完全支持将数据值从源对象传输到目标,支持可绑定属性的集合中的每个对象还必须实现 INotifyPropertyChanged 接口。 

在实现自己的集合前,请考虑使用 ObservableCollection<T> 或现有集合类之一,例如 List<T>、Collection<T> 和 BindingList<T> 等。 如果有高级方案并且希望实现自己的集合,请考虑使用 IList,它提供可以按索引逐个访问的对象的非泛型集合,因而可提供最佳性能。

6.2 集合视图

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

什么是集合视图?

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

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

视图创建方法

创建并使用视图的一种方式是直接实例化视图对象,然后将它用作绑定源。 以什么是数据绑定部分中所示的数据绑定演示应用为例。 该应用的实现方式是将 ListBox 绑定到基于数据集合的视图,而不是直接绑定到数据集合。 下面的示例摘自数据绑定演示应用。 CollectionViewSource 类是从 CollectionView 继承的类的 XAML 代理。 在此特定示例中,视图的 Source 绑定到当前应用对象的 AuctionItem 集合(类型为 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 的内部类型无法对项进行分组。
IListListCollectionView最快。
IBindingListBindingListCollectionView

使用默认视图

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

若要获取默认视图,请使用 GetDefaultView 方法。 

包含 ADO.NET DataTables 的集合视图

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

排序

如前所述,视图可以将排序顺序应用于集合。 如同在基础集合中一样,数据可能具有或不具有相关的固有顺序。 借助集合视图,可以根据自己提供的比较条件来强制确定顺序,或更改默认顺序。 由于这是基于客户端的数据视图,因此一种常见情况是用户可能希望根据列对应的值,对多列表格数据进行排序。 通过使用视图,可以应用这种用户实施的排序,而无需对基础集合进行任何更改,甚至不必再次查询集合内容。 

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

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{// Sort the items first by Category and then by StartDatelistingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}

筛选

视图还可以将筛选器应用于集合,以便视图仅显示完整集合的特定子集。 可以根据条件在数据中进行筛选。 例如,正如什么是数据绑定部分中的应用所做的那样,“仅显示成交商品”CheckBox 包含了筛选出成交价等于或大于 25 美元的项的逻辑。 如果选择了 CheckBox,则会执行以下代码将 ShowOnlyBargainsFilter 设置为 Filter 事件处理程序。

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{if (((CheckBox)sender).IsChecked == true)listingDataView.Filter += ListingDataView_Filter;elselistingDataView.Filter -= ListingDataView_Filter;
}

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

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{// Start with everything excludede.Accepted = false;// Only inlcude items with a price less than 25if (e.Item is AuctionItem product && product.CurrentPrice < 25)e.Accepted = true;
}

如果直接使用其中一个 CollectionView 类而不是 CollectionViewSource,则可以使用 Filter 属性指定回叫。 

分组

除了用来查看 IEnumerable 集合的内部类之外,所有集合视图都支持分组功能,用户可以利用此功能将集合视图中的集合划分成逻辑组。 这些组可以是显式的,由用户提供组列表;也可以是隐式的,这些组依据数据动态生成。

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

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

当前项指针

视图还支持当前项的概念。 可以在集合视图中的对象之间导航。 在导航时,你是在移动项指针,该指针可用于检索存在于集合中特定位置的对象。 

由于 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 显示所选项的详细信息。

只需将两个或更多控件绑定到同一视图即可实现主-从方案。 数据绑定演示
中的以下示例演示了在
什么是数据绑定部分中的应用 UI 上看到的 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"/>

请注意,这两个控件都绑定到同一个源,即 listingDataView 静态资源。 此绑定有效是因为将单一实例对象(在本例中为 ContentControl)绑定到集合视图时,它会自动绑定到该视图的 CurrentItem。 CollectionViewSource 对象会自动同步货币和选择。 如果列表控件未像本示例中那样绑定到 CollectionViewSource 对象,则需要将其 IsSynchronizedWithCurrentItem 属性设置为 true 才能起作用。

你可能已经注意到上述示例使用了一个模板。 实际上,如果不使用模板(ContentControl 显式使用的模板以及 ListBox 隐式使用的模板),数据不会按照我们希望的方式显示。 现在,我们开始介绍下一节中的数据模板化。

7、数据模板化

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

demo-no-template.png?view=netdesktop-8.0

如前面部分中的示例所示,ListBox 控件和 ContentControl 都绑定到 AuctionItem 的整个集合对象(更具体地说,是绑定到集合对象视图)。 如果未提供如何显示数据集合的特定说明,则 ListBox 会以字符串形式显示基础集合中的每个对象,ContentControl 会以字符串形式显示绑定到的对象。

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

<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。 如屏幕截图所示,除了可以在控件中放置数据以外,使用 DataTemplate 还可以为数据定义引人注目的视觉对象。 例如,上述 DataTemplate 中使用了 DataTrigger,因而 SpecialFeatures 值为 HighLight 的 AuctionItem 会显示为带有橙色边框和一个星号。

8、数据验证

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

8.1 将验证规则与绑定关联

WPF 数据绑定模型允许将 ValidationRules 与 Binding 对象关联。 例如,以下示例将 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 方法来创建自己的验证规则。 以下示例演示了
什么是数据绑定部分中添加产品清单“起始日期”TextBox 所用的规则。

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

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 集合中的每条规则。 我们会在“验证过程”一节中对此深入讨论。

8.2 提供视觉反馈

如果用户输入的值无效,你可能希望在应用 UI 上提供一些有关错误的反馈。 提供此类反馈的一种方法是将 Validation.ErrorTemplate 附加属性设置为自定义 ControlTemplate。 如前面部分所示,StartDateEntryFormTextBox 使用名为 validationTemplate 的 ErrorTemplate。 以下示例显示了 validationTemplate 的定义。

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

AdornedElementPlaceholder 元素指定应放置待装饰控件的位置。

此外,还可以使用 ToolTip 来显示错误消息。 StartDateEntryForm 和 StartPriceEntryFormTextBox 都使用样式 textStyleTextBox,该样式创建显示错误消息的 ToolTip。 以下示例显示了 textStyleTextBox 的定义。 如果绑定元素属性上的一个或多个绑定出错,则附加属性 Validation.HasError 为 true

<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>

使用自定义 ErrorTemplate 和 ToolTip 时,StartDateEntryFormTextBox 在发生验证错误时如下所示。

demo-validation-date.png?view=netdesktop-8.0

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

demo-validation-price.png?view=netdesktop-8.0

8.3 验证过程

通常,在目标的值传输到绑定源属性时会进行验证。 此传输在 TwoWay 和 OneWayToSource 绑定上发生。 重申一下,导致源更新的因素取决于 UpdateSourceTrigger 属性的值,如
触发源更新的因素部分所述。

以下各项描述了验证过程。 只要验证过程中发生验证错误或其他类型的错误,该过程就会中断:

  1. 绑定引擎检查是否为该 Binding 定义了任何将 ValidationStep 设置为 RawProposedValue 的自定义 ValidationRule 对象,在这种情况下,绑定引擎将对每个 ValidationRule 调用 Validate 方法,直到其中一个出错或直到全部通过。

  2. 绑定引擎随后会调用转换器(如果存在)。

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

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

  5. 绑定引擎检查是否为该 Binding 定义了任何将 ValidationStep 设置为 UpdatedValue 的自定义 ValidationRule 对象,在这种情况下,绑定引擎将对每个 ValidationRule(将 ValidationStep 设置为 UpdatedValue)调用 Validate 方法,直到其中一个出错或直到全部通过。 如果 DataErrorValidationRule 与绑定关联并且其 ValidationStep 设置为默认的 UpdatedValue,则此时将检查 DataErrorValidationRule。 此时检查将 ValidatesOnDataErrors 设置为 true 的所有绑定。

  6. 绑定引擎检查是否为该 Binding 定义了任何将 ValidationStep 设置为 CommittedValue 的自定义 ValidationRule 对象,在这种情况下,绑定引擎将对每个 ValidationRule(将 ValidationStep 设置为 CommittedValue)调用 Validate 方法,直到其中一个出错或直到全部通过。

如果 ValidationRule 在整个过程中的任何时间都没有通过,则绑定引擎会创建 ValidationError 对象并将其添加到绑定元素的 Validation.Errors 集合中。 绑定引擎在任何给定步骤运行 ValidationRule 对象之前,它会删除在执行该步骤期间添加到绑定元素的 Validation.Errors 附加属性的所有 ValidationError。 例如,如果将 ValidationStep 设置为 UpdatedValue 的 ValidationRule 失败,则下次执行验证过程时,绑定引擎会在调用将 ValidationStep 设置为 UpdatedValue 的任何 ValidationRule 之前删除 ValidationError。

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

另请注意,任何方向(目标到源或源到目标)的有效值传输操作都会清除 Validation.Errors 附加属性。

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

9、调试机制

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/241406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【微服务】:微服务最佳实践

关键需求 最大限度地提高团队的自主性&#xff1a;创建一个团队可以完成更多工作而不必与其他团队协调的环境。 优化开发速度&#xff1a;硬件便宜&#xff0c;人不是。使团队能够轻松快捷地构建强大的服务。 关注自动化&#xff1a;人们犯错误。更多的系统操作也意味着更多的…

蓝牙物联网室内定位系统解决方案

目前国内外室内定位技术较多&#xff0c;常见的有无线局域网(Wireless Fidelity,WiFi)、射频识别(Radio FrequencyIdentification,RFID)、蓝牙低功耗(Bletooth Low EnergyBLE)、超宽带(Ultra Wide BandUWB)技术等。近几年智能设备的迅速发展和蓝牙设备的生产制造成本越来越低&a…

信号与线性系统翻转课堂笔记8——周期信号的频谱

信号与线性系统翻转课堂笔记8——周期性信号的频谱 The Flipped Classroom8 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&#…

OpenCV利用HSV颜色区间分离不同物体

需求 当前有个需求是从一个场景中将三个不同的颜色的二维码分离出来&#xff0c;如下图所示。 这里有两个思路可以使用 思路一是通过深度学习的方式&#xff0c;训练一个能够识别旋转边界框的模型&#xff0c;但是需要大量的数据进行模型训练&#xff0c;此处缺少训练数据&a…

CentOs 安装MySQL

1、拉取安装包 wget --no-check-certificate dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm 成功拉取 2、安装 yum install mysql-community-release-el6-5.noarch.rpm 过程中可能需要你同意一些东西&#xff0c;y 即可 然后稍微检查一下 yum repolist enabled…

基于 OpenCV 的车辆变道检测,计算机视觉+图像处理技术

本期教程我们将和小伙伴们一起研究如何使用计算机视觉和图像处理技术来检测汽车在行驶中时汽车是否在改变车道&#xff01;大家一定听说过使用 OpenCV 的 haar 级联文件可以检测到面部、眼睛等&#xff0c;但是如果目标是汽车&#xff0c;公共汽车呢&#xff1f; 01. 数据集 …

前端工程注入版本号

文章目录 一、前言二、webpack三、vite四、最后 一、前言 容器化时代&#xff0c;当页面出现问题时&#xff0c;如果你的新版本有可能已经修复了&#xff0c;那样你再排查它就没有意义了。为什么不一定是最新版本呢&#xff1f;一是可能是缓存作祟&#xff0c;二是可能运维成员…

互联网+建筑工地源码,基于微服务+Java+Spring Cloud +Vue+UniApp开发

一、智慧工地概念 智慧工地就是互联网建筑工地&#xff0c;是将互联网的理念和技术引入建筑工地&#xff0c;然后以物联网、移动互联网技术为基础&#xff0c;充分应用BIM、大数据、人工智能、移动通讯、云计算、物联网等信息技术&#xff0c;通过人机交互、感知、决策、执行和…

使用Python自己写了一个Renpy 汉化插件

之前看了很多教程都是Renpy怎么解包、怎么创建翻译文件&#xff0c;对翻译过程没有过多讲解&#xff0c;就根据翻译文件写了一个小程序&#xff0c;如果需要的可以自行下载使用。 使用方法&#xff1a; 1.按照正常unrpa的解包方式renpy进行解包&#xff1b; 2.使用renpy-sdk…

React 的 Suspense 和 ErrorBoundary 这关系也能有?

Suspense 组件想必大家都用过&#xff0c;一般是和 React.lazy 结合用&#xff0c;用来加载一些异步组件。 比如这样一个组件&#xff1a; // src/Aaa.jsx export default function Aaa() {return <div>aaa</div> }就可以在另一个组件里用 lazy Suspense 异步加…

测试开发体系介绍——测试体系介绍-L3

目录&#xff1a; 测试框架体系TDDDDTBDDATDD介绍 测试框架是什么&#xff1f;测试框架的价值&#xff1a;测试框架的收益&#xff1a;常见测试框架类型&#xff1a;TDDBDDBehaviorDrivenDevelopmentATDDAcceptanceTestDrivenDevelopmentMBTModelBasedTestingDDTDataDrivenTes…

案例147:基于微信小程序的酒店管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

MFC 视图窗口

目录 视图窗口概述 视图窗口的使用 视图窗口创建流程 命令消息 WM_COMMAND 处理顺序 对象关系 视图窗口概述 作用&#xff1a;提供了一个用于显示数据的窗口 关于视图窗口 视图类是用来展示用户&#xff0c;文档类是用来存储和管理数据视图窗口是覆盖掉框架窗口的客户区…

详解Vue3中的插槽(slot)

本文主要介绍Vue3中的插槽&#xff08;slot&#xff09;。 目录 一、在普通写法中使用插槽&#xff08;slot&#xff09;作用域插槽默认插槽 二、在setup写法中使用插槽&#xff1a;注意事项 在Vue3中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于在父组件中向子组件…

Unity矩阵平移旋转缩放Matrix4x4

Unity矩阵平移旋转缩放Matrix4x4 Unity中的矩阵&#xff08;Matrix4x4&#xff09;创建自定义模型平移矩阵缩放矩阵旋转矩阵 Unity中的矩阵&#xff08;Matrix4x4&#xff09; 最近在研究帧同步定点数物理系统中需要自定义定点数矩阵&#xff0c;所以在这里分享下基础的矩阵案…

蓝牙物联网在智能家居中的应用前景

物联网智能家居系统是应用物联网技术&#xff0c;在传统家居环境下将各种零散无序的电器整合成统一整体&#xff0c;实现家电的全程自动控制&#xff0c;满足用户高效管理需求的一种新型家居模式。 其主要的子系统有家居感知系统、家庭网络系统、智能家居控制管理系统等&#x…

使用教程之【SkyWant.[2304]】路由器操作系统,破解移动【Netkeeper】校园网【小白篇】

许多高校目前饱受Netkeeper认证的痛苦&#xff0c;普通路由器无法使用&#xff0c; 教你利用SkyWant的Netkeeper认证软件来使你的SkyWant路由器顺利认证上网&#xff0c;全宿舍又可以合作共赢了&#xff01; 步骤一&#xff1a;正确连接网线&#xff0c;插电开机 正确连接网…

分布式Session使用步骤

目录 1. 为什么用分布式Session2. Spring-Session使用步骤2-1. 添加依赖2-2. yml配置 1. 为什么用分布式Session 将一个项目部署到多台服务器上时&#xff0c;多台服务器的Tomcat的Session不共享。那么就有可能造成Session数据不一致情况&#xff0c;所以此时就需要使用分布式…

Gateway API

Gateway API 目录 原文链接 https://onedayxyy.cn/docs/GatewayAPI 本节实战 实战名称&#x1f6a9; 实战&#xff1a;Gateway API在istio里的安装及测试-2023.12.23(测试失败) 前言 Gateway API 是由 SIG-NETWORK 社区管理的开源项目&#xff0c;项目地址&#xff1a;http…

nodejs+vue+ElementUi洗衣店订单管理系统4691l

衣服但是找订单的时间太长&#xff0c;体验非常的差。而且对于店家这也很头疼&#xff0c;麻烦的查找订单的方式&#xff0c;让他总是重复着繁琐的步骤&#xff0c;记录的时候也很容易出问题&#xff0c;容易把衣服弄错&#xff0c;再然后就是对于收来的衣服也很麻烦&#xff0…