Sunday, July 20, 2014

C# WPF: StackPanels and WrapPanels

So in yesterday's article, "C# WPF: CheckBoxes", we learned about CheckBoxes, content controls, and also used a StackPanel to position controls underneath each other. We'll learn a bit more about the StackPanel in this article, and we'll also compare it to another layout control - the WrapPanel.

So let's create a new WPF application, and replace the default <Grid> with a <StackPanel> of CheckBoxes. Your MainWindow's XAML code should look something like this:

<Window x:Class="CsWpfStackPanelWrapPanel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBlock>Choose the pizzas you want to order:</TextBlock>
        <CheckBox>Margherita</CheckBox>
        <CheckBox>Funghi</CheckBox>
        <CheckBox>Pepperoni</CheckBox>
        <CheckBox>Quattro Stagioni</CheckBox>
        <CheckBox>BBQ Chicken</CheckBox>
        <CheckBox>Ranch Special</CheckBox>
        <CheckBox>Vegeterian</CheckBox>
        <CheckBox>Ham and Mushroom</CheckBox>
        <CheckBox>Meat Lovers</CheckBox>
    </StackPanel>
</Window>

As we saw in yesterday's article, a <StackPanel> will lay out any controls inside it as a vertical list by default. You should be able to see this in Visual Studio's preview:


That's because the <StackPanel> has an Orientation property, which by default is set to Vertical. See what happens when we change this to Horizontal:


So as you can see, the <StackPanel> is also useful to position controls horizontally next to each other.

However, as you can see from the preview, we have so many checkboxes that they didn't all fit in the 525-pixel-wide window, so that the ones further right can't be seen because they fall outside the window's boundaries.

Let us now see what happens if we change the <StackPanel> to a <WrapPanel>:


So the <WrapPanel> is actually quite similar to the <StackPanel>, but there are two main differences. The first, as you've probably noticed, is that a <WrapPanel> will wrap controls that don't fit on one line onto subsequent lines.

The second is that a <WrapPanel>'s Orientation is Horizontal by default. You can change it to Vertical to have controls listed and wrapping vertically instead. Let's add a bunch of other checkboxes just to see how this works:


Great! In this article we've seen how to use the <StackPanel> and <WrapPanel> layout controls. Both of these can be used to list items in either vertical or horizontal lists. They are two key differences between them:
  • The <StackPanel> will always use keep controls in one row or column, even if they don't fit; the <WrapPanel> will instead wrap controls onto subsequent rows/columns when they don't fit.
  • The default Orientation of the <StackPanel> is Vertical, while that of the <WrapPanel> is Horizontal.
Stay tuned for more WPF goodness! :)

Saturday, July 19, 2014

C# WPF: CheckBoxes

In the previous article, "C# WPF: Basic Login Form using a Grid", we have already taken a look at some simple WPF controls including the TextBlock, TextBox and PasswordBox, and Button. In this article we will take a look at another control: the CheckBox.

So the first thing we're going to do is create a new WPF application, and we're going to create a sort of small questionnaire that will ask the user certain questions about them.

Let's add our first CheckBox by replacing the default <Grid> with the following in XAML:

    <CheckBox Name="PlayedTennisField">Have you ever played tennis?</CheckBox>

You can see how the text we entered inside the <CheckBox> element is actually displayed next to the CheckBox itself:


You can achieve the same thing by instead setting the Content property:

    <CheckBox Name="PlayedTennisField"
        Content="Have you ever played tennis?" />

CheckBoxes and Buttons are examples of Content Controls: they have that Content property (equivalent to setting the element's inner text, as we have seen above) which can be used to set text describing the control.

Let's add another CheckBox underneath the one we have:

    <CheckBox Name="BrokenLawField"
        Content="Have you ever broken the law?" />

You'll notice that the program will now refuse to compile:


Visual Studio is complaining because we have two items within the <Window> element. In fact, <Window> is also a content control, and that means that we can only have one element directly inside it. However, we can have many different controls inside a window by placing them inside something that isn't a content control, as we did with the <Grid> in the previous article.

This time, however, we'll use a <StackPanel> instead. A <StackPanel> may contain many different controls, and will by default display them underneath each other as in a list. Let's use it as follows:

    <StackPanel>
        <CheckBox Name="PlayedTennisField"
            Content="Have you ever played tennis?" />
        <CheckBox Name="BrokenLawField"
            Content="Have you ever broken the law?" />
    </StackPanel>

By placing the <StackPanel> directly inside the <Window>, the <Window> has just one element as its content, but at the same time we're indirectly allowing it to contain several others:


WPF provides us with two separate events that we can use to take action when a checkbox is either checked or unchecked. Add an event handler for each to the first checkbox by beginning to type the word "Checked", then pressing TAB twice when intellisense comes up, and then do the same for "Unchecked". Your first CheckBox's XAML should now look something like this:

    <CheckBox Name="PlayedTennisField"
        Content="Have you ever played tennis?"
        Checked="PlayedTennisField_Checked"
        Unchecked="PlayedTennisField_Unchecked" />

Note that you can also set both events to use the same handler.

You will now find a couple of event handlers that have been generated for you in your codebehind file (MainWindow.xaml.cs). If you add some code inside them, you can then run the program and test that it is indeed being called when the checkbox's state changes:


CheckBoxes are usually used for Boolean (true/false) data entry; however in some cases it may make sense to leave the value unset, in the sense that neither a true nor a false value is selected (i.e. a null value). In this case, the user may feel uncomfortable about telling the program whether they broke the law or not.

This situation is supported by using a ThreeState CheckBox. All we need to do is set the IsThreeState property of the second CheckBox to True. Clicking on the CheckBox will now cycle through checked, indeterminate (the shaded or unset state) and unchecked:


There is a separate event we can use to take action when the indeterminate state is set, and that is the Indeterminate event. You can now set up the second CheckBox as follows, and add whatever code you need in the three event handlers in the codebehind:

        <CheckBox Name="BrokenLawField"
            Content="Have you ever broken the law?"
            IsThreeState="True"
            Checked="BrokenLawField_Checked"
            Indeterminate="BrokenLawField_Indeterminate"
            Unchecked="BrokenLawField_Unchecked" />

Finally, let us see how we can extract the values from the checkboxes. To do this, let us first add a button beneath the checkboxes:

        <Button Name="SubmitButton"
            Content="Submit"
            Click="SubmitButton_Click" />

As we did in the previous article, you can access the controls from the codebehind by name. You'll find that the CheckBoxes have an IsChecked property, but it's a little awkward because it's not a regular bool, but a "bool?", which means "nullable boolean". In other words this can have values of not just true and false, but also null. It should be clear at this stage that this null value is there to support the indeterminate state.

So let's add some code to retrieve the state of the CheckBoxes and display a summary of the options that were selected:

        private void SubmitButton_Click(object sender, RoutedEventArgs e)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("Played tennis: ");
            sb.Append(NullableBooleanToString(this.PlayedTennisField.IsChecked));
            sb.Append(Environment.NewLine);

            sb.Append("Broke the law: ");
            sb.Append(NullableBooleanToString(this.BrokenLawField.IsChecked));

            MessageBox.Show(sb.ToString(), "Summary");
        }

        private string NullableBooleanToString(bool? value)
        {
            if (value == true)
                return "yes";
            else if (value == false)
                return "no";
            else
                return "unknown";
        }

And here is a test run:


Very good! In this article we have learned how to use the WPF CheckBox, including its events, threestate feature, and how to retrieve its state from the codebehind. We have also learned a little about content controls, and how to use a stack panel for to lay out controls in a vertical list.

Thanks for reading, and check back for more WPF tutorials! :)

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