PasswordBox с MVVM

Привет, люди stackoverflow. Я работаю с MVVM, у меня есть ViewModel вызов UserViewModel с помощью пароля свойства. В Вид есть элемент управления PasswordBox.

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

Но этот xaml не работает. Как вы делаете привязку? Помоги пожалуйста!!

Ответ 1

По соображениям безопасности свойство Пароль не является свойством зависимостей, поэтому вы не можете привязываться к нему. К сожалению, вам нужно будет выполнить привязку в коде по старомодному способу (зарегистрироваться для события OnPropertyChanged и обновить значение через код...)


Быстрый поиск приводит меня к этому сообщению в блоге, в котором показано, как написать прикрепленное свойство, чтобы обойти проблему. Стоит ли это делать или нет, но действительно зависит от вашего отвращения к кодовомузади.

Ответ 2

Вы всегда можете написать элемент управления, который обертывает пароль и добавляет свойство зависимостей для свойства Password.

Я бы просто использовал код позади, но если вы должны сделать что-то вроде:

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;
    }

}

Ответ 3

Возникает проблема с 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;
    }
}

Ответ 4

Чтобы избежать использования пароля в памяти в виде обычного текста в любой точке, я предоставляю значение в качестве параметра для моей команды.

<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}">

Ответ 5

Проверьте еще один поток в поле пароля. Лучше не хранить пароль на любом DP или общедоступном ресурсе.

Другой поток в окне пароля