Showing posts with label exceptions. Show all posts
Showing posts with label exceptions. Show all posts

Sunday, 17 March 2013

Binding to PasswordBox in WPF using MVVM

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:
<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:
<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:
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.

Tuesday, 4 December 2012

Dynamic Typing and Extension Methods

Earlier today I came across an interesting runtime exception of type RuntimeBinderException. The specific exception message was:

'System.DateTime' does not contain a definition for 'ToUKFormat'

'ToUKFormat' is a custom extension method on DateTime that I wrote and was in-scope at compile time when I called it. So what caused the runtime exception? It turns out that you cannot call an extension method on an object/value that is dynamically typed. In my case, I was using the dynamic ViewBag in an ASP.NET MVC application to store a DateTime value - then in my view, I was retrieving that value from the ViewBag and calling a custom written extension method on it ('ToUKFormat'). In razor syntax, I was doing something similar to:

@ViewBag.Foo.Bar.ToUKFormat()
I have given the solution further below but if you want to reproduce the problem in a simple console application, you can use the following code:

namespace DynamicRuntimeIssue
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic today = DateTime.Today;

            Console.WriteLine(today.ToUKFormat()); // Exception...
            Console.ReadLine();
        }
    }

    static class DateTimeExtensions
    {
        public static string ToUKFormat(this DateTime dateTime)
        {
            return dateTime.ToString("dd/MM/yyyy");
        }
    }
}
When you run the code above, you will encounter the aforementioned exception. Of course, if you appropriately type the 'today' variable to a DateTime rather than dynamic, then you'll find that it all works as expected. However, if you cannot avoid the use of dynamic, like for example in my case where I was using ViewBag - then you can overcome this exception by calling your extension method as if it was any normal static method. You call the method and pass-in your object/value through as an actual parameter. Following on from the example above, you would therefore have:

Console.WriteLine(DateTimeExtensions.ToUKFormat(today));
Rather than:

Console.WriteLine(today.ToUKFormat());
If you want to understand why you can't call extension methods on a dynamic type, then go over to this stackoverflow question where someone else has had the same issue - you will particularly want to read Eric Lippert's response dated March 15 2011.