如何验证WPF中的PasswordBox

14
我正在尝试为PasswordBox创建验证。为了进行验证,我遵循了link中所示的方法,该方法展示了如何在TextBox上进行验证。
问题出现在PasswordBoxes上。由于安全原因,它的Password属性不可绑定,因此我尝试按照this link(对于CodeProject用户,在here中也有说明)来进行绑定。
看起来非常棒!我可以将PasswordBox与其Password属性绑定,然后可以进行验证。但是它却不理我...
这是我使用且正常工作的普通TextBox
<local:ErrorProvider Grid.Column="1" Grid.Row="2" >
    <TextBox Width="160" 
          HorizontalAlignment="Left" 
           Name="textBoxUserPass" 
           Text="{Binding Path=Password, UpdateSourceTrigger=Explicit}" />
 </local:ErrorProvider>

这是我尝试模拟的 PasswordBox

<local:ErrorProvider Grid.Column="1" Grid.Row="2" >
      <PasswordBox Width="160"
          HorizontalAlignment="Left"
          Name="textBoxUserPass"
          local:PasswordBoxAssistant.BindPassword="True"
          local:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, UpdateSourceTrigger=Explicit}" />
 </local:ErrorProvider>

这是我获取每个TextBoxBindingExpression的方法:

BindingExpression beUserName = textBoxUserName.GetBindingExpression(TextBox.TextProperty);
if (beUserName != null) beUserName.UpdateSource();

这就是我为PasswordBox获取它的方式:

BindingExpression bePassword = textBoxUserPass.GetBindingExpression(PasswordBoxAssistant.BoundPassword);
if (bePassword != null) bePassword.UpdateSource();

如果在我的验证类中定义了任何错误,当我执行以下操作时:
if (!beUserName.HasError && !bePassword.HasError)

每个BindingExpression应该根据错误验证的情况说出true或false。但是对于我的PasswordBox,从未收到值...有什么想法吗?

你尝试过在绑定上设置 ValidatesOnDataErrors=TrueValidatesOnExceptions=True 吗? - Richard Deeming
它对于“PasswordBox”不存在... - Sonhja
它适用于BoundPassword附加属性:local:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, UpdateSourceTrigger=Explicit, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" - Richard Deeming
真的,它有效了!你能否发布答案,因为这是此情况的确切解决方案? - Sonhja
3
大家好像都在使用的 PasswordBoxAssistant 在哪里? - James
4
PasswordBoxAssistant: http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html密码框助手: http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html - Charlie
4个回答

12

尝试在绑定上设置ValidatesOnDataErrors=TrueValidatesOnExceptions=True:

<PasswordBox ...
   local:PasswordBoxAssistant.BoundPassword="{Binding Path=Password,
      UpdateSourceTrigger=Explicit, 
      ValidatesOnDataErrors=True, 
      ValidatesOnExceptions=True}"
/>

3

在你的绑定上设置Mode=TwoWay

local:PasswordBoxAssistant.BoundPassword="{Binding Path=Password,Mode=TwoWay,
UpdateSourceTrigger=Explicit}"

2

另一种解决方案,不使用任何“不安全”的字符串,是调整Window代码,类似于这样:

假设我有一个像下面这样的MVVM对象,使用WPF验证和IDataErrorInfo

public class MyObject : INotifyPropertyChanged, IDataErrorInfo
{
    ...
    public SecureString SecurePassword
    {
        get
        { ... }
        set
        {
            ...
            OnPropertyChanged("SecurePassword");
        }
    }

    ...

    string IDataErrorInfo.Error { get { return Validate(null); } }
    string IDataErrorInfo.this[string columnName] { get { return Validate(columnName); } }

    private string Validate(string memberName)
    {
        string error = null;
        ...
        if (memberName == "SecurePassword" || memberName == null)
        {
            // this is where I code my custom business rule
            if (SecurePassword == null || SecurePassword.Length == 0)
            {
                error = "Password must be specified.";
            }
        }
        ...
        return error;
    }

}

有一个类似这样的带有PasswordBox的Window Xaml:

<PasswordBox Name="MyPassword" PasswordChanged="MyPassword_Changed" ... />

那么,相应的窗口代码将会触发PasswordBox绑定,如下所示:
// add a custom DependencyProperty
public static readonly DependencyProperty SecurePasswordProperty =
    DependencyProperty.RegisterAttached("SecurePassword", typeof(SecureString), typeof(MyWindow));

public MyWindow()
{
    InitializeComponent();

    DataContext = myObject; // created somewhere

    // create a binding by code
    Binding passwordBinding = new Binding(SecurePasswordProperty.Name);
    passwordBinding.Source = myObject;
    passwordBinding.ValidatesOnDataErrors = true;
    // you can configure other binding stuff here
    MyPassword.SetBinding(SecurePasswordProperty, passwordBinding);
}

private void MyPassword_Changed(object sender, RoutedEventArgs e)
{
    // this should trigger binding and therefore validation
    ((MyObject)DataContext).SecurePassword = MyPassword.SecurePassword;
}

1
我真的希望人们意识到PasswordBox本身是不安全的,因为它会愉快地为任何请求解密密码的应用程序解密密码。只需附加到进程,并通过反射说:“嘿,PasswordBox,密码属性里有什么?”我知道你可以通过避免将其存储在字符串字段中来获得一些内存抓取保护,但对于大多数用例而言,如果任何恶意负载已被安装,游戏已经结束了-也许不值得担心。 - Daniel
@Daniel - SecureString并不能保护免受实时调试器或类似的实时威胁。有关已处理威胁的良好解释在此处:https://blogs.msdn.microsoft.com/shawnfa/2004/05/27/making-strings-more-secure/(还要确保您阅读Shawn的评论)。 - Simon Mourier

2
据我所记,给PasswordBox添加验证的唯一方法是在SecurePassword的绑定属性的setter中抛出新的ValidationException。PasswordBoxAssistant不能帮助您完成此操作。

能否提供一个示例或链接?这听起来像是一个解决方案。 - Sonhja
这是我在一款旧应用程序中发现的代码:public SecureString NewPassword { get { return _newPassword; } set { _newPassword = value; string error = IsPasswordValid();
if (!string.IsNullOrEmpty(error)) { throw new ValidationException(error); } } }
- Cristian Chereches
嗯,我会尝试一下,但确切的解决方案是Richard Deeming发布的那个(对于这种情况)。但我也会尝试这个,因为我在我的代码中也有这个特定的情况。 - Sonhja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接