数据绑定与数据控件全解析
1. 数据模板的使用
数据模板在展示数据时非常有用。以下是一个简单的数据模板示例:
<TextBlock Grid.Row="1"
Text="{Binding ModelName}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
使用静态资源引用可以使用这个数据模板:
<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource ProductDataTemplate}"
SelectionChanged="lstProducts_SelectionChanged"></ListBox>
需要注意的是,数据模板并不一定需要数据绑定。可以通过声明式(在 XAML 标记中)或编程式(调用
ListBox.Items.Add()
方法)添加
Product
对象,数据模板的工作方式是相同的。
2. 隐式数据模板
之前的示例中,模板是通过列表控件的
ItemTemplate
属性显式设置的。而 Silverlight 还提供了隐式模板。使用隐式模板时,需要为特定类型的数据定义一个数据模板。具体操作步骤如下:
1. 从
ListBox
控件中移除
ItemTemplate
属性:
<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch"
SelectionChanged="lstProducts_SelectionChanged"></ListBox>
-
更改资源集合中数据模板的定义方式,移除键名并添加
DataType属性:
<UserControl.Resources>
<DataTemplate DataType="local:Product">
...
</DataTemplate>
</UserControl.Resources>
同时,需要定义一个 XML 命名空间,将
local
前缀映射到包含
Product
类的命名空间:
<UserControl xmlns:local="clr-namespace:DataBinding.DataService" ... >
当用
Product
对象填充列表时,
ListBox
控件会自动找到合适的数据模板并使用它。隐式模板有以下两种特殊用途:
-
作为不同位置相同数据的快捷方式
:如果需要在不同控件中使用相同的数据模板,将其定义为应用程序资源会更方便,所有绑定的控件都会自动获取该模板。
-
创建具有多个模板的列表
:如果数据源包含多种类型的对象,可以为每种类型提供不同的隐式模板。例如,有一个
Product
对象列表,其中包含一些派生自
Product
的对象(如
SalesProduct
、
ExportProduct
或
RestrictedProduct
),可以为每个类提供单独的隐式模板,绑定的控件会为相应的类使用正确的模板。
3. 更高级的模板
数据模板可以非常自给自足,除了基本元素(如
TextBlock
和数据绑定表达式)外,还可以使用更复杂的控件、附加事件处理程序、将数据转换为不同的表示形式、使用动画等。可以在绑定表达式中使用值转换器将数据转换为更有用的表示形式。例如,
ImagePathConverter
接受一个图片文件名,并使用它创建一个包含相应图像内容的
BitmapImage
对象,该对象可以直接绑定到
Image
元素。以下是使用
ImagePathConverter
构建的显示每个产品图像的数据模板:
<UserControl.Resources>
<local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>
<DataTemplate x:Key="ProductDataTemplate">
<Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
CornerRadius="4">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"></TextBlock>
<Image Grid.Row="2" Grid.RowSpan="2" Source=
"{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}">
</Image>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
如果模板中存在错误,不会抛出异常,控件将无法显示数据并保持空白。可以使用数据绑定调试功能解决此问题。
4. 更改项目布局
数据模板可以很好地控制项目展示的各个方面,但不能改变项目之间的组织方式。列表框默认将每个项目放入单独的水平行中并堆叠这些行以创建列表。可以通过替换列表用于布局其子项的容器来更改此布局,具体操作是设置
ItemsPanel
属性,使用一段 XAML 定义要使用的面板,该面板可以是任何派生自
System.Windows.Controls.Panel
的类,包括实现自定义布局逻辑的自定义布局容器。以下是使用
Silverlight Toolkit
中的
WrapPanel
的示例,它可以将项目从左到右排列在多行中:
<ListBox Margin="7,3,7,10" Name="lstProducts"
ItemTemplate="{StaticResource ProductDataTemplate}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel></controlsToolkit:WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
5. 数据控件概述
虽然数据绑定是一个灵活而强大的系统,但要获得想要的结果可能需要大量工作。例如,一个典型的数据表单需要将多个不同的属性绑定到不同的控件,以有意义的方式安排它们,并使用适当的转换器、模板和验证逻辑。Silverlight 提供了一些功能来帮助减轻部分工作:
| 控件名称 | 功能描述 |
| ---- | ---- |
|
Label
和
DescriptionViewer
控件 | 自动从数据对象中提取元数据并显示在页面上 |
| 数据注解 | 允许在数据类中嵌入验证规则,与
ValidationSummary
控件配合使用可轻松列出页面中的所有验证错误 |
|
DataGrid
控件 | 是 Silverlight 丰富数据支持的核心,是一个高度可定制的表格,支持排序、编辑、分组和分页 |
|
TreeView
控件 | 处理层次结构数据时非常节省时间,如包含嵌套产品列表的类别列表 |
|
PivotViewer
控件 | 是一个带有内置动画的奇特数据可视化工具,适用于特定应用场景 |
6. 更好的数据表单
使用数据绑定构建的基本数据表单虽然需要的代码相对较少,但实际构建时需要大量手写的 XAML 标记,并且需要包含硬编码的详细信息(如标题文本、提示和错误消息)。如果数据模型频繁更改,管理这些细节会很麻烦。为了解决这些问题,Silverlight 的开发者正在努力构建更高级的数据控件、辅助类和基于服务器的数据管理框架。
Label
、
DescriptionViewer
和
ValidationSummary
这三个控件结合强大的数据注解功能,可以更轻松地构建丰富的数据表单。需要注意的是,要使用这些控件,必须添加对
System.Windows.Controls.Data.Input.dll
程序集的引用。
7. 数据类标记的目标
使用
Label
、
DescriptionViewer
和
ValidationSummary
控件与智能数据类(使用少量属性嵌入额外信息的类)结合使用时,优势更加明显。基于属性的方法有以下好处:
-
节省时间
:可以更快地构建数据表单。
-
更易于维护
:如果底层数据模型发生变化,只需调整数据类中的属性,无需编辑页面标记。
但在使用 Web 服务时,基于属性的方法存在一个缺点,即所有属性都放在数据类中,自动生成的客户端代码会剥离这些属性。可以使用 Web 服务类型共享来避免这个问题。
8.
Label
控件
Label
控件可以替代为数据控件添加标题的
TextBlock
。以下是一个简单示例:
<TextBlock Margin="7">Model Number:</TextBlock>
<TextBox Margin="5" Grid.Column="1" x:Name="txtModelNumber"
Text="{Binding ModelNumber, Mode=TwoWay}"></TextBox>
可以用
Label
替换
TextBlock
:
<dataInput:Label Margin="7" Content="Model Number:"></dataInput:Label>
<TextBox Margin="5" Grid.Column="1" x:Name="txtModelNumber"
Text="{Binding ModelNumber, Mode=TwoWay}"></TextBox>
当使用
Target
属性将
Label
绑定到要添加标题的控件时,它会检查引用的元素,找到绑定的属性,并查找
Display
属性:
<dataInput:Label Margin="7" Target="{Binding ElementName=txtModelNumber}">
</dataInput:Label>
<TextBox Margin="5" Grid.Column="1" x:Name="txtModelNumber"
Text="{Binding ModelNumber, Mode=TwoWay}"></TextBox>
<Display(Name := "Model Number")> _
Public Property ModelNumber() As String
End Property
Label
会显示
Display
属性中的文本。在添加
Display
属性之前,需要添加对
System.ComponentModel.DataAnnotations.dll
程序集的引用并导入相应的命名空间。
Label
不仅可以显示基本标题,还可以通过改变外观来标记必需属性和验证错误。例如,添加
Required
属性可以将属性指定为必需属性:
<Required>, _
<Display(Name := "Model Number")> _
Public Property ModelNumber() As String
End Property
默认情况下,
Label
会将标题文本加粗。可以通过修改
Label
控件模板中的
Required
和
NotRequired
视觉状态来更改此格式。同样,当用户编辑数据时,
Label
会关注验证错误。绑定需要使用
ValidatesOnExceptions
和
NotifyOnValidationError
属性启用验证:
<dataInput:Label Margin="7" Grid.Row="2"
Target="{Binding ElementName=txtUnitCost}"></dataInput:Label>
<TextBox Margin="5" Grid.Row="2" Grid.Column="1" x:Name="txtUnitCost" Width="100"
HorizontalAlignment="Left" Text="{Binding UnitCost, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}"></TextBox>
当在
UnitCost
字段中输入非数字字符并切换焦点时,
Label
中的标题文本会从黑色变为红色。可以通过修改
Label
控件模板中的
Valid
和
Invalid
视觉状态来实现更有趣的效果。
9.
DescriptionViewer
控件
Label
控件负责显示标题文本并突出显示必需属性和无效数据,但用户在填写复杂表单时可能需要更多信息。
DescriptionViewer
控件可以轻松将描述性文本融入用户界面。它依赖于
Display
属性的
Description
属性:
<Display(Name := "Model Number", _
Description := "This is the alphanumeric product tag used in the warehouse.")> _
Public Property ModelNumber() As String
End Property
以下是添加
DescriptionViewer
的示例:
<TextBlock Margin="7">Model Number</TextBlock>
<TextBox Margin="5" Grid.Column="1" x:Name="txtModelNumber"
Text="{Binding ModelNumber, Mode=TwoWay, ValidatesOnExceptions=true,
NotifyOnValidationError=true}"></TextBox>
<dataInput:DescriptionViewer Grid.Column="2"
Target="{Binding ElementName=txtModelNumber}"></dataInput:DescriptionViewer>
DescriptionViewer
显示一个小信息图标,当用户将鼠标悬停在其上时,描述文本会显示在工具提示中。可以通过设置
GlyphTemplate
属性替换图标:
<dataInput:DescriptionViewer Grid.Row="1" Grid.Column="2"
Target="{Binding ElementName=ModelName}">
<dataInput:DescriptionViewer.GlyphTemplate>
<ControlTemplate>
<Image Source="info.jpg" Stretch="None"></Image>
</ControlTemplate>
</dataInput:DescriptionViewer.GlyphTemplate>
</dataInput:DescriptionViewer>
DescriptionViewer
在绑定数据有验证错误时不会改变外观,但它包含
IsValid
属性并支持四种基本的验证视觉状态(
ValidFocused
、
ValidUnfocused
、
InvalidFocused
和
InvalidUnfocused
),可以通过更改模板来突出显示错误或应用动画。
10.
ValidationSummary
控件
Silverlight 提供了多种方式标记无效数据,如输入控件外观变化、弹出错误消息、
Label
标题文本变红等。但在长表单中,显示一个总结一组控件问题的错误列表通常很有用。
ValidationSummary
控件可以轻松实现此功能,无需编写代码。它会监控容器中的错误事件,通常不需要设置
Target
属性,它会自动引用其容器并监控其中的所有控件。以下是添加
ValidationSummary
的示例:
<dataInput:ValidationSummary Grid.Row="6" Grid.ColumnSpan="3" Margin="7" />
要捕获错误,绑定的
Mode
必须设置为
TwoWay
,并且必须设置
ValidatesOnExceptions
和
NotifyOnValidationError
为
true
。当没有错误时,
ValidationSummary
不可见且不占用空间;当有一个或多个错误时,会显示一个包含错误图标和错误数量的标题以及详细的错误列表。如果用户点击错误消息,
ValidationSummary
会触发
FocusingInvalidControl
事件并将焦点转移到包含数据的输入控件(除非明确将
FocusControlsOnClick
属性设置为
False
)。如果要防止某个控件将其错误添加到
ValidationSummary
中,可以设置附加的
ShowErrorsInSummary
属性:
<TextBox Margin="5" x:Name="txtUnitCost" Width="100" HorizontalAlignment="Left"
dataInput:ValidationSummary.ShowErrorsInSummary="False"
Text="{Binding UnitCost, Mode=TwoWay, ValidatesOnExceptions=true,
NotifyOnValidationError=true}"></TextBox>
数据绑定与数据控件全解析
11. 数据控件使用总结
在实际开发中,合理运用这些数据控件能显著提升开发效率和用户体验。下面通过一个流程图来展示使用这些控件构建数据表单的大致流程:
graph LR
A[定义数据类] --> B[添加数据注解]
B --> C[创建数据表单界面]
C --> D[使用Label控件显示标题]
C --> E[使用DescriptionViewer控件提供描述信息]
C --> F[使用ValidationSummary控件汇总错误]
D --> G[绑定数据属性]
E --> G
F --> G
G --> H[验证数据]
H --> I{数据是否有效}
I -- 是 --> J[保存数据]
I -- 否 --> K[显示错误信息]
K --> H
这个流程图展示了从定义数据类开始,到最终保存有效数据的整个过程。在这个过程中,各个数据控件发挥着不同的作用。
12. 数据绑定与控件的组合应用案例
为了更好地理解这些数据绑定和控件的组合应用,我们来看一个完整的案例。假设我们要创建一个产品信息管理系统,需要展示和编辑产品的相关信息。
首先,定义产品数据类:
Imports System.ComponentModel.DataAnnotations
Public Class Product
<Required>
<Display(Name := "Model Number", Description := "This is the unique identifier of the product.")>
Public Property ModelNumber As String
<Display(Name := "Model Name", Description := "The name of the product.")>
Public Property ModelName As String
<Required>
<Display(Name := "Unit Cost", Description := "The cost of each unit of the product.")>
Public Property UnitCost As Decimal
End Class
然后,创建 XAML 界面:
<UserControl xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"
xmlns:local="clr-namespace:YourNamespace">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Model Number -->
<dataInput:Label Margin="7" Target="{Binding ElementName=txtModelNumber}"/>
<TextBox Margin="5" Grid.Row="0" Grid.Column="1" x:Name="txtModelNumber"
Text="{Binding ModelNumber, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}"/>
<dataInput:DescriptionViewer Grid.Row="0" Grid.Column="2"
Target="{Binding ElementName=txtModelNumber}"/>
<!-- Model Name -->
<dataInput:Label Margin="7" Grid.Row="1" Target="{Binding ElementName=txtModelName}"/>
<TextBox Margin="5" Grid.Row="1" Grid.Column="1" x:Name="txtModelName"
Text="{Binding ModelName, Mode=TwoWay}"/>
<dataInput:DescriptionViewer Grid.Row="1" Grid.Column="2"
Target="{Binding ElementName=txtModelName}"/>
<!-- Unit Cost -->
<dataInput:Label Margin="7" Grid.Row="2" Target="{Binding ElementName=txtUnitCost}"/>
<TextBox Margin="5" Grid.Row="2" Grid.Column="1" x:Name="txtUnitCost" Width="100"
HorizontalAlignment="Left"
Text="{Binding UnitCost, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}"/>
<dataInput:DescriptionViewer Grid.Row="2" Grid.Column="2"
Target="{Binding ElementName=txtUnitCost}"/>
<!-- Validation Summary -->
<dataInput:ValidationSummary Grid.Row="3" Grid.ColumnSpan="3" Margin="7"/>
</Grid>
</UserControl>
在这个案例中,我们使用了
Label
控件来显示标题,
DescriptionViewer
控件来提供描述信息,
ValidationSummary
控件来汇总验证错误。当用户输入数据时,数据绑定会自动验证数据的有效性,并通过相应的控件显示错误信息。
13. 性能优化与注意事项
在使用这些数据绑定和控件时,还需要注意一些性能优化和其他方面的问题:
-
数据绑定的性能
:过多的数据绑定可能会影响应用程序的性能。尽量减少不必要的绑定,特别是在数据量较大的情况下。可以考虑使用虚拟列表等技术来提高性能。
-
控件模板的使用
:修改控件模板时要谨慎,因为不当的修改可能会导致控件的行为异常。在修改模板之前,最好先备份原始模板。
-
验证逻辑的复杂性
:虽然数据注解可以方便地添加验证规则,但如果验证逻辑过于复杂,可能会导致代码难以维护。可以考虑将复杂的验证逻辑封装到自定义的验证器中。
-
资源管理
:在使用图像等资源时,要注意资源的加载和释放,避免内存泄漏。
14. 不同控件的适用场景总结
不同的数据控件适用于不同的场景,下面通过一个表格来总结:
| 控件名称 | 适用场景 |
| ---- | ---- |
|
Label
| 为数据控件提供标题,自动显示数据类中的标题信息,适用于各种数据表单场景 |
|
DescriptionViewer
| 为用户提供数据属性的详细描述,帮助用户更好地理解数据,适用于复杂表单场景 |
|
ValidationSummary
| 汇总表单中的验证错误,方便用户一次性查看所有错误信息,适用于长表单场景 |
|
DataGrid
| 展示和编辑大量数据,支持排序、分组、分页等功能,适用于数据管理系统 |
|
TreeView
| 处理层次结构数据,如分类列表、文件目录等,适用于需要展示层次关系的场景 |
|
PivotViewer
| 以独特的方式可视化数据,带有动画效果,适用于特定的数据分析和展示场景 |
15. 未来发展趋势
随着技术的不断发展,数据绑定和数据控件也会不断演进。未来可能会有以下发展趋势:
-
更智能的验证机制
:能够自动识别数据的类型和业务规则,提供更精准的验证提示。
-
与新兴技术的融合
:如与人工智能、机器学习技术结合,实现更智能的数据处理和分析。
-
跨平台兼容性
:能够在不同的平台和设备上更好地运行,提供一致的用户体验。
-
可视化设计工具的增强
:开发人员可以通过更直观的可视化工具来设计数据表单和界面,减少手动编写代码的工作量。
通过对数据绑定和各种数据控件的深入了解和合理应用,开发人员可以构建出更加高效、易用的数据管理系统和应用程序。在实际开发中,要根据具体的需求选择合适的控件和技术,不断优化和改进,以满足用户的期望。
超级会员免费看
1354

被折叠的 条评论
为什么被折叠?



