📜 ⬆️ ⬇️

Access to PasswordBox in MVVM environment

When I first read the famous article by Josh Smith WPF Apps With The Model-View-ViewModel Design Pattern, I was very inspired by the MVVM philosophy and decided to write all future projects exclusively using it. As far as MVVM was “implemented”, I encountered certain difficulties and shortcomings (in my subjective opinion). In one of the projects, I needed a UserControl, which would allow authorization on a web service. When I tried to bind the ViewModel property to the PasswordBox control, I received the following error:
A 'Binding' cannot be set on the 'Password' property of type 'PasswordBox'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject

The reason why the Password property is not made DependencyProperty is explained by security considerations. If it were possible to bind a property to PasswordBox, then it would be stored in memory in clear text, and this is not a good idea from the point of view of Microsoft. Therefore, the password in PasswordBox is stored in an encrypted form and is decrypted when the Password property is called.

I found several solutions to this problem. There are ways that preserve the principles of MVVM, but create the problem described above. If the requirements do not provide for this kind of security, then it is possible to go against the system using Attached Properties after all, to bind the ViewModel property. I will describe another way, which, perhaps, slightly violates the principles of MVVM, but retains the principle of password security.

Create a simple control:
')
<Label Content="Login:" HorizontalAlignment="Right"/> <TextBox Grid.Column="1" Margin="3" Text="{Binding UserName, Mode=TwoWay}"/> <Label Grid.Row="1" Content="Password:" HorizontalAlignment="Right"/> <PasswordBox Grid.Row="1" Grid.Column="1" Margin="3" Name="pwdBox"/> <Button Grid.Row="2" Grid.ColumnSpan="2" Width="70" Height="25" HorizontalAlignment="Right" Content="Login" Command="{Binding LoginCommand}"/> 

To get the password, we need an interface:

  public interface IPasswordSupplier { string GetPassword(); } 


Inherit control from this interface and return the value of the password to the function:

  public partial class LoginControl : UserControl, IPasswordSupplier { public LoginControl() { InitializeComponent(); } public string GetPassword() { return pwdBox.Password; } } 


The unpleasant feature of the method is that LoginControl will have to be created in the code, since need to get IPasswordSupplier. With the help of the IoC container, we register the instance of control in App.xaml.cs:

  protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); IUnityContainer container = new UnityContainer(); LoginControl loginControl = new LoginControl(); container.RegisterInstance<IPasswordSupplier>(loginControl); LoginViewModel loginViewModel = new LoginViewModel(container); loginControl.DataContext = loginViewModel; MainWindow mainWindow = new MainWindow(loginControl); MainWindowViewModel windowViewModel = new MainWindowViewModel(loginViewModel); mainWindow.DataContext = windowViewModel; mainWindow.Show(); } 


Now in the ViewModel control we can access the Password property of the PasswordBox:
 public string Password { get { IPasswordSupplier passwordSupplier = container.Resolve<IPasswordSupplier>(); return passwordSupplier.GetPassword(); } } 


Thus, the Password property of our ViewModel does not store the value of the password, but receives it every time it is accessed from PasswordBox, which in turn decrypts it for us. Perhaps this violates MVVM, because Now our ViewModel is indirectly connected to View through this interface, but still this method seems to me most suitable for solving the problem.

Source: https://habr.com/ru/post/169231/


All Articles