【问题标题】:Comparing two Passwords in WPF MVVM比较 WPF MVVM 中的两个密码
【发布时间】:2017-11-03 18:50:25
【问题描述】:

我有一个 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 ); }
    }
}

最后,这是我的 Behavior 类。

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 属性。有人可以帮忙吗?我已经被困在这里一天了。谢谢。

【问题讨论】:

标签: wpf xaml mvvm


【解决方案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>

【讨论】:

  • 完美运行!谢谢你。我当然对“行为”的行为有了新的了解。
猜你喜欢
  • 2016-03-12
  • 2016-09-13
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 2019-09-12
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
相关资源
最近更新 更多