比较WPF MVVM中的两个密码

时间:2017-11-03 18:50:25

标签: wpf xaml mvvm

我有一个WPF应用程序并使用MVVM模式。在我的一个用户控件上,我有两个PasswordBox来比较用户输入的密码。我正在尝试实现比较行为,其结果将确定是否应在ViewModel中启用或禁用提交按钮。我有点卡住了。

修改 这不是@Dbl在评论中提到的重复问题。他的评论中提到的重复问题是关于如何比较两个SecureString数据类型。我的问题完全不同。它是关于如何在XAML UserControl中比较两个对象值 - 无论它们是否为SecureString - 而不破坏MVVM模式,其中附加到一个元素的行为需要知道行为内另一个元素的值。此外,此行为需要能够访问元素的基础ViewModel并更新ViewModel中的INPC属性。

这是我的XAML(为简洁起见,删除了相当多的元素):

<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>

这是我的ViewModel(为了简洁,再次删除大量代码)。

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 ); }
    }
}

最后,这是我的行为类。

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 ); }
    }
}

我的行为中定义了依赖项属性来保存原始密码框中的密码值。在我的行为的lostfocus事件中,我需要比较两个密码并相应地设置我的ViewModel的IsEnabled属性。

我需要在这做两件事。我需要从Password1 PasswordBox元素中检索密码值 我还需要根据密码比较结果设置我的ViewModel的IsEnabled属性。有人可以帮忙吗?我已经被困在这里一天了。感谢。

1 个答案:

答案 0 :(得分:1)

ComparePasswordBehavior的实例对PasswordBoxBindingBehavior的实例一无所知,反之亦然。此外,视图模型的可靠性是比较密码并设置IsEnabled属性。

行为应该只是将密码从PasswordBox转移到视图模型。您应该将SecureStrings存储在视图模型中并在那里进行比较。

请参阅以下示例代码。

<强>行为:

public class PasswordBehavior : Behavior<PasswordBox>
{
    protected override void OnAttached()
    {
        AssociatedObject.LostFocus += OnComparePasswordLostFocus;
        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.LostFocus -= OnComparePasswordLostFocus;
        base.OnDetaching();
    }

    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(SecureString), typeof(PasswordBehavior), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });


    public SecureString Password
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    private static void OnComparePasswordLostFocus(object sender, RoutedEventArgs e)
    {
        PasswordBox pswdBox = sender as PasswordBox;
        PasswordBehavior behavior = Interaction.GetBehaviors(pswdBox).OfType<PasswordBehavior>().FirstOrDefault();
        if (behavior != null)
        {
            behavior.Password = pswdBox.SecurePassword;
        }
    }
}

查看型号:

public class AppUserViewModel : BindableBase
{
    private bool isEnabled;
    public bool IsEnabled
    {
        get { return isEnabled; }
        set { SetProperty(ref isEnabled, value); }
    }

    private SecureString _password1;
    public SecureString Password1
    {
        get { return _password1; }
        set
        {
            if (SetProperty(ref _password1, value))
                ComparePasswords();
        }
    }

    private SecureString _password2;
    public SecureString Password2
    {
        get { return _password2; }
        set
        {
            if (SetProperty(ref _password2, value))
                ComparePasswords();
        }
    }

    private void ComparePasswords()
    {
        IsEnabled = (_password1 != null || _password2 != null) 
            && SecureStringToString(_password1) == SecureStringToString(_password2);
    }

    private string SecureStringToString(SecureString value)
    {
        IntPtr valuePtr = IntPtr.Zero;
        try
        {
            valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
            return Marshal.PtrToStringUni(valuePtr);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
        }
    }
}

查看:

<PasswordBox>
    <i:Interaction.Behaviors>
        <behavior:PasswordBehavior Password="{Binding Password1}" />
    </i:Interaction.Behaviors>
</PasswordBox>
<PasswordBox>
    <i:Interaction.Behaviors>
        <behavior:PasswordBehavior Password="{Binding Password2}"/>
    </i:Interaction.Behaviors>
</PasswordBox>