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