title: Validate Password and Confirm Password
body: <p>I have a WPF form which has following controls -</p>
  
  <p>Name - TextBox</p>
  
  <p>Password - PasswordBox</p>
  
  <p>Confirm Password - PasswordBox</p>
  
  <p>Save - Button</p>
  
  <p>What is want is - Enable save button only if all controls have valid value</p>
  
  <p>Name should not be empty - To validate this i am using custom ValidationRule</p>
  
  <p>Password should not be empty - To validate this i am using custom ValidationRule</p>
  
  <p>Password and Confirm password should match.</p>
  
  <p>I am using ValidationRule because it will not update source in case of error. [I cannot use IDataInfoError].</p>
  
  <p>I am not able validate Password and Confirm Password match condition. Below is my XAML and Code behind -</p>
  
  <pre><code>&lt;Window x:Class="WPFPasswordValidation.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:WPFPasswordValidation"
      Title="MainWindow" Height="350" Width="525"&gt;
  &lt;Window.Resources&gt;
      &lt;ResourceDictionary&gt;            
          &lt;ControlTemplate x:Key="validationTemplate"&gt;
              &lt;DockPanel&gt;
                  &lt;Border BorderBrush="Red" BorderThickness="1"&gt;
                      &lt;AdornedElementPlaceholder/&gt;
                  &lt;/Border&gt;
              &lt;/DockPanel&gt;
          &lt;/ControlTemplate&gt;
          &lt;Style x:Key="textBoxInError" TargetType="{x:Type TextBox}"&gt;
              &lt;Style.Triggers&gt;
                  &lt;Trigger Property="Validation.HasError" Value="true"&gt;
                      &lt;Setter Property="ToolTip"
            Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}"/&gt;
                  &lt;/Trigger&gt;
              &lt;/Style.Triggers&gt;
          &lt;/Style&gt;
          &lt;Style x:Key="passwordBoxInError" TargetType="{x:Type PasswordBox}"&gt;
              &lt;Style.Triggers&gt;
                  &lt;Trigger Property="Validation.HasError" Value="true"&gt;
                      &lt;Setter Property="ToolTip"
            Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}"/&gt;
                  &lt;/Trigger&gt;
              &lt;/Style.Triggers&gt;
          &lt;/Style&gt;
      &lt;/ResourceDictionary&gt;
  
  &lt;/Window.Resources&gt;
  &lt;Grid&gt;
      &lt;TextBox Name="txtName" HorizontalAlignment="Left" Height="23" Margin="155,31,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Validation.Error="Validation_Error" Style="{StaticResource textBoxInError}" Validation.ErrorTemplate="{StaticResource validationTemplate}"&gt;
          &lt;TextBox.Text&gt;
              &lt;Binding Path="Name" UpdateSourceTrigger="Explicit" NotifyOnValidationError="true" Mode="TwoWay"&gt;
                  &lt;Binding.ValidationRules&gt;
                      &lt;local:StringRangeValidationRule MinimumLength="1" MaximumLength="30" 
                                      ErrorMessage="Name is required and must be less than 30 letters."/&gt;
                  &lt;/Binding.ValidationRules&gt;
              &lt;/Binding&gt;
          &lt;/TextBox.Text&gt;
      &lt;/TextBox&gt;
      &lt;PasswordBox HorizontalAlignment="Left" Margin="155,128,0,0" VerticalAlignment="Top" Width="120" Name="txtConfirmPass"/&gt;
      &lt;TextBlock HorizontalAlignment="Left" Margin="106,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top"&gt;&lt;Run Text="Name"/&gt;&lt;/TextBlock&gt;
      &lt;PasswordBox Name="txtPassword" HorizontalAlignment="Left" Margin="155,85,0,0" VerticalAlignment="Top" Width="120" PasswordChar="*" local:PasswordBoxAssistant.BindPassword="true" Validation.Error="Validation_Error" Style="{StaticResource passwordBoxInError}" Validation.ErrorTemplate="{StaticResource validationTemplate}"&gt;
          &lt;local:PasswordBoxAssistant.BoundPassword&gt;
              &lt;Binding Path="Password" Mode="TwoWay" UpdateSourceTrigger="Explicit" NotifyOnValidationError="true"&gt;
                  &lt;Binding.ValidationRules&gt;
                      &lt;local:StringRangeValidationRule MinimumLength="1" MaximumLength="30" 
                                      ErrorMessage="Password is required and must be less than 30 letters."/&gt;
                  &lt;/Binding.ValidationRules&gt;
              &lt;/Binding&gt;
          &lt;/local:PasswordBoxAssistant.BoundPassword&gt;
      &lt;/PasswordBox&gt;
      &lt;TextBlock HorizontalAlignment="Left" Margin="88,85,0,0" TextWrapping="Wrap" Text="Password" VerticalAlignment="Top"/&gt;
      &lt;TextBlock HorizontalAlignment="Left" Margin="42,128,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="20"&gt;&lt;Run Text="Confirm Password"/&gt;&lt;/TextBlock&gt;
      &lt;Button Content="Save" HorizontalAlignment="Left" Margin="169,186,0,0" VerticalAlignment="Top" Width="75" Command="Save"&gt;
          &lt;Button.CommandBindings&gt;
              &lt;CommandBinding Command="Save" Executed="Save_Executed" CanExecute="Save_CanExecute"/&gt;
          &lt;/Button.CommandBindings&gt;
      &lt;/Button&gt;
  &lt;/Grid&gt;
  </code></pre>
  
  <p></p>
  
  <p>Code behind C# -</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Data;
  using System.Windows.Documents;
  using System.Windows.Input;
  using System.Windows.Media;
  using System.Windows.Media.Imaging;
  using System.Windows.Navigation;
  using System.Windows.Shapes;
  using System.Globalization;
  
  namespace WPFPasswordValidation
  {
  /// &lt;summary&gt;
  /// Interaction logic for MainWindow.xaml
  /// &lt;/summary&gt;
  public partial class MainWindow : Window
  {
      private int _noOfErrors = 0;
  
      BindingExpression be = null;
      public MainWindow()
      {
          InitializeComponent();
  
          Person p = new Person();
          p.Name = "abc";            
          p.Password = "1234";
          this.DataContext = p;
  
      }
  
      private void Validation_Error(object sender, ValidationErrorEventArgs e)
      {
          if (e.Action == ValidationErrorEventAction.Added)
              _noOfErrors++;
          else
              _noOfErrors--;
      }
  
      private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
      {
  
      }
  
      private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
      {
  
          be = this.txtName.GetBindingExpression(TextBox.TextProperty);
          be.UpdateSource();
  
          be = this.txtPassword.GetBindingExpression(PasswordBoxAssistant.BoundPassword);
          be.UpdateSource();            
  
          e.CanExecute = _noOfErrors == 0;
          e.Handled = true;
      }
  }
  
  public class StringRangeValidationRule : ValidationRule
  {
      private int _minimumLength = -1;
      private int _maximumLength = -1;
      private string _errorMessage;
  
      public int MinimumLength
      {
          get { return _minimumLength; }
          set { _minimumLength = value; }
      }
  
      public int MaximumLength
      {
          get { return _maximumLength; }
          set { _maximumLength = value; }
      }
  
      public string ErrorMessage
      {
          get { return _errorMessage; }
          set { _errorMessage = value; }
      }
  
      public override ValidationResult Validate(object value,
          CultureInfo cultureInfo)
      {
          ValidationResult result = new ValidationResult(true, null);
          string inputString = (value ?? string.Empty).ToString();
          if (inputString == null)
          {
              inputString = string.Empty;
          }
  
          if (inputString.Length &lt; this.MinimumLength ||
                 (this.MaximumLength &gt; 0 &amp;&amp;
                  inputString.Length &gt; this.MaximumLength))
          {
              result = new ValidationResult(false, this.ErrorMessage);
          }
          return result;
      }
  }
  
  public static class PasswordBoxAssistant
  {
      public static readonly DependencyProperty BoundPassword =
          DependencyProperty.RegisterAttached("BoundPassword", typeof(string), typeof(PasswordBoxAssistant), new PropertyMetadata(string.Empty, OnBoundPasswordChanged));
  
      public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached(
          "BindPassword", typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged));
  
      private static readonly DependencyProperty UpdatingPassword =
          DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false));
  
      private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          PasswordBox box = d as PasswordBox;
  
          // only handle this event when the property is attached to a PasswordBox
          // and when the BindPassword attached property has been set to true
          if (d == null || !GetBindPassword(d))
          {
              return;
          }
  
          // avoid recursive updating by ignoring the box's changed event
          box.PasswordChanged -= HandlePasswordChanged;
  
          string newPassword = (string)e.NewValue;
  
          if (!GetUpdatingPassword(box))
          {
              box.Password = newPassword;
          }
  
          box.PasswordChanged += HandlePasswordChanged;
      }
  
      private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
      {
          // when the BindPassword attached property is set on a PasswordBox,
          // start listening to its PasswordChanged event
  
          PasswordBox box = dp as PasswordBox;
  
          if (box == null)
          {
              return;
          }
  
          bool wasBound = (bool)(e.OldValue);
          bool needToBind = (bool)(e.NewValue);
  
          if (wasBound)
          {
              box.PasswordChanged -= HandlePasswordChanged;
          }
  
          if (needToBind)
          {
              box.PasswordChanged += HandlePasswordChanged;
          }
      }
  
      private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
      {
          PasswordBox box = sender as PasswordBox;
  
          // set a flag to indicate that we're updating the password
          SetUpdatingPassword(box, true);
          // push the new password into the BoundPassword property
          SetBoundPassword(box, box.Password);
          SetUpdatingPassword(box, false);
      }
  
      public static void SetBindPassword(DependencyObject dp, bool value)
      {
          dp.SetValue(BindPassword, value);
      }
  
      public static bool GetBindPassword(DependencyObject dp)
      {
          return (bool)dp.GetValue(BindPassword);
      }
  
      public static string GetBoundPassword(DependencyObject dp)
      {
          return (string)dp.GetValue(BoundPassword);
      }
  
      public static void SetBoundPassword(DependencyObject dp, string value)
      {
          dp.SetValue(BoundPassword, value);
      }
  
      private static bool GetUpdatingPassword(DependencyObject dp)
      {
          return (bool)dp.GetValue(UpdatingPassword);
      }
  
      private static void SetUpdatingPassword(DependencyObject dp, bool value)
      {
          dp.SetValue(UpdatingPassword, value);
      }
  }
  
  public class Person
  {
      private string _name;        
      private string _password;               
  
      public string Password
      {
          get { return _password; }
          set { _password = value; }
      }
  
      public string Name
      {
          get { return _name; }
          set
          {
              _name = value;                
          }
      }        
  }
  </code></pre>
  
  <p>}</p>