使用MVVM模式的密码框

20

大家好,我在使用MVVM模式,我有一个名为UserViewModel的ViewModel,其中有一个名为Password的属性。在View中有一个PasswordBox控件。

<PasswordBox x:Name="txtPassword" Password="{Binding Password}" />

但是这个XAML不起作用。你如何进行绑定??请帮帮我!!


你确定窗口的数据上下文已设置为视图模型了吗?你需要发布更多的代码,这样我们才能理解你的问题。 - Simon P Stevens
DataContext没问题。其他属性都正常工作,但是与PasswordBox一起使用时无法正常工作。 - Rangel
5个回答

13

出于安全原因,Password属性不是一个依赖属性,因此您无法将其绑定。不幸的是,您需要以旧式方式在代码后台执行绑定(注册OnPropertyChanged事件并通过代码更新值...)


一次快速搜索将我带到了这篇博客文章,其中展示了如何编写附加属性以规避此问题。然而,是否值得这样做,实际上取决于您对代码后台的厌恶程度。

4

您可以编写一个控件来包装密码,并添加一个依赖属性以获取密码属性。

我会直接使用代码,但如果必须的话,您可以这样做:

public class BindablePasswordBox : Decorator
{
    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(string), typeof(BindablePasswordBox));

    public string Password
    {
        get { return (string)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public BindablePasswordBox()
    {
        Child = new PasswordBox();
        ((PasswordBox)Child).PasswordChanged += BindablePasswordBox_PasswordChanged;
    }

    void BindablePasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        Password = ((PasswordBox)Child).Password;
    }

}

我使用类似的解决方案,但我发现使用FrameworkPropertyMetadata.BindsTwoWayByDefault注册DependencyProperty很有用,这样你就不必每次在XAML中声明它了。 - Taylor Leese
我知道这个方法相当老了,但是如果我使用它,就无法设置FontSize和ContentAlignment。我该怎么解决这个问题? - Ryan Searle

4

BindablePasswordBox存在一个问题。它只能在一方向上工作,从PasswordBox到PasswordProperty。下面是一个修改过的版本,可以双向工作。它注册了PropertyChangedCallback并在调用时更新PasswordBox的密码。 希望有人会觉得这很有用。

public class BindablePasswordBox : Decorator
{
    public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(string), typeof(BindablePasswordBox), new PropertyMetadata(string.Empty, OnDependencyPropertyChanged));
    public string Password
    {
        get { return (string)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    private static void OnDependencyPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        BindablePasswordBox p = source as BindablePasswordBox;
        if (p != null)
        {
            if (e.Property == PasswordProperty)
            {
                var pb = p.Child as PasswordBox;
                if (pb != null)
                {
                    if (pb.Password != p.Password)
                        pb.Password = p.Password;
                }
            }
        }
    }

    public BindablePasswordBox()
    {
        Child = new PasswordBox();
        ((PasswordBox)Child).PasswordChanged += BindablePasswordBox_PasswordChanged;
    }

    void BindablePasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        Password = ((PasswordBox)Child).Password;
    }
}

1
为了避免密码在任何时候以明文形式出现在内存中,我将该值作为参数提供给我的命令。
<Label>User Name</Label>
<TextBox Text="{Binding UserName}" />
<Label>Password</Label>
<PasswordBox Name="PasswordBox" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 16 0 0">
    <Button Margin="0 0 8 0" MinWidth="65" 
            Command="{Binding LoginAccept}" 
            CommandParameter="{Binding ElementName=PasswordBox}">
        Login
    </Button>
    <Button MinWidth="65" Command="{Binding LoginCancel}">Cancel</Button>
</StackPanel>

然后在我的视图模型中。
public DelegateCommand<object> LoginAccept { get; private set; }
public DelegateCommand<object> LoginCancel { get; private set; }

public LoginViewModel {
    LoginAccept = new DelegateCommand<object>(o => OnLogin(o), (o) => IsLoginVisible);
    LoginCancel = new DelegateCommand<object>(o => OnLoginCancel(), (o) => IsLoginVisible);
}

private void OnLogin(object o)
{
    var passwordBox = (o as System.Windows.Controls.PasswordBox);
    var password = passwordBox.SecurePassword.Copy();
    passwordBox.Clear();
    ShowLogin = false;
    var credential = new System.Net.NetworkCredential(UserName, password);
}

private void OnLoginCancel()
{
    ShowLogin = false;
}

虽然直接从绑定中提供SecurePassword是有道理的,但它似乎总是提供一个空值。因此,这种方法不起作用:
    <Button Margin="0 0 8 0" MinWidth="65" 
            Command="{Binding LoginAccept}" 
            CommandParameter="{Binding ElementName=PasswordBox, Path=SecurePassword}">

我经历过完全相同的事情!你知道为什么直接从绑定中使用“SecurePassword”不起作用吗? - Athafoud
1
除了特定类型有意限制之外,我没有其他想法。SecurePassword不是DependencyProperty,这可能导致问题,但它是一个标准的只读属性。但人们会认为,如果没有其他办法,仍然可以将其作为参数传递,而不是传递整个控件。 - Josh Brown
在视图模型中处理控件是MVVM模式的明显违规。 - BionicCode
是的,它违反了MVVM模式。框架中的错误有时需要打破模式。在这种情况下,安全在我的行业中占据主导地位,并且最好将密码加密在内存中。模式是为了编写易于理解的代码,即使更改语言和框架也是如此。在我看来,绑定和使用都很清晰。 - Josh Brown

0

请查看有关密码框的另一个线程。 最好不要将密码保存在任何 DP 或公共属性上。

密码框上的其他线程


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