Gigi Labs

Please follow Gigi Labs for the latest articles.
Showing posts with label xaml. Show all posts
Showing posts with label xaml. Show all posts

Saturday, July 5, 2014

C# WPF: Basic Login Form using a Grid

Hello everyone! :)

The articles I've written about WPF so far have dealt with how to do specific, sometimes advanced stuff.

In this article, I'd like to start covering some of the basics of WPF that a new WPF developer will need to know. WPF (Windows Presentation Foundation) is Microsoft's latest technology for developing desktop applications - the successor of the earlier but still strong Windows Forms technology. You can do WPF using Visual Studio or SharpDevelop, but unfortunately, it is not supported in the Mono environment and so you can't use it on non-Windows operating systems.

In today's article we'll learn how to use some basic controls and lay them out in a grid to create a login form. When I say "controls", it means any visual element - buttons, text fields, checkboxes, etc. Let's discover WPF by diving right into it! :)

In Visual Studio, go File -> New -> Project... and among the categories on the left, make sure "Windows" (under "Visual C#", not "Visual Basic") is selected. Then, in the middle part, select "WPF Application". Give it a name, select a location, and you're good to go.


When the project loads up, you'll see the screen divided into a few parts. On the right, there's the Solution Explorer where you can see your source files as usual. The main portion of the screen contains a preview of what your window currently looks like, and below it is an editor where you can write XAML to build the contents of your window.


In WPF, the visual stuff like buttons, text fields and the like are defined using XAML. If you look at the default XAML, there's just a <Window> element that contains an empty <Grid> element. You can build up just about any user interface using just <Grid> and a few other layout elements. Let's take a look at what the <Grid> can do for us.

If you open up the Toolbox panel on the left, you'll find that there are many different items you can just drag onto the empty window. Drag a button onto it.


The button is added to your window thanks to this XAML that has been added inside your <Grid>:

    <Button Content="Button"
        HorizontalAlignment="Left"
        Margin="159,113,0,0"
        VerticalAlignment="Top"
        Width="75" />

Among the attributes, you'll notice there is a Margin. In this case, this is being used to position the button relative to the grid. The values are relative to the left and top of the window. Try changing them and see how the button moves. This usage of the <Grid> makes it pretty much like a canvas, where you can put controls wherever you like, even on top of each other.

A more common way to use a <Grid> is to lay out controls in a tabular format, with rows and columns. To do this, you'll first need to define a number of rows and columns inside the grid. Remove the button and set up the grid like this instead:

    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <!-- Controls go here -->

    </Grid>


What we're doing here is setting up the <Grid> to have two rows and two columns. In fact, you should now be able to see the window divided into four cells in the preview:


We can now start adding controls in the part where I put a placeholder comment. Let's replace the comment with this <TextBlock>:

        <TextBlock Grid.Row="0"
                   Grid.Column="0"
                   Text="Username:" />

If you've ever used HTML, you're probably huffing and puffing at this stage. In WPF, you need to define a row and column structure beforehand, and you need to tell the controls into which row and column they belong. We do this by using the Grid.Row and Grid.Column properties. Setting both to zero puts the control into the top-left cell.

<TextBlock> is a simple control that displays text (similar to a label); the user cannot interact with it. Its text can be set using the Text property as above.

Let us now add a text field where the user can enter his username:

        <TextBox Grid.Row="0"
                 Grid.Column="1"
                 Name="UsernameField" />

We use the <TextBox> element to define a text field. Apart from positioning it in the top-right cell, we're also giving it a Name - we'll use this later to retrieve the contents of the text field from code.

Now, let's add another <TextBlock> and text field for the password. However, for passwords we should use the <PasswordBox>, which provides security benefits including masking the input text. Add the following elements for the password:


        <TextBlock Grid.Row="1"
                   Grid.Column="0"
                   Text="Password:" />

        <PasswordBox Grid.Row="1"
                     Grid.Column="1"
                     Name="PasswordField" />

The text fields can be a bit difficult to see in the preview. Press F5 to run the application and see the actual window:



OK, it looks something like a login form. But can we make it look better? One thing we can do is limit the height of each row, so that the fields don't look so massive. And while we're at it, let's add a third row in which we will later add a button to actually perform the login operation:


        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>

This has the following effect on the form (note how third row is nowhere to be seen since it doesn't have any controls just yet):


We can then center the <TextBlock>s vertically to align them better with the text fields. We do this by setting VerticalAlignment="Center" on each of them. Another improvement we can make is to set a small margin on all the controls (Margin="2"), to space them out a little:


There's still some room for improvement, but let's now turn our attention to getting the login form working. First, we need a button. Add the following:

        <Button Grid.Row="2"
                Grid.Column="0"
                Grid.ColumnSpan="2"
                Name="LoginButton"
                Margin="2">Login</Button>

You'll notice a new property in there: Grid.ColumnSpan. This allows the button to take up more than one cell - in fact we're make it span across two columns.

Add a Click property. As you're about to set its value, Intellisense prompts you to create a new event handler:


If you press TAB, the value is filled out for you: Click="LoginButton_Click". But what is this LoginButton_Click? You see, the file you've been working on so far is MainWindow.xaml, but as you can see in Solution Explorer, there's a file called MainWindow.xaml.cs that is associated with it. This is called the codebehind, and your logic code can go here. In it, you'll find some code:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void LoginButton_Click(object sender, RoutedEventArgs e)
        {

        }
    }

Aside from the constructor, you'll notice there is the event handler for the login button that we generated a moment ago. In here, you can work with the fields using the name we gave them in the XAML...

            string username = this.UsernameField.Text;
            string password = this.PasswordField.Password;

...and also perform any logic you need, in this case the actual login. Let's get this working by adding the following code:

            if (username == "flynn" && password == "carsen")
                MessageBox.Show("Logged in!", "Success");
            else
                MessageBox.Show("Invalid username or password.", "Login failed");

You can now run the application and try it out:


You'll notice that you can't press ENTER to login; you have to click on the button. You can resolve this by adding IsDefault="True" to the button in the XAML. This will make it the default button for the window, and pressing ENTER will cause its Click event to fire.

Very good! We have made a working login form in WPF.

In this article, we learned about some of the basic WPF controls, and also two different ways to use a <Grid> to lay out other controls. We created a login form to explore basic usage of all these controls. We also attached an event handler to our button control, and added logic in the codebehind to perform the login.

Thanks for reading, and stay tuned for more! :)

Saturday, June 21, 2014

C# WPF/MVVM: Why You Shouldn't Bind PasswordBox Password

Hi! :)

In WPF circles, the PasswordBox control has generated quite a bit of discussion. The thing is that you can access the password entered by the user using the Password property, but it's not a dependency property, and MVVM purists don't like the fact that they can't bind it to their ViewModel.

In this article, I'm going to show how the password can be bound in the ViewModel despite this limitation. And I'm also going to show you why it's a very bad idea to do this. This article is a little advanced in nature, and assumes you're familiar with WPF and MVVM.

Right, so let's set up something we can work with. Create a new WPF application, and then add a new class called MainWindowViewModel. In your MainWindow's codebehind (i.e. MainWindow.xaml.cs), set up your window's DataContext by adding the following line at the end of your constructor:

            this.DataContext = new MainWindowViewModel();

Right, now let's add a couple of properties to our MainWindowViewModel that we'll want to bind to:

        public string Username { get; set; }
        public string Password { get; set; }


Now we can build our form by editing the XAML in MainWindow.xaml. Let's go with this (just make sure the namespace matches what you have):

<Window x:Class="CsWpfMvvmPasswordBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Login"
        Height="120"
        Width="325">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="20" />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="90" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <TextBlock Text="Username:"
            Grid.Row="0"
            Grid.Column="0"
            Margin="2" />
        <TextBox Text="{Binding Path=Username}"
            Grid.Row="0"
            Grid.Column="1"
            Margin="2" />

        <TextBlock Text="Password:"
            Grid.Row="1"
            Grid.Column="0"
            Margin="2" />
        <PasswordBox Password="{Binding Path=Password}"
            Grid.Row="1"
            Grid.Column="1"
            Margin="2" />

        <Button Content="Login"
            Grid.Row="2"
            Grid.Column="1"
            HorizontalAlignment="Right"
            Width="100"
            Margin="2" />
    </Grid>
</Window>


Now, you'll notice right away that something's wrong when you see the blue squiggly line at the password binding:


Oh no! What are we going to do now? If we can't bind the password, and have to somehow retrieve it from the control, then we'll break the MVVM pattern, right? And we MVVM knights-in-shining-armour can't afford to deviate from the path of light that is MVVM. You see, the Password property can't be bound specifically because it shouldn't, but let's say that we're like most other developers and we're so blinded by this MVVM dogma that we don't care about the security concerns and we want an MVVM-friendly solution.

Well, no problem! It turns out that there actually is an MVVM-friendly way to bind the password - see the PasswordBoxAssistant and PasswordHelper implementations. To get up and running, let's add a new PasswordBoxHelper class to our project, and add the implementation from the second link:

    public static class PasswordHelper
    {
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.RegisterAttached("Password",
            typeof(string), typeof(PasswordHelper),
            new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));

        public static readonly DependencyProperty AttachProperty =
            DependencyProperty.RegisterAttached("Attach",
            typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, Attach));

        private static readonly DependencyProperty IsUpdatingProperty =
            DependencyProperty.RegisterAttached("IsUpdating", typeof(bool),
            typeof(PasswordHelper));


        public static void SetAttach(DependencyObject dp, bool value)
        {
            dp.SetValue(AttachProperty, value);
        }

        public static bool GetAttach(DependencyObject dp)
        {
            return (bool)dp.GetValue(AttachProperty);
        }

        public static string GetPassword(DependencyObject dp)
        {
            return (string)dp.GetValue(PasswordProperty);
        }

        public static void SetPassword(DependencyObject dp, string value)
        {
            dp.SetValue(PasswordProperty, value);
        }

        private static bool GetIsUpdating(DependencyObject dp)
        {
            return (bool)dp.GetValue(IsUpdatingProperty);
        }

        private static void SetIsUpdating(DependencyObject dp, bool value)
        {
            dp.SetValue(IsUpdatingProperty, value);
        }

        private static void OnPasswordPropertyChanged(DependencyObject sender,
            DependencyPropertyChangedEventArgs e)
        {
            PasswordBox passwordBox = sender as PasswordBox;
            passwordBox.PasswordChanged -= PasswordChanged;

            if (!(bool)GetIsUpdating(passwordBox))
            {
                passwordBox.Password = (string)e.NewValue;
            }
            passwordBox.PasswordChanged += PasswordChanged;
        }

        private static void Attach(DependencyObject sender,
            DependencyPropertyChangedEventArgs e)
        {
            PasswordBox passwordBox = sender as PasswordBox;

            if (passwordBox == null)
                return;

            if ((bool)e.OldValue)
            {
                passwordBox.PasswordChanged -= PasswordChanged;
            }

            if ((bool)e.NewValue)
            {
                passwordBox.PasswordChanged += PasswordChanged;
            }
        }

        private static void PasswordChanged(object sender, RoutedEventArgs e)
        {
            PasswordBox passwordBox = sender as PasswordBox;
            SetIsUpdating(passwordBox, true);
            SetPassword(passwordBox, passwordBox.Password);
            SetIsUpdating(passwordBox, false);
        }
    }

You will also need to add the following usings at the top:

using System.Windows;
using System.Windows.Controls;


Now, let's fix our Password binding. First, add the following attribute to the Window declaration in the XAML so that we can access our project's classes (adjust namespace as needed):

        xmlns:local="clr-namespace:CsWpfMvvmPasswordBox"

Then update the PasswordBox declaration to use the PasswordBoxHelper as follows:

        <PasswordBox local:PasswordHelper.Attach="True"
            local:PasswordHelper.Password="{Binding Path=Password}"
            Grid.Row="1"
            Grid.Column="1"
            Margin="2" />

That did it! The project now compiles.

Now, let's see why what we have done is a very stupid thing to do. To do this, we're going to need this WPF utility called Snoop, so go over to their website, download it, and install it using the .msi file.

Run Snoop. All you'll see is a thin bar with a few buttons. On the right hand side, there is a button that looks like a circle (in fact it's supposed to be crosshairs). If you hover over it, it will explain how to use it:


Run the WPF application we just developed. Enter something into the PasswordBox, but shhhh! Don't tell anyone what you wrote there! :)

Next, drag those crosshairs from Snoop onto the WPF window:


When you let go, a window opens. In the treeview to the left, you can navigate through the mounds of crap that internally make up our simple application. When you find the PasswordBox, you'll also find the PasswordHelper:


...and as you can see, the PasswordHelper keeps the password exposed in memory so anyone who knows what he's doing can gain access to it. With a program like Snoop, anyone can access passwords that are bound.

There are a couple of lessons to take from this.

First, don't ever bind passwords in WPF. There are other alternatives you can use, such as passing your entire PasswordBox control as a binding parameter - although this sounds extremely stupid, it's a lot more secure than binding passwords. And arguably, it doesn't break the MVVM pattern.

Secondly, don't be so religious about so-called best practices such as MVVM. Ultimately they are guidelines, and there are many cases such as this where there are more important things to consider (in this case, security). For something as simple as a login window, it's much more practical to just do without MVVM and do everything in the codebehind. It isn't going to affect the scalability, maintainability, robustness [insert architectural buzz-word here], etc of your application if you make an exception that is rational.

Before ending this article, I would like to thank the person who posted this answer to one of my questions on Stack Overflow. That answer helped me understand the dangers of binding passwords, and provided the inspiration for this article. As further reading, you might also want to read this other question (and its answer) which deals with the security of processing passwords in memory in general (not just data binding).

That's all for today. Happy coding! :)