在 WPF 中显式更新和验证数据绑定





5.00/5 (3投票s)
如何在 WPF 中显式更新和验证数据绑定。
前几天,我正在为 Codeproject 文章编写内容,需要将 UI 的一部分绑定到底层数据对象。我想使用所有好的验证功能,例如用于我的 TextBox
的验证样式,以及使用新的 .NET 3.5 接口 IDataErrorInfo
。
这没问题。但我也希望允许用户应用更改或取消更改。当用户选择应用更改时,应该显式地将更改应用到底层数据对象,并且仅当底层数据对象处于有效状态时才更新数据库。
那么我们该怎么做呢?首先,要确保我们有一个提供使用 .NET 3.5 接口 IDataErrorInfo
进行验证的数据对象。
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ComponentModel;
6:
7: namespace Binding.Explicit
8: {
9: class Person : IDataErrorInfo
10: {
11: #region Data
12: private StringBuilder combinedError
13: = new StringBuilder(2000);
14: #endregion
15:
16: #region Ctor
17: public Person ()
18: {
19:
20: }
21: #endregion
22:
23: #region Public Properties
24: public int Age { get; set; }
25: #endregion
26:
27: #region IDataErrorInfo Members
28:
29: /// <summary>
30: /// Return the full list of validation
31: /// errors for this object
32: /// </summary>
33: public string Error
34: {
35: get
36: {
37: return combinedError.ToString();
38: }
39: }
40:
41: /// <summary>
42: /// Validates a particular column, and returns a
43: /// string representing the current error
44: /// </summary>
45: /// <param name=”columnName”>The property name to
46: /// validate</param>
47: /// <returns>A string representing the
48: /// current error</returns>
49: public string this[string columnName]
50: {
51: get
52: {
53: string result = null;
54:
55: //basically we need a case for each property you
56: //wish to validate
57: switch (columnName)
58: {
59: case “Age”:
60: if (Age < 0)
61: {
62: result = “Age cant be < 0″;
63: combinedError.Append (result + “rn”);
64: }
65: if (Age > 20)
66: {
67: result = “Age cant be > 20″;
68: combinedError.Append(result + “rn”);
69: }
70: break;
71: }
72: return result;
73: }
74: }
75:
76: #endregion
77: }
78: }
然后,我们需要创建一些将使用这些绑定的项(在本简单示例中仅为“Age
”)。
1: <Window x:Class=”Binding.Explicit.Window1″
2: xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
3: xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
4: Title=”Window1″ Height=”300″ Width=”300″>
5:
6: <Window.Resources>
7:
8: <Style x:Key=”textStyleTextBox” TargetType=”TextBox”>
9: <Setter Property=”Foreground” Value=”#333333″ />
10: <Style.Triggers>
11: <Trigger Property=”Validation.HasError” Value=”true”>
12: <Setter Property=”ToolTip”
13: Value=”{Binding
14: RelativeSource={RelativeSource Self},
15: Path=(Validation.Errors)[0].ErrorContent}”/>
16: </Trigger>
17: </Style.Triggers>
18: </Style>
19:
20: </Window.Resources>
21:
22: <StackPanel Orientation=”Vertical”>
23: <Label Content=”Age” Width=”auto” Height=”auto”/>
24: <TextBox x:Name=”txtAge” Width=”auto” Height=”auto”
25: Style=”{StaticResource textStyleTextBox}”
26: Text=”{Binding Path=Age,
27: UpdateSourceTrigger=Explicit,
28: ValidatesOnDataErrors=True}”/>
29: <StackPanel Orientation=”Horizontal”>
30: <Button x:Name=”btnUpdate” Content=”Update Object”
31: Width=”auto” Height=”auto” Click=”btnUpdate_Click”/>
32: <Button x:Name=”btnCancel” Content=”Cancel”
33: Width=”auto” Height=”auto” Click=”btnCancel_Click”/>
34: </StackPanel>
35: </StackPanel>
36: </Window>
另外请注意,在此 XAML 中有一个 Style
,该样式由绑定的 TextBox
使用。当绑定的对象处于无效状态时(基本上当 Validation.HasError
为 true
时),此 Style
会在绑定的 TextBox
周围创建一个红色矩形和适当的工具提示。
另外请注意,由于我的需求之一是可以选择更新底层对象或取消任何更改,因此我在 Binding 表达式中使用“UpdateSourceTrigger=Explicit
”。
所以,正如你可能想象的那样,最后一部分是编写代码,我们在其中显式地(手动地)执行绑定更新。让我们看看吧?
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Windows;
6: using System.Windows.Controls;
7: using System.Windows.Data;
8: using System.Windows.Documents;
9: using System.Windows.Input;
10: using System.Windows.Media;
11: using System.Windows.Media.Imaging;
12: using System.Windows.Navigation;
13: using System.Windows.Shapes;
14: using System.ComponentModel;
15:
16: namespace Binding.Explicit
17: {
18: /// <summary>
19: /// Interaction logic for Window1.xaml
20: /// </summary>
21: public partial class Window1 : Window
22: {
23: public Window1()
24: {
25: InitializeComponent();
26: //Create a single Person to be used as the DataContext
27: this.DataContext = new Person();
28: }
29:
30: /// <summary>
31: /// Manually update the Binding SOurce, and see if its in a valid state.
32: /// If its not need to mark bindind as Invalid
33: /// </summary>
34: private void btnUpdate_Click(object sender, RoutedEventArgs e)
35: {
36: BindingExpression expression =
37: txtAge.GetBindingExpression(TextBox.TextProperty);
38: expression.UpdateSource();
39:
40: string errorMessage = string.Empty;
41: if (!IsValid(“Age”, out errorMessage))
42: {
43: ValidationError error = new ValidationError(
44: new ExceptionValidationRule(),
45: expression, errorMessage, null);
46: Validation.MarkInvalid(expression, error);
47: }
48: else
49: {
50: MessageBox.Show(“Success, we could update DB here”,
51: “Success”, MessageBoxButton.OK,
52: MessageBoxImage.Information);
53: }
54: }
55:
56: /// <summary>
57: /// Attempts to see if the underlying data objects
58: /// bound property is in a valid state. The
59: /// errorMessage parameter is also filled in by the
60: /// underlying data object
61: /// </summary>
62: /// <param name=”path”>The property to validate</param>
63: /// <param name=”errorMessage”>The errorMessage that the
64: /// underlying bound data object will fill in</param>
65: /// <returns>True if the underlying bound object is Gvalid</returns>
66: private bool IsValid(string path, out string errorMessage)
67: {
68: errorMessage=((IDataErrorInfo)this.DataContext)[path];
69: return string.IsNullOrEmpty(errorMessage);
70: }
71:
72: /// <summary>
73: /// Exit, you could do something else if you wanted to
74: /// </summary>
75: private void btnCancel_Click(object sender, RoutedEventArgs e)
76: {
77: this.Close();
78: }
79:
80: }
81: }
就这样了,这会得到类似这样的结果
这里有一个 小型演示项目,如果你想获取源代码,可以下载。