比较WPF MVVM中的两个密码

比较WPF MVVM中的两个密码,wpf,xaml,mvvm,Wpf,Xaml,Mvvm,我有一个WPF应用程序,正在使用MVVM模式。在我的一个用户控件上,我有两个密码框来比较用户输入的密码。我正在尝试实现一种比较行为,其结果将决定是否应在ViewModel中启用或禁用submit按钮。我有点困了 编辑: 这不是评论中提到的@Dbl的重复问题。他在评论中提到的重复问题是关于如何比较两种SecureString数据类型。我的问题完全不同。它是关于如何在XAML UserControl中比较两个对象值——不管它们是否为SecureString——而不破坏MVVM模式,其中附加到一个元素

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

编辑: 这不是评论中提到的@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>
最后,这是我的行为课

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

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

该行为应该只是将密码从PasswordBox传输到视图模型。您应该将SecureStrings存储在视图模型中,并在其中进行比较

请参考以下示例代码

行为:

视图:


可能重复的“不要删除你的问题”虽然-在某种程度上,我希望这个问题击中更多的访问从搜索引擎完美地工作!非常感谢。我确实学到了一些新的行为的行为。
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 ); }
    }
}
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>