我的 WPF PasswordBox 有什么问题?

What is wrong with my WPF PasswordBox?

我有一个登录视图,其中 PasswordBox 标记如下:

<PasswordBox x:Name="PasswordText" Width="Auto" Margin="5" PasswordChanged="PasswordTextOnPasswordChanged" />

它的问题是插入符仍然在文本框的最左边;它不会在您键入时向前移动,因此您键入的最后一个字符将成为文本框中的第一个字符。例如。当我输入密码“123”时,PasswordBox的内容转换为普通字符串后是“321”。它不是转换代码,因为如果我用密码预先填充控件,即不涉及输入,它的内容将转换为正确的纯字符串。

这是怎么回事?对我来说插入符号不跟随输入似乎有问题。

幕后(即代码隐藏)我有:

private void PasswordTextOnPasswordChanged(object sender, RoutedEventArgs e)
{
    if (!_suppressPasswordChanged)
    {
        ((LoginFormViewModel)DataContext).Password = PasswordText.SecurePassword;
    }
}

_suppressPasswordChanged 只是为了避免在我明确设置密码时触发 PasswordTextOnPasswordChanged 处理程序。

public void SetPassword(string password)
{
    try
    {
        _suppressPasswordChanged = true;
        PasswordText.Password = password;
    }
    finally
    {
        _suppressPasswordChanged = false;
    }
}

A PasswordBox 没有数据绑定,因此需要视图中的代码在用户在该框中键入内容时更新视图模型。我最初有这个事件处理程序:

private void PasswordTextOnPasswordChanged(object sender, RoutedEventArgs e)
{
    ((LoginFormViewModel)DataContext).Password = PasswordText.SecurePassword;
}

然后我希望能够在视图模型中设置Password 属性,并且不得不添加这个视图方法:

public void SetPassword(string password)
{
    PasswordText.Password = password;
}

从 setter 调用:

set
{
    if (value == _password) return;
    _password = value;
    ((LoginFormPopup)View).SetPassword(_password.ToClearString());
    OnPropertyChanged();
}

这以 SetPassword 触发 PasswordTextOnPasswordChanged 结束,它再次调用 setter,正常情况下应该如此,但在代码递归触发时不会。我为防止这些方法相互调用而进行的第一次令人尴尬的尝试是:

private bool suppressPasswordChanged;
private void PasswordTextOnPasswordChanged(object sender, RoutedEventArgs e)
{
    if (!suppressPasswordChanged)
    {
        ((LoginFormViewModel)DataContext).Password = PasswordText.SecurePassword;
    }
}

public void SetPassword(string password)
{
    try
    {
        suppressPasswordChanged = true;
        PasswordText.Password = password;
    }
    finally
    {
        suppressPasswordChanged = false;
    }
}

但是因为 SetPassword 总是在其 finally 中重置 suppressPasswordChanged,它仍然总是会从 PasswordTextOnPasswordChanged.

调用

我目前有效但可能有点臭的解决方案是将设置 'do-not-call' 标志的责任移交给事件处理程序:

private bool _textChangedEventBusy;
private void PasswordTextOnPasswordChanged(object sender, RoutedEventArgs e)
{
    try
    {
        _textChangedEventBusy = true;
        ((LoginFormViewModel)DataContext).Password = PasswordText.SecurePassword;
    }
    finally
    {
        _textChangedEventBusy = false;
    }
}

public void SetPassword(string password)
{
    if (!_textChangedEventBusy)
    {
        PasswordText.Password = password;
    }
}

现在,SetPassword 只会从视图模型中的 setter 调用,然后将插入符号设置到文本的开头是理想的。