65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2009年6月17日

CPOL

1分钟阅读

viewsIcon

40197

downloadIcon

287

如何在 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.HasErrortrue 时),此 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:  }

就这样了,这会得到类似这样的结果

37356/db-thumb.png

这里有一个 小型演示项目,如果你想获取源代码,可以下载。

© . All rights reserved.