Hi,
I have a WPF form which has following controls -
Name - TextBox
Password - PasswordBox
Confirm Password - PasswordBox
Save - Button
What is want is - Enable save button only if all controls have valid value
Name should not be empty - To validate this i am using custom ValidationRule
Password should not be empty - To validate this i am using custom ValidationRule
Password and Confirm password should match.
I am using ValidationRule because it will not update source in case of error. [I cannot use IDataInfoError].
I am not able validate Password and Confirm Password match condition. Below is my XAML and Code behind -
XAML is -
<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"><Window.Resources><ResourceDictionary> <ControlTemplate x:Key="validationTemplate"><DockPanel><Border BorderBrush="Red" BorderThickness="1"><AdornedElementPlaceholder/></Border></DockPanel></ControlTemplate><Style x:Key="textBoxInError" TargetType="{x:Type TextBox}"><Style.Triggers><Trigger Property="Validation.HasError" Value="true"><Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/></Trigger></Style.Triggers></Style><Style x:Key="passwordBoxInError" TargetType="{x:Type PasswordBox}"><Style.Triggers><Trigger Property="Validation.HasError" Value="true"><Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/></Trigger></Style.Triggers></Style></ResourceDictionary></Window.Resources><Grid><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}"><TextBox.Text><Binding Path="Name" UpdateSourceTrigger="Explicit" NotifyOnValidationError="true" Mode="TwoWay"><Binding.ValidationRules><local:StringRangeValidationRule MinimumLength="1" MaximumLength="30" ErrorMessage="Name is required and must be less than 30 letters."/></Binding.ValidationRules></Binding></TextBox.Text></TextBox><PasswordBox HorizontalAlignment="Left" Margin="155,128,0,0" VerticalAlignment="Top" Width="120" Name="txtConfirmPass"/><TextBlock HorizontalAlignment="Left" Margin="106,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top"><Run Text="Name"/></TextBlock><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}"><local:PasswordBoxAssistant.BoundPassword><Binding Path="Password" Mode="TwoWay" UpdateSourceTrigger="Explicit" NotifyOnValidationError="true"><Binding.ValidationRules><local:StringRangeValidationRule MinimumLength="1" MaximumLength="30" ErrorMessage="Password is required and must be less than 30 letters."/></Binding.ValidationRules></Binding></local:PasswordBoxAssistant.BoundPassword></PasswordBox><TextBlock HorizontalAlignment="Left" Margin="88,85,0,0" TextWrapping="Wrap" Text="Password" VerticalAlignment="Top"/><TextBlock HorizontalAlignment="Left" Margin="42,128,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="20"><Run Text="Confirm Password"/></TextBlock><Button Content="Save" HorizontalAlignment="Left" Margin="169,186,0,0" VerticalAlignment="Top" Width="75" Command="Save"><Button.CommandBindings><CommandBinding Command="Save" Executed="Save_Executed" CanExecute="Save_CanExecute"/></Button.CommandBindings></Button></Grid></Window>
Code behind C# -
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 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> 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 < this.MinimumLength || (this.MaximumLength > 0 && inputString.Length > 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; } } } }
Could you please help me how to achieve above requirement? Any help would be highly appreciable.
ITChamp