Drakestar
Drakestar

Reputation: 748

PasswordBox in UserControl - DataBinding works but box looks empty

Upfront info/question: everything posted below works (data-binding the PasswordBox string), except that my PasswordBox looks empty even though its content it correct.

Details: I have a UserControl with a PasswordBox and a DependencyProperty. That DP can be used to set/retrieve the SecureString from that PWBox via data-binding.

public partial class PasswordUserControl : UserControl
{       
    public SecureString Password
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    // https://msdn.microsoft.com/en-us/library/ms750428(v=vs.110).aspx
    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(SecureString), typeof(PasswordUserControl),
            new PropertyMetadata(default(SecureString)));

    public PasswordUserControl()
    {
        InitializeComponent();

        PasswordEntryBox.PasswordChanged += (sender, args) => {
            Password = ((PasswordBox)sender).SecurePassword; };
    }       
}

Simple XAML:

<UserControl x:Class="WpfClient.PasswordUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:WpfClient"
         mc:Ignorable="d" 
         d:SizeToContent="true">

<StackPanel>
    <DockPanel>
        <Label Content="Password" Margin="0,0,5,0" DockPanel.Dock="Left"/>
        <PasswordBox Name="PasswordEntryBox" Width="200" DockPanel.Dock="Right" />
    </DockPanel>
</StackPanel>

Simple use of that control in another location:

<local:PasswordUserControl Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

"Binding Password" feels into a SecureString:

public SecureString Password
    {
        get { return m_Password; }
        set
        {
            m_Password = value;
            RaisePropertyChanged("Password");
        }
    }

Question: The databinding seems to work both ways; I get the correct user input (triggered logins work). PropertyChanged also works: for every character added, I get a property update.

What doesn't work: the actual PasswordBox looks empty. If I set the Password in code first and look at the control, it doesn't show a bunch of anonymous circle characters for the already-entered, data-bound text (but the string itself is correct - debugger says so and actual logins work!). If I enter the password text into the box manually I do get anonymous characters for my entry, but my input doesn't add to the existing string that's supposedly data-bound. It seemingly erases the data-bound string and creates a new one from scratch.

What am I doing wrong?

Upvotes: 0

Views: 1314

Answers (2)

MC Qu
MC Qu

Reputation: 77

LoginPassword.Password = Globals.credentials == null ? "": new NetworkCredential("", Globals.credentials.SecurePassword).Password;

simply put one line in the xaml.cs file. Name password box to "LoginPassword".

Upvotes: 1

mm8
mm8

Reputation: 169200

For you to be able to see any password characters in the PasswordBox you need to set its Password property to a string. This means that you will need to extract the plain password string from the SecureString object.

You should do this whenever the Password dependency property is set to a new SecureString value.

You could add a PropertyChangedCallback to the dependency property and implement the UserControl as follows:

public partial class PasswordUserControl : UserControl
{
    public SecureString Password
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(SecureString), typeof(PasswordUserControl),
            new PropertyMetadata(default(SecureString), new PropertyChangedCallback(OnPasswordChanged)));


    public PasswordUserControl()
    {
        InitializeComponent();
        PasswordEntryBox.PasswordChanged += PasswordEntryBox_PasswordChanged;
    }

    private bool _handleEvent = true;
    private void PasswordEntryBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
       if(_handleEvent)
            Password = ((PasswordBox)sender).SecurePassword;
    }

    private static void OnPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SecureString ss = e.NewValue as SecureString;
        if(ss != null)
        {
            PasswordUserControl control = d as PasswordUserControl;
            if(control != null)
            {
                control._handleEvent = false;
                control.PasswordEntryBox.Password = ConvertToUnsecureString(ss);
                control._handleEvent = true;
            }
        }
    }

    public static string ConvertToUnsecureString(SecureString securePassword)
    {
        if (securePassword == null)
            throw new ArgumentNullException("securePassword");

        IntPtr unmanagedString = IntPtr.Zero;
        try
        {
            unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
            return Marshal.PtrToStringUni(unmanagedString);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
        }
    }
}

Upvotes: 1

Related Questions