Caliburn.Micro support for PasswordBox?
Here's a much more simplified example, including a binding convention so that PasswordBox
binding in Caliburn.Micro Just Works™:
public static class PasswordBoxHelper
{
public static readonly DependencyProperty BoundPasswordProperty =
DependencyProperty.RegisterAttached("BoundPassword",
typeof(string),
typeof(PasswordBoxHelper),
new FrameworkPropertyMetadata(string.Empty, OnBoundPasswordChanged));
public static string GetBoundPassword(DependencyObject d)
{
var box = d as PasswordBox;
if (box != null)
{
// this funny little dance here ensures that we've hooked the
// PasswordChanged event once, and only once.
box.PasswordChanged -= PasswordChanged;
box.PasswordChanged += PasswordChanged;
}
return (string)d.GetValue(BoundPasswordProperty);
}
public static void SetBoundPassword(DependencyObject d, string value)
{
if (string.Equals(value, GetBoundPassword(d)))
return; // and this is how we prevent infinite recursion
d.SetValue(BoundPasswordProperty, value);
}
private static void OnBoundPasswordChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var box = d as PasswordBox;
if (box == null)
return;
box.Password = GetBoundPassword(d);
}
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox password = sender as PasswordBox;
SetBoundPassword(password, password.Password);
// set cursor past the last character in the password box
password.GetType().GetMethod("Select", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(password, new object[] { password.Password.Length, 0 });
}
}
Then, in your bootstrapper:
public sealed class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
ConventionManager.AddElementConvention<PasswordBox>(
PasswordBoxHelper.BoundPasswordProperty,
"Password",
"PasswordChanged");
}
// other bootstrapper stuff here
}
The solutions provided here seem to be needlessly complicated.
We can very easily use Caliburn.Micro actions to send our password to the ViewModel.
XAML:
<PasswordBox cal:Message.Attach="[Event PasswordChanged] = [Action OnPasswordChanged($source)]" />
ViewModel:
public void OnPasswordChanged(PasswordBox source)
{
password = source.Password;
}
Then remember to clear the password fields so they don't remain in memory.
NOTE: Obviously this solution doesn't allow you to easily change the password from the ViewModel, if that is necessary, then it's probably best to go with the attached property approach.