Binding to a PasswordBox's Password property in WPF
If you’ve ever tried to write XAML which looks like this:
<PasswordBox Password="{Binding Password}"/>
You’ll have quickly found out that:
A ‘Binding’ cannot be set on the ‘Password’ property of type ‘PasswordBox’. A ‘Binding’ can only be set on a DependencyProperty of a DependencyObject.
This is because the Password
property of a PasswordBox
is a normal property instead of a DependencyProperty
.
Why is this?
Security reasons.
If this property were bindable, then the password that the user entered would be stored in memory in a number of different places, which constitutes a security risk.
But… What if that’s not relevant? What if an attacker snopping around the RAM of your machine isn’t a viable risk, and you’re only obscuring the password to stop people looking over the user’s shoulder?
There are a number of ways that people have worked around the issue, either by dropping back to the codebehind or writing magic involving attached properties.
Below is my offering: it allows two-way binding (so that you can update the property that you’ve bound the Password
property to, and those updates will be reflected in the UI), and is reasonably succinct.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace XamlHelpers
{
public static class Secure
{
private static readonly DependencyProperty PasswordInitializedProperty =
DependencyProperty.RegisterAttached("PasswordInitialized", typeof(bool), typeof(Secure), new PropertyMetadata(false));
private static readonly DependencyProperty SettingPasswordProperty =
DependencyProperty.RegisterAttached("SettingPassword", typeof(bool), typeof(Secure), new PropertyMetadata(false));
public static string GetPassword(DependencyObject obj)
{
return (string)obj.GetValue(PasswordProperty);
}
public static void SetPassword(DependencyObject obj, string value)
{
obj.SetValue(PasswordProperty, value);
}
// We play a trick here. If we set the initial value to something, it'll be set to something else when the binding kicks in,
// and HandleBoundPasswordChanged will be called, which allows us to set up our event subscription.
// If the binding sets us to a value which we already are, then this doesn't happen. Therefore start with a value that's
// definitely unique.
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password", typeof(string), typeof(Secure),
new FrameworkPropertyMetadata(Guid.NewGuid().ToString(), HandleBoundPasswordChanged)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.LostFocus // Match the default on Binding
});
private static void HandleBoundPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
var passwordBox = dp as PasswordBox;
if (passwordBox == null)
return;
// If we're being called because we set the value of the property we're bound to (from inside
// HandlePasswordChanged, then do nothing - we already have the latest value).
if ((bool)passwordBox.GetValue(SettingPasswordProperty))
return;
// If this is the initial set (see the comment on PasswordProperty), set ourselves up
if (!(bool)passwordBox.GetValue(PasswordInitializedProperty))
{
passwordBox.SetValue(PasswordInitializedProperty, true);
passwordBox.PasswordChanged += HandlePasswordChanged;
}
passwordBox.Password = e.NewValue as string;
}
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
{
var passwordBox = (PasswordBox)sender;
passwordBox.SetValue(SettingPasswordProperty, true);
SetPassword(passwordBox, passwordBox.Password);
passwordBox.SetValue(SettingPasswordProperty, false);
}
}
}
Usage is something like this:
<!-- Assuming you have xmlns:xaml="clr-namespace:XamlHelpers" or similar -->
<PaswordBox xaml:Secure.Password="{Binding MyPasswordProperty}"/>