Get up to 80 % extra points for free! More info:

Lesson 5 - Code-Behind in C# .NET WPF and finishing the calculator

In the previous lesson, Designing a calculator form in C# .NET WPF, we made a form for a simple calculator in C# .NET. In today's lesson, we're going to work on the logical part of our application. Once we finish working on our app, we'll talk a little bit about how WPF works internally.

Code-Behind

The presentation layer of a WPF application is written in XAML. However, all XAML structures is the design. Each window has what is referred to as "code-behind" calling the application's logic layer. We are able to access this code from the Designer by right-clicking and selecting the "View Code" option. You could also access it using the CTRL + ALT + 0 shortcut, the zero on the alphanumeric keyboard, to move to the and Shift + F7 to move back to the Graphic designer.

Our form's code-behind looks like this (I've omitted namespaces):

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

There you will see that the form is represented by a class inherited from the Window class. A Window is a class that represents the Window control. Each control has its own class in WPF, which won't come as a surprise to you, knowing that C# is an object-oriented language :) The mysterious InitializeCom­ponents() method is called from inside the form's constructor. It internally parses the XAML and creates instances of each control. That is how forms are created.

Naming controls

If we wanted to work with a control from the code-behind, the first thing we would have to do is assign a name to it. Legacy versions of Visual Studio didn't incorporate control naming whatsoever. Later versions of VS added the feature and would assign names automatically based on the element type and an incremental value of the amount of controls of that type in use, e.g. TextBlock1.

Back to our calculator. In order to perform calculations, we will need to read the numbers from both TextBoxes, read the operation from the ComboBox, and write the result into the TextBlock. We will also need to handle the button's click event.

Let's move back to the XAML section and change each of the control's Name attributes to the following values: number1TextBox, number2TextBox, operationComboBox, resultTextBlock, calculateButton. Note that the names are suffixed with the type of control that they are. For larger, more complex forms, this helps clear things out quite a bit. Try to avoid names like button1, textBlock1, etc.

Events

WPF is built upon event models. In our calculator, the only event we have to react to is when the button is clicked. When we double-click the button in the Graphic designer, Visual Studio will send us to the code-behind, where the following method will be generated:

private void calculateButton_Click(object sender, RoutedEventArgs e)
{
}

The method above will be called when the user clicks the button. We'll go over how all of this is achieved later on. Events can be assigned and removed in the Properties window. All you have to do is select the control and click on the flash icon which moves from the properties section to the events section. The button next to it moves you back to properties:

Events in WPF in C# .NET Visual Studio - Form Applications in C# .NET WPF

Let's insert following code into the generated method:

// preparing variables
string operation = operationComboBox.Text;
double number1 = double.Parse(number1TextBox.Text);
double number2 = double.Parse(number2TextBox.Text);
double result = 0;

// calculations
if (operation == "+")
    result = number1 + number2;
else if (operation == "-")
    result = number1 - number2;
else if (operation == "*")
    result = number1 * number2;
else if (operation == "/")
{
    if (number2 != 0)
        result = number1 / number2;
    else
        MessageBox.Show("You cannot divide by zero");
}
resultTextBlock.Text = result.ToString();

The first few lines prepare variables into which we will store values returned from the controls. We can access both the TextBox's text and the ComboBox's selected item text through the Text property. Here, we can see why it's a good idea to name controls properly. The application doesn't know how to deal with invalid user input. If that were to happen, the program would terminate with a value parsing exception. We will teach you user input validation in the next couple of lessons. Once you get there, you could come back and add user validations if you'd like.

Calculating the result should be clear to you all, seeing as how we made a calculator application in the first C# .NET course. The only added feature we need to include is checking whether or not we divide by zero. If that is the case, we will bring up what is known as a MessageBox, which I'm sure you are very familiar with from other applications (pop-up message windows in pretty much every app). To do so, we use the static MessageBox class, which ends up looking something like this:

MessageBox in C# .NET - Form Applications in C# .NET WPF

Convert the result to a string because we will be assigning it to a Text property. At the end of the calculation method, assign the result to the TextBlock that displays the result. Done! Now, try the application out on some test data.

Complete calculator in C# .NET WPF - Form Applications in C# .NET WPF

WPF under the hood

Before we move on to the more advanced material, I'll show you how WPF works internally in a few, short paragraphs. If you haven't finished the object-oriented course till the end, you probably won't fully understand the rest of this lesson. Nonetheless, you don't absolutely have to know every detail about how WPF works to develop practical WPF applications.

Partial class

Moving back to the MainWindow class (in the Code-Behind). The more perceptive amongst you may have noticed that the MainWindow class is partial (marked with the partial modifier). Meaning that it is defined in multiple files. The rest of the declaration is hidden and can be accessed by clicking on the InitializeCom­ponent() method and pressing F12 which moves you to the method implementation.

Doing so takes us to a nasty looking class that was automatically generated by Visual Studio along with the new window. There, we see two methods: InitializeCom­ponent() and Connect().

The InitializeCom­ponent() method reads the XAML and calls the LoadComponent() method on it. Notice the CodeDOM namespace attributes above the class. These are classes for generating C# code at run-time. Which is exactly what this method does, it parses the XAML and creates control instances based on their definition in XAML. The Connect() method "magically" connects methods in the Code-Behind.

Here, we see the EventHandler in use:

this.calculateButton.Click += new System.Windows.RoutedEventHandler(this.calculateButton_Click);

The method also exposes the individual controls under their names, which is done by the ugly switch :) We will never interfere with this file in any way, but it is important for you to understand how WPF works.

Creating controls at run-time

By now, you might be thinking, controls are ordinary classes, right? Then, can they be added to the form by creating an instance in the Code-Behind instead of adding them to the XAML? The answer to that is, yes. Technically, we don't need XAML at all. However, designing forms would be extremely difficult and messy if we didn't.

Let's look back at our Grid in the XAML section:

<Grid Margin="0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="50"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="50"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
</Grid>

To achieve the same results in the Code-Behind, you would have to do something like this:

Grid grid = new Grid();
grid.Margin = ((Thickness)(TypeDescriptor.GetConverter(typeof(Thickness)).ConvertFromInvariantString("0")));
ColumnDefinition columnDefinition = new ColumnDefinition();
columnDefinition.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.ColumnDefinitions.Add(columnDefinition);
ColumnDefinition columnDefinition2 = new ColumnDefinition();
columnDefinition2.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("50")));
grid.ColumnDefinitions.Add(columnDefinition2);
ColumnDefinition columnDefinition3 = new ColumnDefinition();
columnDefinition3.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.ColumnDefinitions.Add(columnDefinition3);
ColumnDefinition columnDefinition4 = new ColumnDefinition();
columnDefinition4.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("50")));
grid.ColumnDefinitions.Add(columnDefinition4);
ColumnDefinition columnDefinition5 = new ColumnDefinition();
columnDefinition5.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.ColumnDefinitions.Add(columnDefinition5);
RowDefinition rowDefinition = new RowDefinition();
rowDefinition.Height = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.RowDefinitions.Add(rowDefinition);
RowDefinition rowDefinition2 = new RowDefinition();
rowDefinition2.Height = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("30")));
grid.RowDefinitions.Add(rowDefinition2);

I'm sure you agree that the code above is not very clear (keep in mind that the code above would only be a small part of the program). That is why XAML, which has a clear and simple tree structure, is used to design applications.

In some cases, it may be useful to set something or create a specific part of the application in the Code-Behind. Which is why I gave you this "Behind-the-Scenes" look, so you would know that it can be done.

In the next lesson, Solved tasks for C# .NET WPF lessons 1-5, we'll start programming a more robust application. A birthday reminder application. The source code calculator we made today is, as always, available for download below.

In the following exercise, Solved tasks for C# .NET WPF lessons 1-5, we're gonna practice our knowledge from previous lessons.


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 83x (108.3 kB)
Application includes source codes in language C#

 

Previous article
Designing a calculator form in C# .NET WPF
All articles in this section
Form Applications in C# .NET WPF
Skip article
(not recommended)
Solved tasks for C# .NET WPF lessons 1-5
Article has been written for you by David Capka Hartinger
Avatar
User rating:
7 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities