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

Lesson 11 - Drawing on Canvas in C# .NET WPF

In the previous exercise, Solved tasks for C# .NET WPF lessons 6-10, we've practiced our knowledge from previous lessons.

In the previous lesson, Solved tasks for C# .NET WPF lessons 6-10, we finished our birthday reminder. We tried the basic form controls on it, as well as bindings and error handling. We're already able to create quite sophisticated applications. In today's C# .NET WPF tutorial, we're going to draw.

Drawing on Canvas

We'll create an application to manage the sales of cinema tickets. As we know, there are many seats in the cinema hall, and the application should show the cinema worker which seats are already occupied. You might be thinking of adding a Button control for each seat. However, if the cinema had 15 rows with 30 seats each, we'd have 450 Button controls. You probably expect there should be a better way than start typing Button1, Button2... And how would they be handled then? If we need to draw something more complex than just one or two pictures, we can use Canvas. The point is we place a single Canvas on the form and draw all we need on it.

We'll simplify the cinema application, it doesn't need to be complex. It'll be able to display only a single hall, which will be empty at the start. The user clicks on the seats to make them occupied, and then presses the "Save" button, which saves all the information about the occupancy of the hall to a simple txt file in the chosen location. We'll do the saving to learn how to work with dialogs.

Form Design

Create a new WPF Application, set the window title to Cinema hall administration. Set the WindowStartupLocation property to CenterScreen. This will ensure that the window will always appear in the center of the screen when the application is launched.

For the form design, we'll use the Grid control we already know very well from the previous lessons. The layout of the application isn't very complicated, so we can split the grid into two rows. The first row will contain the Canvas and the second one will contain a Button with the Save text. Add a Click event to the button and generate a Code Behind method that is called when the user clicks the button. Your form should look like this:

Drawing on Canvas in C# .NET WPF - Form Applications in C# .NET WPF

And here is its corresponding XAML code:

<Window x:Class="Cinema.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-;;compatibility/2006"
        xmlns:local="clr-namespace:Cinema"
        mc:Ignorable="d"
        Title="Cinema hall administration" WindowStartupLocation="CenterScreen" Height="450" Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="350"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <Canvas Name="MyCanvas" Margin="20 50 0 0"></Canvas>
        <Button Name="Save" Click="Save_Click" Content="Save" Grid.Row="1" Height="20" Width="100"></Button>
    </Grid>
</Window>

Logical Layer

You are probably not surprised that we're going to add a Cinema class to our application. It'll have single private field, a two-dimensional array of seats. If you haven't worked with a 2D array yet, you can think of it as a table. A one-dimensional (classic) array is actually just a single row. We then work with the 2D array exactly the same as with a one-dimensional array, except that we have to specify two coordinates (X and Y). In many languages, 2D arrays are created as array of arrays, but C# can define 2D arrays directly as follows:

class Cinema
{
    private bool[,] seats = new bool[30, 15];

}

The seats are of the bool type, because we only need to know whether they are vacant or occupied. 30 is the width of the array, 15 is its height.

We'll also add two private constants to the class, the first one defining the size of the rendered seat in pixels, and the other one defining the space between the seats also in pixels. Get used to constants. When you decide to change the seat size, then you'll just need to overwrite a single constant, without having to mess with the entire rendering code.

private const int size = 16;
private const int space = 2;

We can now move on to methods.

Insert Rectangles

We've already mentioned that we're going to draw on the canvas. Individual rectangles representing the seats will be represented as objects that we'll place on the canvas using a DrawRectangles() method. The canvas is of the Canvas type, and we'll request it as a parameter in the method. You'll need to add using System.Windows.Shapes to make the Rectangle class available. When we'll place individual rectangles on the Canvas, we'll always set their width and height to the size constant, and then set the appropriate color using the Fill property and the static Brushes class. It contains pre-made instances set to certain colors, just pick any.

Using two nested loops, we'll go through all the seats in the array and draw either a green or a red square on the canvas. We'll use the outer loop to go through the rows, and the inner loop to go through each column in the current row. The color (more precisely the brush) is determined by whether the seat at the given coordinates is true or false. The method's code will be as follows:

public void DrawRectangles(Canvas MyCanvas)
      {
          for (int j = 0; j < seats.GetLength(1); j++)
          {
              for (int i = 0; i < seats.GetLength(0); i++)
              {
                  Rectangle rectangle = new Rectangle
                  {
                      Height = size,
                      Width = size,
                  };

                  rectangle.Fill = seats[i, j] ? Brushes.Red : Brushes.Green;
                  MyCanvas.Children.Add(rectangle);

                  Canvas.SetLeft(rectangle, i * (size + space));
                  Canvas.SetTop(rectangle, j * (size + space));
              }
          }
      }

Notice that we don't use the values of 30 and 15 in the loops, but we use the GetLength() method with parameters 0 and 1 instead. This method is used to obtain the size of a two-dimensional array. 0 is the width, and 1 is the height (of course, it depends on us which dimension we treat as the height or the width).

Of course, we don't hard-code the array size because in the future we may want to increase / decrease it without having to search the entire code for the values 30 and 15. It's always better to avoid these problems and work with the array length instead.

It's worth mentioning the rendering of the rectangles themselves. We can see that we render them by adding each Rectangle instance as a child to our Canvas. Then, in order to display the rectangles exactly where we want, it's necessary to set up their positions. We use the static Canvas class, which contains the SetLeft() and SetTop() methods, which represent the coordinates of the upper-left corner of the rectangle. Since every seat is 16 pixels wide + 2 pixels of empty space, we need to multiply its coordinates by that value. If the i had the value of 2 (when drawing the third column), we'd draw the rectangle at the X coordinate of 36, rather than the 2 :) The same applies to the Y coordinate.

Wiring the Form and the Logic Layer

The base of the logic is complete, now let's wire it to the form. We'll go to the Code Behind of the form and create a private Cinema instance there:

private Cinema cinema = new Cinema();

Now it's really simple. On the cinema instance, we'll call the DrawRectangles() method and pass our Canvas to it, which we defined in the form, as a parameter. To render the seats when we launch the application, we'll put this code in the form constructor in the Code Behind.

public MainWindow()
{
    InitializeComponent();
    cinema.DrawRectangles(MyCanvas);
}

We really don't have to be ashamed of the result:

Drawing on anvas in C# NET WPF - Form Applications in C# .NET WPF

In the next lesson, Handling Rectangle Clicks in C# .NET WPF, we'll show how to change the seat state by clicking on it, and implement saving to a file. As always, you can download the project below, in case something went south for you.


 

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 155x (578.49 kB)
Application includes source codes in language C#

 

Previous article
Solved tasks for C# .NET WPF lessons 6-10
All articles in this section
Form Applications in C# .NET WPF
Skip article
(not recommended)
Handling Rectangle Clicks in C# .NET WPF
Article has been written for you by Radek Němec
Avatar
User rating:
3 votes
Activities