Get Password from PasswordBox in ListBox of ListBo

2019-08-30 16:47发布

问题:

I have a WPF ListBox, each ListBoxItem has many UIElements. One of those UIElements is PasswordBox. These UIElements are defined in Xaml ControlTemplate.

What I need to do is get a Password string from PasswordBox in code behind. But I can not bind Password Property of PasswordBox, which is not a dynamic property and it make sense as Password should not be stored in a memory for long.

Now the Solution that comes to my mind is to get UIElements of ListBoxItem in code behind and get Password from there.

But I am unable to figure out how can i get the UIElements in code behind from ListBox.

回答1:

The Password property of the password box is not is dependency property, hence you can not bind this property.

You can us either of the following way to get the password from passwordbox control.

  1. Pass the password box control as a parameter to the command attached with item.

    XAML

    <Window x:Class="WpfApplication4.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <StackPanel>
        <PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" />
        <Button Content="Ok" Command="{Binding Path=OkCommand}" CommandParameter="{Binding ElementName=txtPassword}"/>
    </StackPanel>
    

Code behind

public class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        public ICommand OkCommand { get; private set; }

        public MyViewModel()
        {
            OkCommand = new GeneralCommand<object>(ExecuteOkCommand, param => CanExecuteCommand());
        }

        private void ExecuteOkCommand(object parameter)
        {
            var passwordBox = parameter as PasswordBox;
            var password = passwordBox.Password;
        }

        private bool CanExecuteCommand()
        {
            return true;
        }
    }

This slightly violates the MVVM pattern since now the viewmodel knows something about how the view is implemented.

  1. Use the attach property to bind the password.

XAML

<StackPanel>
    <PasswordBox w:PasswordHelper.Attach="True" 
         w:PasswordHelper.Password="{Binding Text, ElementName=plain, Mode=TwoWay}" 
                 Width="130"/>
    <TextBlock Padding="10,0" x:Name="plain" />
</StackPanel>

Code

public static class PasswordHelper
{
    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.RegisterAttached("Password",
        typeof(string), typeof(PasswordHelper),
        new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));

    public static readonly DependencyProperty AttachProperty =
        DependencyProperty.RegisterAttached("Attach",
        typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, Attach));

    private static readonly DependencyProperty IsUpdatingProperty =
       DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), 
       typeof(PasswordHelper));


    public static void SetAttach(DependencyObject dp, bool value)
    {
        dp.SetValue(AttachProperty, value);
    }

    public static bool GetAttach(DependencyObject dp)
    {
        return (bool)dp.GetValue(AttachProperty);
    }

    public static string GetPassword(DependencyObject dp)
    {
        return (string)dp.GetValue(PasswordProperty);
    }

    public static void SetPassword(DependencyObject dp, string value)
    {
        dp.SetValue(PasswordProperty, value);
    }

    private static bool GetIsUpdating(DependencyObject dp)
    {
        return (bool)dp.GetValue(IsUpdatingProperty);
    }

    private static void SetIsUpdating(DependencyObject dp, bool value)
    {
        dp.SetValue(IsUpdatingProperty, value);
    }

    private static void OnPasswordPropertyChanged(DependencyObject sender,
        DependencyPropertyChangedEventArgs e)
    {
        PasswordBox passwordBox = sender as PasswordBox;
        passwordBox.PasswordChanged -= PasswordChanged;

        if (!(bool)GetIsUpdating(passwordBox))
        {
            passwordBox.Password = (string)e.NewValue;
        }
        passwordBox.PasswordChanged += PasswordChanged;
    }

    private static void Attach(DependencyObject sender,
        DependencyPropertyChangedEventArgs e)
    {
        PasswordBox passwordBox = sender as PasswordBox;

        if (passwordBox == null)
            return;

        if ((bool)e.OldValue)
        {
            passwordBox.PasswordChanged -= PasswordChanged;
        }

        if ((bool)e.NewValue)
        {
            passwordBox.PasswordChanged += PasswordChanged;
        }
    }

    private static void PasswordChanged(object sender, RoutedEventArgs e)
    {
        PasswordBox passwordBox = sender as PasswordBox;
        SetIsUpdating(passwordBox, true);
        SetPassword(passwordBox, passwordBox.Password);
        SetIsUpdating(passwordBox, false);
    }
}

You can refer the "http://wpftutorial.net/PasswordBox.html" for detail information.