Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Explicitly Updating And Validating Databindings In WPF

, 17 Jun 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
The other day I was working on something for a Codeproject article, where I needed to bind part of my UI to an underlying data object. I want to use all the good validation thing such as a Validation Style to use for my TextBox, and also the use of the new .NET 3.5 interface [...]

The other day I was working on something for a Codeproject article, where I needed to bind part of my UI to an underlying data object. I want to use all the good validation thing such as a Validation Style to use for my TextBox, and also the use of the new .NET 3.5 interface IDataErrorInfo.

So that was fine. But I also wanted to allow the user to either apply the changes or cancel them. When the user chooses to apply the changes, the changes should be made Explicitly to the underlying data object, and ONLY update database if the underlying data object is in a valid state.

So how do we do that. Well the first thing to do is make sure we have a data object that provides validation using the .NET 3.5 interface 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:          /// <span class="code-SummaryComment"><summary></span>
  30:          /// Return the full list of validation 
  31:          /// errors for this object
  32:          /// <span class="code-SummaryComment"></summary></span>
  33:          public string Error
  34:          {
  35:              get
  36:              {
  37:                  return combinedError.ToString();
  38:              }
  39:          }
  40:  
  41:          /// <span class="code-SummaryComment"><summary></span>
  42:          /// Validates a particular column, and returns a 
  43:          /// string representing the current error
  44:          /// <span class="code-SummaryComment"></summary></span>
  45:          /// <span class="code-SummaryComment"><param name=”columnName”>The property name to </span>
  46:          /// validate<span class="code-SummaryComment"></param></span>
  47:          /// <span class="code-SummaryComment"><returns>A string representing the </span>
  48:          /// current error<span class="code-SummaryComment"></returns></span>
  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:  }

Then we need to create some items that will use these bindings (only “Age” in this simple case)

   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>

Also notice that within this XAML is a Style that is used by the bound TextBox. This Style creates a red rectangle around the bound TextBox and the appropriate tooltip, when the bound object is in an invalid state (basically when Validation.HasError is true.

Also notice that because part of my requirements, was to be able to choose to update the underlying object or cancel any changes, I am using the “UpdateSourceTrigger=Explicit” within the Binding expression.

So as you can probably imagine, the last part is to do the code behind, where we actually do the binding update Explicitly (manually). So let’s see that shall we

   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:      /// <span class="code-SummaryComment"><summary></span>
  19:      /// Interaction logic for Window1.xaml
  20:      /// <span class="code-SummaryComment"></summary></span>
  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:          /// <span class="code-SummaryComment"><summary></span>
  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:          /// <span class="code-SummaryComment"></summary></span>
  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:          /// <span class="code-SummaryComment"><summary></span>
  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:          /// <span class="code-SummaryComment"></summary></span>
  62:          /// <span class="code-SummaryComment"><param name=”path”>The property to validate</param></span>
  63:          /// <span class="code-SummaryComment"><param name=”errorMessage”>The errorMessage that the</span>
  64:          /// unlerlying bound data object will fill in<span class="code-SummaryComment"></param></span>
  65:          /// <span class="code-SummaryComment"><returns>True if the underlying bound object is valid</returns></span>
  66:          private bool IsValid(string path, out string errorMessage)
  67:          {
  68:              errorMessage=((IDataErrorInfo)this.DataContext)[path];
  69:              return string.IsNullOrEmpty(errorMessage);
  70:          }
  71:  
  72:          /// <span class="code-SummaryComment"><summary></span>
  73:          /// Exit, you could do something else if you wanted to
  74:          /// <span class="code-SummaryComment"></summary></span>
  75:          private void btnCancel_Click(object sender, RoutedEventArgs e)
  76:          {
  77:              this.Close();
  78:          }
  79:  
  80:      }
  81:  }

And that’s it, which gives us this sort of thing

db-thumb.png

Here is a small demo project should you wish to have the source code : bindingexplicit.zip

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionThank you, Very good. Pinmembertal_segal2-Oct-13 21:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141216.1 | Last Updated 17 Jun 2009
Article Copyright 2009 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid