I'm currently working on a Windows Presentation Foundation (WPF) client application that adheres to the Model View ViewModel (MVVM)
design pattern. Having had a lot of experience with .NET WinForms, the
WPF learning curve wasn't so steep. The main areas I initially
concentrated on were getting to grips with XAML, then concepts such as
data binding, commands, dependency properties and attached properties.
It's the last of these that helped in resolving the problem I had with
the PasswordBox control and view model binding.
In WPF, data binding enables you to hook a specific property in your data context (in my case, the view model) to a property on a user interface control. For example, the following XAML markup shows how the Text property on a TextBox can be bound to the FirstName property on the view model:
Of course, if the application I was working on wasn't using MVVM, then I could easily have picked up the user entered password in the code-behind in a button click event handler (e.g. this.userPasswordBox.Password would return the plain password). But because the application was adhering to MVVM, the code-behind was pretty much empty (except for the usual constructor boilerplate code). Instead, all of the UI "events" were being handled by the view model via command objects exposed as public properties. This therefore made retrieving the password from the PasswordBox and assigning it to the view model Password property an annoyingly interesting problem!
After doing a bit of research on the web, I found a couple of solutions.
The first solution above works but I wasn't too happy with passing a UI component to my view model - it violates the MVVM design pattern principle of decoupling the view from the view model. I went for the second option as it enabled binding to the PasswordBox.Password property (albeit indirectly) and didn't violate the use of MVVM in the process.
To get this working, take a look at the static PasswordBoxAssistant class on Samuel Jack's interesting blog. The class defines the attached properties that you can then use in your XAML markup, in my case, it resulted in the following markup:
In WPF, data binding enables you to hook a specific property in your data context (in my case, the view model) to a property on a user interface control. For example, the following XAML markup shows how the Text property on a TextBox can be bound to the FirstName property on the view model:
<TextBox Height="23"
Width="120"
Text="{Binding Path=FirstName, Mode=TwoWay}" />
As the Mode is set to TwoWay, any text changes by the user will result
in the FirstName property being updated in the view model, likewise, any
programmatic change to the FirstName view model property will result in
the user interface being updated (provided that your view model implements the INotifyPropertyChanged
interface and the setter of the FirstName property raises the
OnPropertyChanged event). In my case, I had a PasswordBox control in my
XAML markup that needed binding to a view model string property named
Password. Initially I tried this in XAML:
Width="120"
Text="{Binding Path=FirstName, Mode=TwoWay}" />
<PasswordBox Name="userPasswordBox"
Height="23"
Width="120"
Password="{Binding Path=Password, Mode=OneWay}" />
However, the Visual Studio XAML designer immediately underlined the 'Password="{Binding...' line and stated the error:
Height="23"
Width="120"
Password="{Binding Path=Password, Mode=OneWay}" />
A 'Binding' cannot be set on the 'Password' property of type 'PasswordBox'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.As the binding error message alludes to, it turns out that the Password property on the PasswordBox control is a standard .NET CLR property and this was a deliberate security-based design decision by Microsoft to prevent plain text passwords from sitting around in memory. In my case however, having the password sit in memory wasn't an issue.
Of course, if the application I was working on wasn't using MVVM, then I could easily have picked up the user entered password in the code-behind in a button click event handler (e.g. this.userPasswordBox.Password would return the plain password). But because the application was adhering to MVVM, the code-behind was pretty much empty (except for the usual constructor boilerplate code). Instead, all of the UI "events" were being handled by the view model via command objects exposed as public properties. This therefore made retrieving the password from the PasswordBox and assigning it to the view model Password property an annoyingly interesting problem!
After doing a bit of research on the web, I found a couple of solutions.
- Pass the PasswordBox control to the view model: In this solution, the PasswordBox control itself is passed back to the view model via a command parameter (which is hooked up to the "OK" button). The method that handles the command then gets the PasswordBox instance via an object type parameter which can be cast back into a PasswordBox. You can then retrieve the password easily through the PasswordBox.Password property.
- Use attached properties to enable binding: In this solution, attached properties (a special type of dependency property) are created, enabling you to use them on the PasswordBox control and bind to your view model property.
The first solution above works but I wasn't too happy with passing a UI component to my view model - it violates the MVVM design pattern principle of decoupling the view from the view model. I went for the second option as it enabled binding to the PasswordBox.Password property (albeit indirectly) and didn't violate the use of MVVM in the process.
To get this working, take a look at the static PasswordBoxAssistant class on Samuel Jack's interesting blog. The class defines the attached properties that you can then use in your XAML markup, in my case, it resulted in the following markup:
<PasswordBox Name="userPasswordBox"
Height="23"
Width="120"
ap:PasswordBoxAssistant.BindPassword="true"
ap:PasswordBoxAssistant.BoundPassword=
"{Binding Path=Password,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
/>
As shown in the markup above, you'll need to import the namespace that
the PasswordBoxAssistant class is defined in and add an alias to it (in
my case "ap"). Having done that, it's finally just a case of using the
attached properties to wire up the binding to your view model property.
Height="23"
Width="120"
ap:PasswordBoxAssistant.BindPassword="true"
ap:PasswordBoxAssistant.BoundPassword=
"{Binding Path=Password,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
/>
Sign up here with your email
ConversionConversion EmoticonEmoticon