I have come across a problem with binding to a PasswordBox
. It seems it\'s a security risk but I am using the MVVM pattern so I wish to bypass this. I found som
I am using succinct MVVM-friendly solution that hasn't been mentioned yet. First, I name the PasswordBox in XAML:
<PasswordBox x:Name="Password" />
Then I add a single method call into view constructor:
public LoginWindow()
{
InitializeComponent();
ExposeControl<LoginViewModel>.Expose(this, view => view.Password,
(model, box) => model.SetPasswordBox(box));
}
And that's it. View model will receive notification when it is attached to a view via DataContext and another notification when it is detached. The contents of this notification are configurable via the lambdas, but usually it's just a setter or method call on the view model, passing the problematic control as a parameter.
It can be made MVVM-friendly very easily by having the view expose interface instead of child controls.
The above code relies on helper class published on my blog.
In windows universal app
you can use this code with the property "Password" and binding with the modelView
<PasswordBox x:Uid="PasswordBox" Password="{Binding Waiter.Password, Mode=TwoWay}" Name="txtPassword" HorizontalAlignment="Stretch" Margin="50,200,50,0" VerticalAlignment="Top"/>
Its very simple . Create another property for password and Bind this with TextBox
But all input operations perform with actual password property
private string _Password;
public string PasswordChar
{
get
{
string szChar = "";
foreach(char szCahr in _Password)
{
szChar = szChar + "*";
}
return szChar;
}
set
{
_PasswordChar = value; NotifyPropertyChanged();
}
}
public string Password { get { return _Password; }
set
{
_Password = value; NotifyPropertyChanged();
PasswordChar = _Password;
}
}
you can do it with attached property, see it.. PasswordBox with MVVM
As mentioned before VM should be unaware of the View but passing whole PasswordBox looks like the simplest approach. So maybe instead of casting passed parameter to PasswordBox use Reflection to extract Password property from it. In this case VM expects some kind of Password Container with property Password(I'm ussing RelayCommands from MVMM Light-Toolkit):
public RelayCommand<object> SignIn
{
get
{
if (this.signIn == null)
{
this.signIn = new RelayCommand<object>((passwordContainer) =>
{
var password = passwordContainer.GetType().GetProperty("Password").GetValue(passwordContainer) as string;
this.authenticationService.Authenticate(this.Login, password);
});
}
return this.signIn;
}
}
It can be easily tested with anonymous class:
var passwordContainer = new
{
Password = "password"
};
To me, both of these things feel wrong:
PasswordBox
as a command parameter to the ViewModelTransferring the SecurePassword (SecureString instance) as described by Steve in CO seems acceptable. I prefer Behaviors
to code behind, and I also had the additional requirement of being able to reset the password from the viewmodel.
Xaml (Password
is the ViewModel property):
<PasswordBox>
<i:Interaction.Behaviors>
<behaviors:PasswordBinding BoundPassword="{Binding Password, Mode=TwoWay}" />
</i:Interaction.Behaviors>
</PasswordBox>
Behavior:
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace Evidence.OutlookIntegration.AddinLogic.Behaviors
{
/// <summary>
/// Intermediate class that handles password box binding (which is not possible directly).
/// </summary>
public class PasswordBoxBindingBehavior : Behavior<PasswordBox>
{
// BoundPassword
public SecureString BoundPassword { get { return (SecureString)GetValue(BoundPasswordProperty); } set { SetValue(BoundPasswordProperty, value); } }
public static readonly DependencyProperty BoundPasswordProperty = DependencyProperty.Register("BoundPassword", typeof(SecureString), typeof(PasswordBoxBindingBehavior), new FrameworkPropertyMetadata(OnBoundPasswordChanged));
protected override void OnAttached()
{
this.AssociatedObject.PasswordChanged += AssociatedObjectOnPasswordChanged;
base.OnAttached();
}
/// <summary>
/// Link up the intermediate SecureString (BoundPassword) to the UI instance
/// </summary>
private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e)
{
this.BoundPassword = this.AssociatedObject.SecurePassword;
}
/// <summary>
/// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString())
/// </summary>
private static void OnBoundPasswordChanged(object s, DependencyPropertyChangedEventArgs e)
{
var box = ((PasswordBoxBindingBehavior)s).AssociatedObject;
if (box != null)
{
if (((SecureString)e.NewValue).Length == 0)
box.Password = string.Empty;
}
}
}
}