I have a WPF application and am using MVVM pattern. On one of my User Controls, I have two PasswordBoxes to compare the user entered passwords. I am trying to implement a compare behavior whose result will determine if the submit button should be enabled or disabled in the ViewModel. I am kinda stuck.
EDIT: This is not a duplicate question as @Dbl mentioned in a comment. The duplicate question mentioned in his comment is about how to compare two SecureString data types. My question is totally different. It is about how to compare two object values - does not matter if they are SecureString or not - in a XAML UserControl without breaking the MVVM pattern where a behavior attached to one element needs to know about the value of another element inside the behavior. Also, this behavior needs to be able to access the underlying ViewModel of the the element and update an INPC property in the ViewModel.
Here is my XAML (removed quite a bit of elements for brevity):
<UserControl
x:Class="DynaProPOS.WPF.Views.AppUser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behavior="clr-namespace:DynaProPOS.WPF.Behaviors"
xmlns:custProps="clr-namespace:DynaProPOS.WPF.CustomProperties"
prism:ViewModelLocator.AutoWireViewModel="True"
Background="{DynamicResource BackgroundBrush}">
<Border Width="750" Height="260" BorderBrush="White" BorderThickness="2">
<Grid x:Name="grid" KeyboardNavigation.TabNavigation="Cycle" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto">
<PasswordBox TabIndex="3" Grid.Row="3" Grid.Column="1" Margin="2" x:Name="Password1" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<behavior:PasswordBoxBindingBehavior Password="{Binding Password}" />
</i:Interaction.Behaviors>
</PasswordBox>
<PasswordBox TabIndex="4" Grid.Row="4" Grid.Column="1" Margin="2,18,2,4" x:Name="Password2" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<behavior:ComparePasswordBehavior OriginalPassword="{Binding ElementName=Password1, Path=Password}"/>
</i:Interaction.Behaviors>
</PasswordBox>
<Grid Grid.Column="3" Grid.RowSpan="5" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="90*" />
</Grid.RowDefinitions>
</Grid>
<syncfusion:ButtonAdv TabIndex="6" x:Name="RegisterButton" Grid.Row="5" Grid.Column="4" Margin="5" HorizontalAlignment="Right" Label="Submit" VerticalAlignment="Center" />
</Grid>
</Border>
And Here is my ViewModel (again, remove lot of code for brevity).
public class AppUserViewModel : BindableBase
{
private bool isEnabled;
public AppUserViewModel(IAuthenticationService _authService)
{
authService = _authService;
RegisterCommand = new DelegateCommand( async () => await RegisterUserAsync() );
}
public bool IsEnabled
{
get { return isEnabled; }
set { SetProperty( ref isEnabled, value ); }
}
}
And finally, here is my Behavior class.
public class ComparePasswordBehavior : Behavior<PasswordBox>
{
protected override void OnAttached()
{
AssociatedObject.LostFocus += OnComparePasswordLostFocus;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.LostFocus -= OnComparePasswordLostFocus;
base.OnDetaching();
}
public static readonly DependencyProperty OriginalPasswordProperty =
DependencyProperty.Register("OriginalPassword", typeof(SecureString), typeof(ComparePasswordBehavior), new PropertyMetadata(null));
private static void OnComparePasswordLostFocus( object sender, RoutedEventArgs e )
{
PasswordBox pswdBox = sender as PasswordBox;
var behavior = Interaction.GetBehaviors(pswdBox).OfType<ComparePasswordBehavior>().FirstOrDefault();
if (behavior != null)
{
var binding = BindingOperations.GetBindingExpression( behavior, OriginalPasswordProperty);
PropertyInfo propInfo = binding.DataItem.GetType().GetProperty(binding.ParentBinding.Path.Path);
// at this point I am stumped. I don't seems to be able to
// retrieve the value from the original password box element.
// I am also not able to set the IsEnabled property of the ViewModel.
}
}
public SecureString OriginalPassword
{
get { return ( SecureString )GetValue( OriginalPasswordProperty ); }
set { SetValue( OriginalPasswordProperty, ( SecureString )value ); }
}
}
I have a dependency property defined in my behavior to hold the password value from the original password box. In the lostfocus event of my behavior, I need to compare the two passwords and set the IsEnabled Property of my ViewModel accordingly.
I need to do two things here. I need to retrieve the Password value from Password1 PasswordBox Element I also need to set the IsEnabled Property of my ViewModel based on the password comparison result. Can somebody please help? I have been stuck here for a day now. Thanks.