深入探究数据绑定:从基础到服务构建
立即解锁
发布时间: 2025-08-14 00:59:01 阅读量: 10 订阅数: 28 


Silverlight 3开发实战指南
### 深入探索Silverlight数据绑定:从基础到高级应用
#### 1. 数据对象存储为资源
在Silverlight开发中,通常会将所有绑定控件放置在同一个容器中,这样只需为容器设置一次`DataContext`,而不必为每个绑定元素单独设置。除了这种方式,还可以将数据对象定义为XAML标记中的资源,然后通过添加`Source`属性来修改每个绑定表达式。
以下是创建`Product`对象作为资源的示例代码:
```xml
<UserControl.Resources>
<local:Product x:Key="resourceProduct"
ModelNumber="AEFS100"
ModelName="Portable Defibrillator" UnitCost="77"
Description="Analyzes the electrical activity of a person's heart and applies an electric shock if necessary.">
</local:Product>
</UserControl.Resources>
```
此标记假设已将项目命名空间映射到XML命名空间前缀`local`。例如,如果项目名为`DataBinding`,则需要在`UserControl`开始标记中添加以下属性:
```xml
xmlns:local="clr-namespace:DataBinding"
```
要在绑定表达式中使用此对象,需要指定`Source`属性。可以使用`StaticResource`表达式来设置`Source`属性,该表达式使用资源的键名:
```xml
<TextBox
Text="{Binding ModelNumber, Source={StaticResource resourceProduct} }">
</TextBox>
```
不过,在每个数据绑定表达式中都必须指定`Source`属性。如果需要将大量元素绑定到同一个数据对象,设置容器的`DataContext`属性会更简单。在这种情况下,仍然可以使用`StaticResource`来设置`DataContext`属性,从而将一组嵌套元素绑定到定义为资源的单个数据对象:
```xml
<Grid Name="gridProductDetails" DataContext="{StaticResource resourceProduct}">
```
需要注意的是,将数据对象定义为资源会牺牲一定的灵活性。虽然仍然可以更改该对象,但无法替换它。如果计划从其他源(如Web服务)检索数据对象的详细信息,在代码中创建数据对象会更自然。
此外,`Binding`标记扩展除了`Source`属性外,还支持其他几个属性,包括`Mode`(允许使用双向绑定来编辑数据对象)和`Converter`(允许在显示源值之前对其进行修改)。
#### 2. 双向绑定编辑
当用户更改文本控件中显示的绑定值时,可能会想知道内存中的`Product`对象是否会发生变化。可以使用以下代码从`DataContext`中获取当前的`Product`对象,并将其属性显示在`TextBlock`中:
```csharp
Product product = (Product)gridProductDetails.DataContext;
lblCheck.Text = "Model Name: " + product.ModelName + "\nModel Number: " +
product.ModelNumber + "\nUnit Cost: " + product.UnitCost;
```
运行此代码会发现,更改显示的值不会产生任何影响,`Product`对象仍保持其原始形式。这是因为绑定表达式默认使用单向绑定。
实际上,Silverlight允许在设置`Binding.Mode`属性时使用`System.Windows.Data.BindingMode`枚举中的三个值之一,具体如下表所示:
| 名称 | 描述 |
| ---- | ---- |
| OneWay | 当源属性更改时,目标属性会更新。 |
| TwoWay | 当源属性更改时,目标属性会更新;当目标属性更改时,源属性也会更新。 |
| OneTime | 目标属性最初根据源属性值设置,但从那时起忽略更改。通常,当知道源属性不会更改时,使用此模式以减少开销。 |
如果将一个或多个绑定更改为使用双向绑定,在文本框中所做的更改会在焦点离开文本框时(例如,移动到另一个控件或单击按钮时)提交到内存中的对象:
```xml
<TextBox Text="{Binding UnitCost, Mode=TwoWay}"></TextBox>
```
需要注意的是,使用文本框进行双向绑定时,内存中的数据对象在文本框失去焦点之前不会被修改。但其他元素会立即执行更新,例如在列表框中进行选择、移动滑块中的拇指或更改复选框的状态时,源对象会立即被修改。
在某些情况下,需要精确控制更新的应用时间。例如,可能需要文本框在用户输入时应用更改,而不是等待焦点更改。在这种情况下,需要在代码中手动调用`BindingExpression.UpdateSource()`方法。以下是每次用户输入或编辑文本时强制文本框更新源数据对象的代码:
```csharp
private void txtUnitCost_TextChanged(object sender, TextChangedEventArgs e)
{
BindingExpression expression =
txtUnitCost.GetBindingExpression(TextBox.TextProperty);
expression.UpdateSource();
}
```
如果所有更新都通过代码进行,可以使用`Binding`对象的`UpdateSourceTrigger`属性禁用Silverlight的自动更新系统:
```xml
<TextBox Text=
"{Binding UnitCost, Mode=TwoWay, UpdateSourceTrigger=Explicit}"></TextBox>
```
Silverlight的`UpdateSourceTrigger`仅支持两个值:`Default`和`Explicit`,无法选择`PropertyChanged`(如WPF中那样)。但通过一些代码和`UpdateSource()`方法,可以确保在需要时进行更新。
#### 3. 数据验证
当Silverlight数据绑定系统遇到无效数据时,通常会忽略它。在编辑双向字段时,可能会出现以下三种类型的错误:
- **数据类型不正确**:例如,像`UnitCost`这样的数字属性无法容纳字母或特殊字符,也不能保存极大的数字(大于1.79769313486231570E+308的数字)。
- **属性设置器异常**:例如,`UnitCost`属性可能会进行范围检查,如果尝试设置负数,会抛出异常。
- **只读属性**:此类属性根本无法设置。
由于Silverlight数据绑定系统不会提供任何视觉反馈,因此很可能会错过这些错误。不正确的值会保留在绑定控件中,但永远不会应用到绑定对象。
为避免混淆,最好尽快提醒用户他们的错误。最简单的方法是使用`Binding`对象的两个属性`ValidatesOnExceptions`和`NotifyOnValidationError`,这两个属性会告诉Silverlight使用错误通知事件。
##### 3.1 ValidatesOnException
`ValidatesOnExceptions`是实现任何类型验证的第一步。将`ValidatesOnExceptions`设置为`true`后,数据绑定系统会对任何错误做出反应,无论错误是发生在类型转换器还是属性设置器中。但当`ValidatesOnException`设置为`false`(默认值)时,数据绑定系统在遇到这些情况时会静默失败。数据对象不会更新,但违规值会保留在绑定控件中。
以下是将此属性应用于`UnitCost`绑定的示例:
```xml
<TextBox Margin="5" Grid.Row="2" Grid.Column="1" x:Name="txtUnitCost"
Text="{Binding UnitCost, Mode=TwoWay, ValidatesOnExceptions=True}"></TextBox>
```
这个简单的更改使应用程序能够捕获和显示错误,前提是使用支持`ValidationState`组控件状态的控件进行双向数据绑定。无需额外工作即可支持此功能的控件包括:
- `TextBox`
- `PasswordBox`
- `CheckBox`
- `RadioButton`
- `ListBox`
- `ComboBox`
在验证时,控件必须支持三种状态:`Valid`、`InvalidUnfocused`和`InvalidFocused`。这些状态组成了`ValidationState`组,允许控件在包含无效数据时改变其外观。
为了更好地理解其工作原理,以包含无效数据的文本框为例。首先,考虑`Product`类的一个版本,它使用以下代码捕获负价格并抛出异常:
```csharp
private double unitCost;
public double UnitCost
{
get { return unitCost; }
set
{
if (value < 0) throw new ArgumentException("Can't be less than 0.");
unitCost = value;
}
}
```
如果用户输入负数,属性设置器会抛出`ArgumentException`。由于`ValidatesOnException`设置为`true`,此异常会被数据绑定系统捕获,然后将文本框的`ValidationState`从`Valid`切换到`InvalidFocused`(如果文本框当前具有焦点)或`InvalidUnfocused`(如果文本框没有焦点)。
在未聚焦状态下,文本框会有一个深红色边框,右上角会有一个错误通知图标(一个小红三角形)。在聚焦状态下,或者当用户将鼠标悬停在错误图标上时,异常消息文本会显示在弹出的红色警报气球中。
需要注意的是,为了使红色弹出气球正常显示,文本框与浏览器窗口边缘之间必须有足够的空间。如果文本框右侧有空间,气球会显示在那里;否则,会显示在左侧。气球会显示在同一位置的任何其他元素(如按钮或标签)之上,但不能超出浏览器窗口。如果消息太长而无法在可用空间中显示,部分内容会被截断。
虽然错误弹出窗口看起来简单且非常有用,但将验证显示连接到控件模板存在一个缺点:如果想更改控件显示错误消息的方式(或完全禁用错误显示),则需要替换整个控件模板,并确保包含所有其他无关状态和标记细节。由于平均控件模板相当长,这个过程既繁琐又有潜在局限性。
##### 3.2 NotifyOnValidationError
在将`ValidatesOnExceptions`设置为`true`后,还可以选择启用`NotifyOnValidationError`。启用后,当发生错误时,数据绑定系统会触发`BindingValidationError`事件:
```xml
<TextBox Margin="5" Grid.Row="2" Grid.Column="1" x:Name="txtUnitCost"
Text="{Binding UnitCost, Mode=TwoWay, ValidatesOnExceptions=True,
NotifyOnValidationError=True}"></TextBox>
```
`BindingValidationError`是一个冒泡事件,这意味着可以在事件发生的位置(文本框中)或更高级别(如包含的`Grid`)处理它。在事件发生的位置处理错误,可以编写针对性的错误处理逻辑,分别处理不同字段中的错误;在更高级别处理错误(如下所示),可以为许多不同类型的错误重用相同的逻辑:
```xml
<Grid Name="gridProductDetails"
BindingValidationError="Grid_BindingValidationError">
```
当问题发生时,需要采取相应的措施。可以选择显示消息或更改应用程序某些部分的外观,但`BindingValidationError`事件的真正强大之处在于可以执
0
0
复制全文
相关推荐










