Reputation: 748
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
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
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