Cómo enlazar a un PasswordBox en MVVM

Me he encontrado con un problema con el enlace a un PasswordBox. Parece que es un riesgo de seguridad, pero estoy usando el patrón MVVM, así que deseo omitir esto. Encontré un código interesante aquí (¿alguien ha usado esto o algo similar?)

http://www.wpftutorial.net/PasswordBox.html

Técnicamente se ve genial, pero no estoy seguro de cómo recuperar la contraseña.

Básicamente tengo propiedades en mi LoginViewModel para nombre de Username y Password . Username está bien y está funcionando ya que es un TextBox .

Utilicé el código anterior como se indica e ingresé este

  

Cuando tenía PasswordBox como TextBox y Binding Path=Password , la propiedad en mi LoginViewModel se actualizó.

Mi código es muy simple, básicamente tengo un Command para mi Button . Cuando lo CanLogin se llama a CanLogin y si devuelve verdadero, llama a Login .
Puedes ver que reviso mi propiedad para el nombre de Username que funciona muy bien.

En Login , envío a mi servicio un nombre de Username y una Password , el nombre de Username contiene datos de mi View pero la Password es Null|Empty

 private DelegateCommand loginCommand; public string Username { get; set; } public string Password { get; set; } public ICommand LoginCommand { get { if (loginCommand == null) { loginCommand = new DelegateCommand( Login, CanLogin ); } return loginCommand; } } private bool CanLogin() { return !string.IsNullOrEmpty(Username); } private void Login() { bool result = securityService.IsValidLogin(Username, Password); if (result) { } else { } } 

Esto es lo que estoy haciendo

   

Tengo mi TextBox , esto no es problema, pero en mi ViewModel la Password está vacía.

¿Estoy haciendo algo mal o me falta un paso?

Puse un punto de interrupción y, ViewModel el código ingresa a la clase de ayuda estática, pero nunca actualiza mi Password en mi ViewModel .

Lo siento, pero lo estás haciendo mal.

Las personas deben tener la siguiente directriz de seguridad tatuada en el interior de los párpados:
Nunca guarde contraseñas de texto sin formato en la memoria.

La razón por la que WPF / Silverlight PasswordBox no expone un DP para la propiedad Contraseña está relacionada con la seguridad.
Si WPF / Silverlight mantenían un DP para Contraseña, requeriría que el marco mantenga la contraseña encriptada en la memoria. Lo cual se considera un vector de ataque de seguridad bastante problemático. PasswordBox usa memoria encriptada (de tipo) y la única forma de acceder a la contraseña es a través de la propiedad CLR.

Sugeriría que al acceder a la propiedad CLR PasswordBox.Password, se abstendría de colocarlo en cualquier variable o como valor para cualquier propiedad.
Mantener su contraseña en texto sin formato en la máquina del cliente RAM es una seguridad negativa.
Así que deshazte de esa “contraseña de cadena pública {get; set;}” que tienes arriba.

Al acceder a PasswordBox.Password, solo sácalo y envíalo al servidor CUANTO ANTES. No mantenga el valor de la contraseña y no la trate como lo haría con cualquier otro texto de máquina del cliente. No mantenga contraseñas de texto claras en la memoria.

Sé que esto rompe el patrón MVVM, pero nunca deberías enlazar a PasswordBox.Password Attached DP, almacenar tu contraseña en ViewModel o cualquier otra trampa similar.

Si está buscando una solución sobre architecture, esta es una:
1. Cree la interfaz IHavePassword con un método que devuelva la contraseña de texto sin cifrar.
2. Haga que su UserControl implemente una interfaz IHavePassword.
3. Registre la instancia de UserControl con su IoC como implementación de la interfaz IHavePassword.
4. Cuando se produzca una solicitud del servidor que requiera su contraseña, llame a su IoC para obtener la implementación de IHavePassword y solo obtenga la codiciada contraseña.

Solo mi opinión sobre eso.

– Justin

Mis 2 centavos:

Desarrollé una vez un diálogo de inicio de sesión típico (cuadros de usuario y contraseña, más el botón “Aceptar”) usando WPF y MVVM. Resolví el problema de encuadernación con contraseña simplemente pasando el control PasswordBox como un parámetro al comando adjunto al botón “Aceptar”. Entonces en la vista que tuve:

   

Y en ViewModel, el método Execute del comando adjunto era el siguiente:

 void Execute(object parameter) { var passwordBox = parameter as PasswordBox; var password = passwordBox.Password; //Now go ahead and check the user name and password } 

Esto viola ligeramente el patrón de MVVM ya que ahora el ViewModel sabe algo acerca de cómo se implementa la Vista, pero en ese proyecto en particular me lo pude permitir. Espero que sea útil para alguien también.

Tal vez me esté perdiendo algo, pero parece que la mayoría de estas soluciones complican las cosas y eliminan las prácticas seguras.

Este método no infringe el patrón MVVM y mantiene una seguridad completa. Sí, técnicamente es código subyacente, pero no es más que un enlace de “caso especial”. El ViewModel aún no tiene conocimiento de la implementación de View, que en mi opinión sí lo hace si está intentando pasar el PasswordBox al ViewModel.

Code Behind! = Violación automática de MVVM. Todo depende de lo que hagas con él. En este caso, estamos simplemente codificando manualmente un enlace, por lo que todo se considera parte de la implementación de UI y, por lo tanto, está bien.

En ViewModel, solo una propiedad simple. Lo hice “solo escritura” ya que no debería haber necesidad de recuperarlo fuera del ViewModel por ningún motivo, pero no tiene que ser así. Tenga en cuenta que es un SecureString, no solo una cadena.

 public SecureString SecurePassword { private get; set; } 

En el xaml, configura un controlador de eventos PasswordChanged.

  

En el código detrás:

 private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (this.DataContext != null) { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; } } 

Con este método, su contraseña permanece en SecureString en todo momento y, por lo tanto, proporciona la máxima seguridad. Si realmente no le importa la seguridad o necesita la contraseña de texto claro para un método posterior que lo requiera (nota: la mayoría de los métodos .NET que requieren una contraseña también admiten una opción SecureString, por lo que puede que realmente no necesite una contraseña de texto claro) incluso si crees que sí lo haces), puedes usar la propiedad Contraseña en su lugar. Me gusta esto:

(Propiedad de ViewModel)

 public string Password { private get; set; } 

(Código detrás)

 private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (this.DataContext != null) { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; } } 

Si desea mantener las cosas fuertemente tipadas, puede sustituir el elenco (dynamic) con la interfaz de su ViewModel. Pero realmente, los enlaces de datos “normales” tampoco están fuertemente tipados, así que no es un gran problema.

 private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) { if (this.DataContext != null) { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; } } 

El mejor de todos los mundos: su contraseña es segura, su ViewModel solo tiene una propiedad como cualquier otra propiedad, y su View es independiente y no requiere referencias externas.

Puedes usar este XAML:

        

Y este comando ejecuta el método:

 private void ExecutePasswordChangedCommand(PasswordBox obj) { if (obj != null) Password = obj.Password; } 

Esto funciona bien para mí.

  

Una solución simple sin violar el patrón MVVM es introducir un evento (o delegado) en ViewModel que recolecta la contraseña.

En ViewModel :

public event EventHandler HarvestPassword;

con estos EventArgs:

 class HarvestPasswordEventArgs : EventArgs { public string Password; } 

en la Vista , suscríbase al evento al crear ViewModel y complete el valor de la contraseña.

 _viewModel.HarvestPassword += (sender, args) => args.Password = passwordBox1.Password; 

En ViewModel , cuando necesite la contraseña, puede iniciar el evento y recostackr la contraseña desde allí:

 if (HarvestPassword == null) //bah return; var pwargs = new HarvestPasswordEventArgs(); HarvestPassword(this, pwargs); LoginHelpers.Login(Username, pwargs.Password); 

Publiqué un GIST aquí que es una caja de contraseña enlazable.

 using System.Windows; using System.Windows.Controls; namespace CustomControl { public class BindablePasswordBox : Decorator { ///  /// The password dependency property. ///  public static readonly DependencyProperty PasswordProperty; private bool isPreventCallback; private RoutedEventHandler savedCallback; ///  /// Static constructor to initialize the dependency properties. ///  static BindablePasswordBox() { PasswordProperty = DependencyProperty.Register( "Password", typeof(string), typeof(BindablePasswordBox), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged)) ); } ///  /// Saves the password changed callback and sets the child element to the password box. ///  public BindablePasswordBox() { savedCallback = HandlePasswordChanged; PasswordBox passwordBox = new PasswordBox(); passwordBox.PasswordChanged += savedCallback; Child = passwordBox; } ///  /// The password dependency property. ///  public string Password { get { return GetValue(PasswordProperty) as string; } set { SetValue(PasswordProperty, value); } } ///  /// Handles changes to the password dependency property. ///  /// the dependency object /// the event args private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs eventArgs) { BindablePasswordBox bindablePasswordBox = (BindablePasswordBox) d; PasswordBox passwordBox = (PasswordBox) bindablePasswordBox.Child; if (bindablePasswordBox.isPreventCallback) { return; } passwordBox.PasswordChanged -= bindablePasswordBox.savedCallback; passwordBox.Password = (eventArgs.NewValue != null) ? eventArgs.NewValue.ToString() : ""; passwordBox.PasswordChanged += bindablePasswordBox.savedCallback; } ///  /// Handles the password changed event. ///  /// the sender /// the event args private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs) { PasswordBox passwordBox = (PasswordBox) sender; isPreventCallback = true; Password = passwordBox.Password; isPreventCallback = false; } } } 

Esta implementación es ligeramente diferente. Usted transfiere una contraseña a la Vista a través del enlace de una propiedad en ViewModel, no usa ningún parámetro de comando. The ViewModel permanece Ignorante de la vista. Tengo un proyecto VB vs 2010 que se puede descargar desde SkyDrive. Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511

La forma en que estoy usando PasswordBox en una aplicación Wpf MvvM es bastante simplista y funciona bien para Mí. Eso no significa que creo que es la forma correcta o la mejor manera. Es solo una implementación de Using PasswordBox y el patrón MvvM.

Básicamente, usted crea una propiedad pública de solo lectura a la que la Vista se puede vincular como un PasswordBox (El control real) Ejemplo:

 Private _thePassWordBox As PasswordBox Public ReadOnly Property ThePassWordBox As PasswordBox Get If IsNothing(_thePassWordBox) Then _thePassWordBox = New PasswordBox Return _thePassWordBox End Get End Property 

Utilizo un campo de respaldo solo para hacer la auto Inicialización de la propiedad.

Luego, desde Xaml, enlaza el contenido de un ContentControl o un contenedor de control. Ejemplo:

   

Desde allí tienes el control total de la contraseña También utilizo PasswordAccessor (solo una función de String) para devolver el valor de la contraseña al iniciar sesión o cualquier otra cosa para la que deseas la contraseña. En el ejemplo, tengo una propiedad pública en un modelo de objetos de usuario genérico. Ejemplo:

 Public Property PasswordAccessor() As Func(Of String) 

En el objeto del usuario, la propiedad de la cadena de contraseña es de solo lectura sin ningún almacén de respaldo, solo devuelve la contraseña del PasswordBox. Ejemplo:

 Public ReadOnly Property PassWord As String Get Return If((PasswordAccessor Is Nothing), String.Empty, PasswordAccessor.Invoke()) End Get End Property 

Luego, en ViewModel, me aseguro de que el Accesor se haya creado y establecido en la propiedad PasswordBox.Password ‘Ejemplo:

 Public Sub New() 'Sets the Accessor for the Password Property SetPasswordAccessor(Function() ThePassWordBox.Password) End Sub Friend Sub SetPasswordAccessor(ByVal accessor As Func(Of String)) If Not IsNothing(VMUser) Then VMUser.PasswordAccessor = accessor End Sub 

Cuando necesito la cadena de contraseña, digo para iniciar sesión, obtengo la propiedad Contraseña de objetos de usuario que realmente invoca la función para obtener la contraseña y devolverla, y el objeto de usuario no almacena la contraseña. Ejemplo: estaría en ViewModel

 Private Function LogIn() as Boolean 'Make call to your Authentication methods and or functions. I usally place that code in the Model Return AuthenticationManager.Login(New UserIdentity(User.UserName, User.Password) End Function 

Deberias hacer eso. ViewModel no necesita ningún conocimiento de los controles de la vista. La vista solo se une a la propiedad en el modelo de vista, que no es diferente de la vinculación de vista a una imagen u otro recurso. En este caso, ese recurso (Propiedad) resulta ser un control de usuario. Permite realizar pruebas ya que ViewModel crea y posee la Propiedad y la Propiedad es independiente de la Vista. En cuanto a la Seguridad, no sé cuán buena es esta implementación. Pero al usar una función, el valor no se almacena en la propiedad en sí a la que accede la propiedad.

Para resolver el problema de OP sin romper MVVM, usaría convertidor de valor personalizado y un contenedor para el valor (la contraseña) que debe recuperarse del cuadro de contraseña.

 public interface IWrappedParameter { T Value { get; } } public class PasswordBoxWrapper : IWrappedParameter { private readonly PasswordBox _source; public string Value { get { return _source != null ? _source.Password : string.Empty; } } public PasswordBoxWrapper(PasswordBox source) { _source = source; } } public class PasswordBoxConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // Implement type and value check here... return new PasswordBoxWrapper((PasswordBox)value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new InvalidOperationException("No conversion."); } } 

En el modelo de vista:

 public string Username { get; set; } public ICommand LoginCommand { get { return new RelayCommand>(password => { Login(Username, password); }); } } private void Login(string username, string password) { // Perform login here... } 

Como el modelo de vista usa IWrappedParameter , no necesita tener ningún conocimiento sobre PasswordBoxWrapper ni PasswordBoxConverter . De esta forma puede aislar el objeto PasswordBox del modelo de vista y no romper el patrón MVVM.

En la vista:

    ...   

Si bien acepto que es importante evitar el almacenamiento de la contraseña en cualquier lugar, aún necesito la posibilidad de crear una instancia del modelo de vista sin vista y ejecutar mis pruebas en su contra.

La solución que funcionó para mí fue registrar la función PasswordBox.Password con el modelo de vista y hacer que el modelo de vista lo invoque al ejecutar el código de inicio de sesión.

Esto significa una línea de código en el código de la vista detrás.

Entonces, en mi Login.xaml tengo

  

y en Login.xaml.cs tengo

 LoginViewModel.PasswordHandler = () => PasswordBox.Password; 

luego en LoginViewModel.cs tengo el PasswordHandler definido

 public Func PasswordHandler { get; set; } 

y cuando se necesita iniciar sesión, el código invoca al controlador para obtener la contraseña de la vista …

 bool loginResult = Login(Username, PasswordHandler()); 

De esta forma, cuando quiero probar el modelo de vista, simplemente puedo configurar PasswordHandler en un método anónimo que me permite entregar la contraseña que deseo usar en la prueba.

Pasé una gran cantidad de tiempo mirando varias soluciones. No me gustó la idea de los decoradores, los comportamientos desordenan la interfaz de usuario de validación, el código detrás … ¿en serio?

La mejor SecureString es adherirse a una propiedad adjunta personalizada y vincularla a su propiedad SecureString en su modelo de visualización. Mantenlo ahí todo el tiempo que puedas. Siempre que necesite un acceso rápido a la contraseña simple, conviértala temporalmente en una cadena insegura utilizando el siguiente código:

 namespace Namespace.Extensions { using System; using System.Runtime.InteropServices; using System.Security; ///  /// Provides unsafe temporary operations on secured strings. ///  [SuppressUnmanagedCodeSecurity] public static class SecureStringExtensions { ///  /// Converts a secured string to an unsecured string. ///  public static string ToUnsecuredString(this SecureString secureString) { // copy&paste from the internal System.Net.UnsafeNclNativeMethods IntPtr bstrPtr = IntPtr.Zero; if (secureString != null) { if (secureString.Length != 0) { try { bstrPtr = Marshal.SecureStringToBSTR(secureString); return Marshal.PtrToStringBSTR(bstrPtr); } finally { if (bstrPtr != IntPtr.Zero) Marshal.ZeroFreeBSTR(bstrPtr); } } } return string.Empty; } ///  /// Copies the existing instance of a secure string into the destination, clearing the destination beforehand. ///  public static void CopyInto(this SecureString source, SecureString destination) { destination.Clear(); foreach (var chr in source.ToUnsecuredString()) { destination.AppendChar(chr); } } ///  /// Converts an unsecured string to a secured string. ///  public static SecureString ToSecuredString(this string plainString) { if (string.IsNullOrEmpty(plainString)) { return new SecureString(); } SecureString secure = new SecureString(); foreach (char c in plainString) { secure.AppendChar(c); } return secure; } } } 

Asegúrese de permitir que el GC recopile su elemento de interfaz de usuario, así que resista la tentación de utilizar un controlador de eventos estáticos para el evento PasswordChanged en el PasswordBox . También descubrí una anomalía en la que el control no actualizaba la IU cuando usaba la propiedad SecurePassword para configurarla, por lo que estoy copiando la contraseña en Password .

 namespace Namespace.Controls { using System.Security; using System.Windows; using System.Windows.Controls; using Namespace.Extensions; ///  /// Creates a bindable attached property for the  property. ///  public static class PasswordBoxHelper { // an attached behavior won't work due to view model validation not picking up the right control to adorn public static readonly DependencyProperty SecurePasswordBindingProperty = DependencyProperty.RegisterAttached( "SecurePassword", typeof(SecureString), typeof(PasswordBoxHelper), new FrameworkPropertyMetadata(new SecureString(),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AttachedPropertyValueChanged) ); private static readonly DependencyProperty _passwordBindingMarshallerProperty = DependencyProperty.RegisterAttached( "PasswordBindingMarshaller", typeof(PasswordBindingMarshaller), typeof(PasswordBoxHelper), new PropertyMetadata() ); public static void SetSecurePassword(PasswordBox element, SecureString secureString) { element.SetValue(SecurePasswordBindingProperty, secureString); } public static SecureString GetSecurePassword(PasswordBox element) { return element.GetValue(SecurePasswordBindingProperty) as SecureString; } private static void AttachedPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // we'll need to hook up to one of the element's events // in order to allow the GC to collect the control, we'll wrap the event handler inside an object living in an attached property // don't be tempted to use the Unloaded event as that will be fired even when the control is still alive and well (eg switching tabs in a tab control) var passwordBox = (PasswordBox)d; var bindingMarshaller = passwordBox.GetValue(_passwordBindingMarshallerProperty) as PasswordBindingMarshaller; if (bindingMarshaller == null) { bindingMarshaller = new PasswordBindingMarshaller(passwordBox); passwordBox.SetValue(_passwordBindingMarshallerProperty, bindingMarshaller); } bindingMarshaller.UpdatePasswordBox(e.NewValue as SecureString); } ///  /// Encapsulated event logic ///  private class PasswordBindingMarshaller { private readonly PasswordBox _passwordBox; private bool _isMarshalling; public PasswordBindingMarshaller(PasswordBox passwordBox) { _passwordBox = passwordBox; _passwordBox.PasswordChanged += this.PasswordBoxPasswordChanged; } public void UpdatePasswordBox(SecureString newPassword) { if (_isMarshalling) { return; } _isMarshalling = true; try { // setting up the SecuredPassword won't trigger a visual update so we'll have to use the Password property _passwordBox.Password = newPassword.ToUnsecuredString(); // you may try the statement below, however the benefits are minimal security wise (you still have to extract the unsecured password for copying) //newPassword.CopyInto(_passwordBox.SecurePassword); } finally { _isMarshalling = false; } } private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e) { // copy the password into the attached property if (_isMarshalling) { return; } _isMarshalling = true; try { SetSecurePassword(_passwordBox, _passwordBox.SecurePassword.Copy()); } finally { _isMarshalling = false; } } } } } 

Y el uso de XAML:

  

Mi propiedad en el modelo de vista se veía así:

 [RequiredSecureString] public SecureString LogonPassword { get { return _logonPassword; } set { _logonPassword = value; NotifyPropertyChanged(nameof(LogonPassword)); } } 

RequiredSecureString es simplemente un validador personalizado simple que tiene la siguiente lógica:

 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] public class RequiredSecureStringAttribute:ValidationAttribute { public RequiredSecureStringAttribute() :base("Field is required") { } public override bool IsValid(object value) { return (value as SecureString)?.Length > 0; } } 

Aqui lo tienes. Una solución MVVM pura completa y probada.

Usé este método y pasé el cuadro de contraseña, aunque esto viola el MVVM fue esencial para mí porque estaba usando un control de contenido con una plantilla de datos para mi inicio de sesión dentro de mi caparazón, que es un complejo entorno de shell. Así que acceder al código detrás del caparazón habría sido una mierda.

Pasar el passwordbox creo que es lo mismo que acceder al control desde el código hasta donde yo sé. Acepto las contraseñas, no las guardo en la memoria, etc. En esta implementación, no tengo propiedad para la contraseña en el modelo de visualización.

Comando de botón

 Command="{Binding Path=DataContext.LoginCommand, ElementName=MyShell}" CommandParameter="{Binding ElementName=PasswordBox}" 

ViewModel

 private void Login(object parameter) { System.Windows.Controls.PasswordBox p = (System.Windows.Controls.PasswordBox)parameter; MessageBox.Show(p.Password); } 

Pensé que arrojaría mi solución en la mezcla, ya que este es un problema tan común … y tener muchas opciones siempre es algo bueno.

Simplemente envolví PasswordBox en UserControl e implementé DependencyProperty para poder enlazar. Hago todo lo posible para evitar almacenar cualquier texto claro en la memoria, por lo que todo se hace a través de una SecureString y PasswordBox.Password . Durante el ciclo foreach , cada personaje queda expuesto, pero es muy breve. Honestamente, si le preocupa que su aplicación WPF se vea comprometida por esta breve exposición, tiene mayores problemas de seguridad que deben ser manejados.

La belleza de esto es que no se está rompiendo ninguna de las reglas de MVVM, ni siquiera las “puristas”, ya que se trata de un UserControl , por lo que se permite tener código subyacente. Cuando lo está utilizando, puede tener comunicación pura entre View y ViewModel sin que su VideModel tenga conocimiento de ninguna parte de View o del origen de la contraseña. Solo asegúrese de que está vinculando a SecureString en su ViewModel .

BindablePasswordBox.xaml

    

BindablePasswordBox.xaml.cs (Versión 1 – Sin soporte de enlace bidireccional)

 using System.ComponentModel; using System.Security; using System.Windows; using System.Windows.Controls; namespace BK.WPF.CustomControls { public partial class BindanblePasswordBox : UserControl { public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(SecureString), typeof(BindanblePasswordBox)); public SecureString Password { get { return (SecureString)GetValue(PasswordProperty); } set { SetValue(PasswordProperty, value); } } public BindanblePasswordBox() { InitializeComponent(); PswdBox.PasswordChanged += PswdBox_PasswordChanged; } private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e) { var secure = new SecureString(); foreach (var c in PswdBox.Password) { secure.AppendChar(c); } Password = secure; } } } 

Uso de la Versión 1:

  

BindablePasswordBox.xaml.cs (Version 2 – Tiene soporte de enlace bidireccional)

 public partial class BindablePasswordBox : UserControl { public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(SecureString), typeof(BindablePasswordBox), new PropertyMetadata(PasswordChanged)); public SecureString Password { get { return (SecureString)GetValue(PasswordProperty); } set { SetValue(PasswordProperty, value); } } public BindablePasswordBox() { InitializeComponent(); PswdBox.PasswordChanged += PswdBox_PasswordChanged; } private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e) { var secure = new SecureString(); foreach (var c in PswdBox.Password) { secure.AppendChar(c); } if (Password != secure) { Password = secure; } } private static void PasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var pswdBox = d as BindablePasswordBox; if (pswdBox != null && e.NewValue != e.OldValue) { var newValue = e.NewValue as SecureString; if (newValue == null) { return; } var unmanagedString = IntPtr.Zero; string newString; try { unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(newValue); newString = Marshal.PtrToStringUni(unmanagedString); } finally { Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); } var currentValue = pswdBox.PswdBox.Password; if (currentValue != newString) { pswdBox.PswdBox.Password = newString; } } } } 

Usage of Version 2:

  

you can do it with attached property, see it.. PasswordBox with MVVM

As you can see i am binding to Password, but maybe its bind it to the static class..

It is an attached property . This kind of property can be applied to any kind of DependencyObject , not just the type in which it is declared. So even though it is declared in the PasswordHelper static class, it is applied to the PasswordBox on which you use it.

To use this attached property, you just need to bind it to the Password property in your ViewModel :

  

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 SignIn { get { if (this.signIn == null) { this.signIn = new RelayCommand((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:

  • Implementing clear text password properties
  • Sending the PasswordBox as a command parameter to the ViewModel

Transferring 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):

      

Behavior:

 using System.Security; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace Evidence.OutlookIntegration.AddinLogic.Behaviors { ///  /// Intermediate class that handles password box binding (which is not possible directly). ///  public class PasswordBoxBindingBehavior : Behavior { // 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(); } ///  /// Link up the intermediate SecureString (BoundPassword) to the UI instance ///  private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e) { this.BoundPassword = this.AssociatedObject.SecurePassword; } ///  /// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString()) ///  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; } } } } 

In windows universal app

you can use this code with the property “Password” and binding with the modelView

   

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; } } 

For anyone who is aware of the risks this implementation imposes, to have the password sync to your ViewModel simply add Mode=OneWayToSource .

XAML

  

You find a solution for the PasswordBox in the ViewModel sample application of the WPF Application Framework (WAF) project.

However, Justin is right. Don’t pass the password as plain text between View and ViewModel. Use SecureString instead (See MSDN PasswordBox).

I have done like:

XAML:

      

DO#:

 private void NewPassword_PasswordChanged(object sender, RoutedEventArgs e) { try { //change tablenameDataTable: yours! and tablenameViewSource: yours! tablenameDataTable.Rows[tablenameViewSource.View.CurrentPosition]["password"] = NewPassword.Password; } catch { this.Password.Text = this.NewPassword.Password; } } 

¡Esto funciona para mi!

I used an authentication check followed by a sub called by a mediator class to the View (which also implements an authentication check) to write the password to the data class.

It’s not a perfect solution; however, it remedied my problem of not being able to move the password.

I am using succinct MVVM-friendly solution that hasn’t been mentioned yet. First, I name the PasswordBox in XAML:

  

Then I add a single method call into view constructor:

 public LoginWindow() { InitializeComponent(); ExposeControl.Expose(this, view => view.Password, (model, box) => model.SetPasswordBox(box)); } 

Y eso es. 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.

I spent ages trying to get this working. In the end, I gave up and just used the PasswordBoxEdit from DevExpress.

It is the simplest solution ever, as it allows binding without pulling any horrible tricks.

Solution on DevExpress website

For the record, I am not affiliated with DevExpress in any way.

                 

well my answerd is more simple just in the for the MVVM pattern

in class viewmodel

 public string password; PasswordChangedCommand = new DelegateCommand(PasswordChanged); Private void PasswordChanged(RoutedEventArgs obj) { var e = (WatermarkPasswordBox)obj.OriginalSource; //or depending or what are you using var e = (PasswordBox)obj.OriginalSource; password =e.Password; } 

the password property of the PasswordBox that win provides or WatermarkPasswordBox that XCeedtoolkit provides generates an RoutedEventArgs so you can bind it.

now in xmal view

        

o

        

If you want it combined it all in only one control and one command

      

And on your Vm (like Konamiman showed)

 public void AuthentifyEmp(object obj) { var passwordBox = obj as PasswordBox; var password = passwordBox.Password; } private RelayCommand _authentifyEmpCommand; public RelayCommand AuthentifyEmpCommand => _authentifyEmpCommand ?? (_authentifyEmpCommand = new RelayCommand(AuthentifyEmp, null)); 

Here’s my take on it:

  1. Using an attached property to bind the password defeats the purpose of securing the password. The Password property of a password box is not bindable for a reason.

  2. Passing the password box as command parameter will make the ViewModel aware of the control. This will not work if you plan to make your ViewModel reusable cross platform. Don’t make your VM aware of your View or any other controls.

  3. I don’t think introducing a new property, an interface, subscribing to password changed events or any other complicated things is necessary for a simple task of providing the password.

XAML

   

Code behind – using code behind does not necessarily violate MVVM. As long as you don’t put any business logic in it.

 btnLogin.CommandParameter = new Func(()=>pbPassword.Password); 

ViewModel

 LoginCommand = new RelayCommand>(getpwd=> { service.Login(username, getpwd()); });