Графический интерфейс windows forms

Первое приложение с .NET CLI

Последнее обновление: 25.11.2023

Для создания графических интерфейсов с помощью платформы .NET применяются разные технологии — Window Forms, WPF, UWP.
Однако наиболее простой и удобной платформой до сих пор остается Window Forms или сокращенно WinForms. Данное руководство ставит своей целью дать понимание принципов создания графических интерфейсов с помощью технологии WinForms
и работы основных элементов управления.

Создадим первое приложение на C# и Windows Forms. Что нам для этого потребуется? Прежде всего необходим текстовый редактор для написания кода программы.
Можно взять любой понравившийся текстовый редактор, например, Visual Studio Code

Также для компиляции и запуска программы нам потребуется .NET SDK. Для его установки перейдем на официальный сайт по ссылке
.NET SDK

После установки .NET SDK для первого проекта определим какую-нибудь папку. Например, в моем случае это будет папка C:\dotnet\winforms\helloapp.
Откроем терминал/командную строку и перейдем к созданной папке проекта с помощью команды cd

cd C:\dotnet\winforms\helloapp

В данном случае мы для создания и запуска проекта мы будем использовать встроенную инфраструктуру .NET CLI, которая устанавливается вместе с .NET SDK.

Для создания проекта в .NET CLI применяется команда dotnet new, после которой указывается тип проWindows Formsекта. Для создания проекта
Windows Forms применяется шаблон — winforms. Поэтому введем в терминале команду dotnet new winforms

C:\Users\eugen>cd C:\dotnet\winforms\helloapp

C:\dotnet\winforms\helloapp>dotnet new winforms
Шаблон "Приложение Windows Forms" успешно создан.

Идет обработка действий после создания...
Восстановление C:\dotnet\winforms\helloapp\helloapp.csproj:
  Определение проектов для восстановления...
  Восстановлен C:\dotnet\winforms\helloapp\helloapp.csproj (за 1,04 sec).
Восстановление выполнено.



C:\dotnet\winforms\helloapp>

После выполнения этой команды у нас будет создан следующий проект:

Структура проекта Windows Forms

Рассмотрим базовую структуру простейшего стандартного проекта Windows Forms:

  • helloapp.csproj: стандартный файл проекта C#, который соответствует назанию проекта (по умолчанию названию каталога) и описывает все его настройки.

  • helloapp.csproj.user: дополнительный файл проекта C#, который хранит специфичные для текущего пользователя настройки.

  • Form1.cs: содержит класс формы, которая по умолчанию запускается при старте приложения

  • Form1.Designer.cs: он содержит определение компонентов формы, добавленных
    на форму в графическом дизайнере (графический дизайнер Windows Forms на данный момент официально доступен только в Visual Studio)

  • Program.cs: определяет класс Program, который запускается при старте приложения и запускает форму Form1

Например, посмотрим на содержимое файла helloapp.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

Для компиляции приложения Windows Forms указаны следующие настройки:

  • OutputType: определяет выходной тип проекта. Должен иметь значение WinExe — то есть выполняемое приложение с
    расширением exe под Windows

  • TargetFramework: определяет применяемую для компиляции версию фреймворка .NET. Поскольку при создании проекта
    была выбрана версия .NET 8, а сам проект зависит от компонентов Windows, то здесь должно быть значение net7.0-windows

  • Nullable: подключает в проект функционалность ссылочных nullable-типов

  • UseWindowsForms: указывает, будет ли проект использовать Windows Forms (для этого устанавливается значение true)

  • ImplicitUsings: подключает в проект функциональность неявно подключаемых глобальных пространств имен

Запуск проекта

Проект по умолчанию не представляет какой-то грандиозной функциональности, тем не менее этот проект мы уже можем запустить. Итак, запустим проект. Для этого выполним команду
dotnet run

C:\dotnet\winforms\helloapp>dotnet run

При запуске запускается графическая форма, код которой определяет класс Form1:

Первое приложение на Windows Forms на С# с .NET CLI

Запуск приложения

Файл Program.cs определяет точку входа в приложение:

namespace helloapp;

static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }    
}

Метод Main снабжен атрибутом [STAThread]. Этот атрибут, грубого говоря,
необходим для корректной работы компонентов Windows. В самом методе сначала вызывается метод

ApplicationConfiguration.Initialize()

который устанавливает некоторую базовую конфигурацию приложения

Затем вызывается метод

Application.Run(new Form1());

в который передается объект отображаемой по умолчанию на экране формы.

То есть, когда мы запустим приложение, сработает метод Main, в котором будет вызван метод Application.Run(new Form1()),
благодаря чему мы увидим форму Form1 на экране.

Определение формы

Теперь посмотрим на определение формы и немного изменим его. Для этого откроем файл Form1.cs в текстовом редакторе. По умолчанию он выглядит следующим образом:

namespace helloapp;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
}

Класс формы — Form1 представляет графическую форму — фактически то окно, которое мы увидим на экране при запуске проекта.

Этот класс определяется как частичный (с модификатором partial) и наследуется от встроенного класса Form,
который содержит базовую функциональность форм.

В самом классе Form1 определен по умолчанию только конструктор, где вызывается метод InitializeComponent(), который выполняет инициализацию компонентов формы из файла дизайнера
Form1.Designer.cs. По сути именно код этого файла передается выше через вызов InitializeComponent()

Теперь изменим его код следующим образом:

namespace helloapp;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        // определяем кнопку
        Button button = new Button();
        // текст кнопки
        button.Text ="Click";
        // положение кнопки
        button.Location = new Point(50, 50);
        // размер кнопки
        button.Size = new Size { Width = 80, Height = 30 };
        // обработчик нажатия кнопки
        button.Click += (o, e) => MessageBox.Show("Hello METANIT.COM!");
        // добавление кнопки на форму
        this.Controls.Add(button);
    }
}

В данном случае на форму добавляется кнопка, и у нее настраиваются различные свойства, как текст, положение на форме, размер, а также обработчик нажатия.
Заново запустим проект, и нам отобразится та же форма, которая теперь будет содержать кнопку. И по нажатию на кнопку появится маленькое окно с сообщением.

Первое приложение на Windows Forms на С#

So far, all our programs have used the console, or terminal, as their interface. But in modern operating systems, graphical user interfaces (GUIs) are more common. C# has a variety of choices built in for creating graphical programs for Windows environment, and on this course, we’ll take a quick glance of one of the options.

Creating a new Windows Forms project

When we created our console projects, we used the command dotnet new …. We can create a graphical project in the same manner. This time, the command is dotnet new winforms.

NOTICE! If you are unable to create a project with the command above, you might have to change your Terminal in VSC to be PowerShell. Windows Forms programs can only be created and run through a Windows based terminal.

The program can be run with same commands as a console program, i.e. dotnet run. One major difference is in our csproj file, where we decide we are creating a Windows application.

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

</Project>

As you can see, the OutputType is now WinExe, meaning a Windows Executable file, and will have a file type .exe. With this, we will also find an executable file from our folders.

Windows Forms is one type of graphical interface that can be created with C#. We will be using it in our examples in this course.

Let’s create our first project.

NOTICE! This, and all the examples in this part, only work on Windows!

  • Create a folder where you want your project. The example will be in a folder called GuiProject.

  • To keep the folder structure neat, create a folder src in the project folder, and navigate there.

  • Run the command dotnet new winforms inside the src folder.

You will get a project structure something like this:

.
└── src
    ├── Form1.Designer.cs
    ├── Form1.cs
    ├── Program.cs
    ├── obj
    └── src.csproj

You can now test out your project, with dotnet run. You should get an program that looks like this:

Winform1

Hooray, the program opens! It does not do anything quite yet. Let’s look into our files, and then start adding some functionality.

// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace src
{
  static class Program
  {
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      Application.SetHighDpiMode(HighDpiMode.SystemAware);
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
  }
}

Our Main now has quite much more than we have created with text interfaces. The concept is still quite the same: The main functionality of Main is to trigger the program into running. The last line is the most interesting, with Application.Run(new Form1());. Let’s look at that Form1 now.

There are some parts of the code we have not handled yet, and won’t handle in this course, such as the comments with <summary>. Those are parts for Windows Fields Designer and its comments, and we shall not worry about them.

We do have to care about [STAThread]. “This attribute must be present on the entry point of any application that uses Windows Forms; if it is omitted, the Windows components might not work correctly. If the attribute is not present, the application uses the multithreaded apartment model, which is not supported for Windows Forms.”

// Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace src
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
  }
}

Nothing much happens in this file, except we call the method InitializeComponent();. You might notice, that the call is done as the class would call its own method, and that is quite true. This class is called a partial class, and it inherits Form. This means, that it implements some of the parts of Form, but not all. It is calling for a method from another partial class. This time, it is partial of Form1 itself. Let’s have a look.

// Form1.Designer.cs
namespace src
{
  partial class Form1
  {
    /// <summary>
    ///  Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    ///  Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    ///  Required method for Designer support - do not modify
    ///  the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.components = new System.ComponentModel.Container();
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(800, 450);
      this.Text = "Form1";
    }

    #endregion
  }
}

Once again, we see quite much of the comments created by the designer, but also a different comment, starting with a #. Those are for collapsing code out of view if certain options are enabled, but for us, yet another part not to worry about. Let’s clean up the file:

namespace src
{
  partial class Form1
  {
    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
      this.components = new System.ComponentModel.Container();
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(800, 450);
      this.Text = "Form1";
    }
  }
}

Much more readable. Now we can see we have two mehtods, Dispose and InitializeComponent, and we recognize the latter was called by the other partial. Let’s look at that first.

The method contains several lines, which each have a specific function for creating our program window:

this.components = new System.ComponentModel.Container();
  • Is used for storing elements which do not have a visual representation during the runtime, but might be needed otherwise, such as a timer running in the background.
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
  • Is used to define, which kind of method is used for scaling. By default, the scaling is Font, but could be also Dpi, Inherit or None.
this.ClientSize = new System.Drawing.Size(800, 450);
  • Is quite self-explanatory, defining the size of the window we draw.
  • Defines the text in the up-left corner of our window.

Our other method, protected override void Dispose(bool disposing), is automatically created, and takes care of disposing (or handing over to garbage collection) the elements of our program. It is used for example when we close the program window. For our intents and purposes, we don’t have to touch it.

First functionality — “Hello World”

As always, we start with a classic “Hello World” in our code. Let’s add some content to our window.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace src
{
  public partial class Form1 : Form
  {
    // Added TextBox1
    private TextBox textBox1;
    public Form1()
    {
      InitializeComponent();
    }
  }
}

As our other form is a partial for this one, we want to create the priave TextBox here. We could, just as well, create it in the next file, and bring the correct namespace with it. Let’s keep it here, though, for at least now.

namespace src
{
  partial class Form1
  {

    private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
      this.components = new System.ComponentModel.Container();
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(800, 450);
      // More meaningful text
      this.Text = "Hello World Application";

      // Call this.textBox1 and initialize
      this.textBox1 = new System.Windows.Forms.TextBox();
      // Give the textbox some content
      this.textBox1.Text = "Hello World";
      // Make it ReadOnly, so it cannot be edited by the users
      this.textBox1.ReadOnly = true;
      // Center to the screen
      this.textBox1.Location = new System.Drawing.Point((this.Width - this.textBox1.Width) / 2, (this.Height - this.textBox1.Height) / 2);
      // Add to controls
      this.Controls.Add(this.textBox1);
    }
  }
}

That’s a lot of code for a simple string.

this.textBox1 = new System.Windows.Forms.TextBox();
  • We initialize a standard textbox object by getting it from the correct namespace. As you might notice, we do not have any using directives in this part of the form, but we could just as well have them here.
this.textBox1.Text = "Hello World";
  • Creates the text content inside the textbox.
this.textBox1.ReadOnly = true;
  • Prevents the users from editing the textbox. You can take this line away and see what happens.
this.textBox1.Location = new System.Drawing.Point((this.Width - this.textBox1.Width) / 2, (this.Height - this.textBox1.Height) / 2);
  • Not required, but nice to have. We center an item by giving it a new Point as Location. Location is the top-left corner of the item.
this.Controls.Add(this.textBox1);
  • Earlier we mentioned components, which have the non-displayable components. In Controls, we store everything we want to show to the user.

Winform2

More functionality — Button

Quite a lot of work has gone into getting a simple one-liner into our GUI. Let’s create ourselves a button next:

// Form1.cs

// plenty of usings

namespace src
{
  public partial class Form1 : Form
  {
    private TextBox textBox1;
    // Added the private Button
    private Button button1;

    public Form1()
    {
      InitializeComponent();
    }
  }
}
namespace src
{
  partial class Form1
  {
    private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
      this.components = new System.ComponentModel.Container();
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(800, 450);
      this.Text = "Hello World Application";

      this.textBox1 = new System.Windows.Forms.TextBox();

      this.textBox1.Text = "Hello World";
      this.textBox1.ReadOnly = true;
      this.textBox1.Location = new System.Drawing.Point((this.Width - this.textBox1.Width) / 2, (this.Height - this.textBox1.Height) / 2);
      this.Controls.Add(this.textBox1);

      // Button
      this.button1 = new System.Windows.Forms.Button();
      this.button1.Text = "Click me!";
      this.button1.Click += new System.EventHandler(ShowMessage);
      Controls.Add(this.button1);


    }

    // Button function
    private void ShowMessage(object sender, System.EventArgs e)
    {
      this.textBox1.Text = "Button Clicked!";
    }
  }
}

Let’s see what our button’s code does:

this.button1 = new System.Windows.Forms.Button();
  • Initialize a new button object
this.button1.Text = "Click me!";
  • Give the button a text
this.button1.Click += new System.EventHandler(ShowMessage);
  • Button’s functionality. On a Click, We Call an EventHandler, which takes as a parameter a method.
Controls.Add(this.button1);
  • Add to Controls so the button is visible

Winform3

private void ShowMessage(object sender, System.EventArgs e)
{
  this.textBox1.Text = "Button Clicked!";
}
  • Our method takes actually in two parameters, but we give it only one in our code. The second parameter, System.EventArgs e refers to Event Arguments, and in this case, it would be mouse click. As a crude simplification, as this is called inside the EventHandler, we can assume the event to be given (i.e. the mouse to be clicked), which triggers the method call.

  • In our method, we change our textBox1 text.

Winform4

As we can see, now that we did not give our button any specific location, it will start in the top-left corner. What happens, if we create 2 buttons?

// Very ugly copy-paste code
this.button1 = new System.Windows.Forms.Button();
this.button1.Text = "Click me!";
this.button1.Click += new System.EventHandler(ShowMessage);
Controls.Add(this.button1);

this.button2 = new System.Windows.Forms.Button();
this.button2.Text = "Click me instead!";
this.button2.Click += new System.EventHandler(ShowMessage);
Controls.Add(this.button2);

Winform5

We can see that only the first button is drawn. If we want to have the other button in another location, we have to define the new location. Let’s also make some other adjustements to the code:

this.button2 = new System.Windows.Forms.Button();
this.button2.Text = "Click me instead!";
this.button2.AutoSize = true;
this.button2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
this.button2.Location = new System.Drawing.Point(this.button1.Width+5, 0);
this.button2.Click += new System.EventHandler(ShowMessage); 
Controls.Add(this.button2);
this.button2.AutoSize = true;
this.button2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
  • With these we allow our button to grow in size, and make the whole text visible.
this.button2.Location = new System.Drawing.Point(this.button1.Width+5, 0);
  • We define our button location to start from the button 1 width + 5 pixels, but keeping the starting height the same. This way, we have our buttons side by side with a little gap between them:

Winform6

EventHandling

We have now already done some event handling with our buttons. But what do events and event handling actually mean?

An event is a message sent by an object to signal the occurrence of an action. The action can be caused by user interaction, such as a button click, or it can result from some other program logic, such as changing a property’s value, like we did with our text field earlier. The object that raises the event is called the event sender. The event sender doesn’t know which object or method will receive (handle) the events it raises. The event is typically a member of the event sender; for example, the Click event is a member of the Button class.

To respond to an event, you define an event handler method in the event receiver. This method must match the signature of the delegate for the event you are handling. In the event handler, you perform the actions that are required when the event is raised, such as collecting user input after the user clicks a button. To receive notifications when the event occurs, your event handler method must subscribe to the event.

The following example shows an event handler method named ShowMessage that matches the signature for the EventHandler delegate. The method subscribes to the Button.Click event.

private void ShowMessage(object sender, System.EventArgs e)
{
  this.textBox1.Text = "Button Clicked!";
}

An event handler can also have options. For example, our two buttons can have same handler but different functionality:

private void ShowMessage(object sender, System.EventArgs e)
{
  string buttonName = (sender as System.Windows.Forms.Button).Text;
  if (buttonName == "Click me!")
  {
    this.textBox1.Text = "Button Clicked!";
  }
  else
  {
    this.textBox1.Text = "Other Button Clicked!";
  }
}

Our first actual program

Let’s create something more meaningful with our new skills, like a simple Calculator.

Our project structure looks something like this:

.
└── src
    ├── Calculators
    │   ├── Calculator.Designer.cs
    │   └── Calculator.cs
    ├── GuiCalculator.csproj
    ├── Program.cs
    ├── bin
    └── obj

And you can find the code from here!, which is part of the exercise repository.

We shall not have the whole code here, but you can find it from the link above. Let’s take a look at some of the highlights:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GuiCalculator
{
  static class Program
  {
    [STAThread]
    static void Main()
    {
      Application.SetHighDpiMode(HighDpiMode.SystemAware);
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Calculator());
    }
  }
}
  • Our Main class is still very nice and clean, as it should. Even though our program is larger, the Main has been kept simple.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GuiCalculator
{
  public partial class Calculator : Form
  {
    // Calculation variables
    private double accumulator = 0;
    private string lastOperation;

    // All the buttons are already initialized here
    private Button button0 = new Button();
    private Button button1 = new Button();
    private Button button2 = new Button();
    /* 
    . . .

    ALL THE BUTTONS
    */
    private TextBox results = new TextBox();

    // Very clean constructor
    public Calculator()
    {
      InitializeComponent();
    }
  }
}
private void InitializeComponent()
{
  this.components = new System.ComponentModel.Container();
  this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
  this.ClientSize = new System.Drawing.Size(300, 300);
  // Text at the top
  this.Text = "Calculator";

  // Button sizes
  int buttonWidth = 60;
  int buttonHeight = 60;

  // Results box
  this.results.Text = "0";
  this.results.Font = new System.Drawing.Font("Arial", 30);
  this.results.Width = 300;
  // Move text to right side
  this.results.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
  this.results.ReadOnly = true;
  this.results.Location = new System.Drawing.Point(0, 0);

  // Define button 7, top-left
  this.button7.Location = new System.Drawing.Point(0, buttonHeight);
  this.button7.Font = new System.Drawing.Font("Arial", 20);
  this.button7.Height = buttonHeight;
  this.button7.Width = buttonWidth;
  this.button7.Text = "7";
  this.button7.Click += new System.EventHandler(AddToString);
  
  /* 
  
  AND A LOT MORE CODE HERE 
  
  */
  
  // Add all the buttons to the Controls
  this.Controls.Add(this.results);

  this.Controls.Add(button0);
  this.Controls.Add(button1);
  /*
  Add all the items to the Controls. ALL OF THEM.
  */
  • First we create our program window like previously
  • Then we define couple of variables to be used in our layout
  • Define the TextBox for results and calculations
    • Initial text is “0”
    • Larger font
    • Width set to whole program
    • Users cannot edit the text
    • Start from top-most-corner (default)
  • Define the first button, as it starts from top-left after the results box
    • Start from below the TextBox
    • Larger font
    • Use the variables to make it a square
    • Define what reads on the button
    • Add a handler

You can read the rest of the button codes from the repository. They are all quite alike. Most interesting are our event handlers:

private void AddToString(object sender, System.EventArgs e)
{
  string number = (sender as System.Windows.Forms.Button).Text;
  if ((results.Text.Contains(".") && number == ".") || (results.Text == "0" && number == "."))
  {
    return;
  }
  if (results.Text == "0")
  {
    results.Text = number;
  }
  else
  {
    results.Text += number;
  }
}
  • With this event handler we add the numbers and decimals to our TextBox.Text.
    • To keep numbers clean, we check where we can add a decimal
      • Not in the beginning of the string, and only one per number
  • If the TextBox.Text is 0, replace with the input number
  • Else add to the end
private void CalculateResult(object sender, System.EventArgs e)
{
  string operation = (sender as System.Windows.Forms.Button).Text;
  double currentValue = System.Convert.ToDouble(results.Text, System.Globalization.CultureInfo.InvariantCulture);
  if (operation == "C")
  {
    this.accumulator = 0;
  }
  else if (lastOperation == "+")
  {
    accumulator += currentValue;
  }
  else if (lastOperation == "-")
  {
    accumulator -= currentValue;
  }
  else if (lastOperation == "*")
  {
    accumulator *= currentValue;
  }
  else if (lastOperation == "/")
  {
    accumulator /= currentValue;
  }
  else
  {
    accumulator = currentValue;
  }

  lastOperation = operation;

  if (operation == "=")
  {
    results.Text = accumulator.ToString();
  }
  else
  {
    results.Text = "0";
  }
}
  • This is more tricy. It goes through all our operations buttons.
string operation = (sender as System.Windows.Forms.Button).Text;
  • We save our operation name into a string, by parsing the name from the Button.Text. The sender is recognized from the method parameter.
double currentValue = System.Convert.ToDouble(results.Text, System.Globalization.CultureInfo.InvariantCulture);
  • We parse the variable currentValue from the results.Text, into a double.
  if (operation == "C")
  {
    this.accumulator = 0;
  }
  • If we want to reset our calculation, we press “C”. It sets the accumulated total to 0.
else if (lastOperation == "+")
{
  accumulator += currentValue;
}
  • With “+” we add our currentValue to the accumulated total. Rest of the operations function similarly, according to their signs.

  • We actually use the variable lastOperation instead of the operation, since we only want to show the result if there is already a previous operation done.

    • For example, if we input to our calculator

We will see the results show us

So the previous result is only shown when the next operation is pressed. Remember, that even the total is an operator.

lastOperation = operation;

if (operation == "=")
{
  results.Text = accumulator.ToString();
}
else
{
  results.Text = "0";
}
  • After the operation, we save our operation to the variable lastOperation, so we can check it for the next operation press.

  • If the operation was “=”, we show the total of the calculation so far. Otherwise, we show 0 when a operator is clicked, so we are waiting for another input.

Conclusion

Graphical interfaces require quite much code to achieve even a simple program. The applications get quite complicated quite fast. This part was meant to demonstrate the possibilities of GUI, not be an exhaustive guide.

There are easier ways of doing GUI for C#, such as using Visual Basic. Learning these is left for you.


Ксожалению, конструктор форм (Forms Designer) не поддерживается в C++ Тем не менее, вы можете использовать конструктор форм (Forms Designer) в С#, и потом перенести полученный с помощью С# код графического интерфейса пользователя в программу на C++ Для переноса графического интерфейса пользователя в программу на C++ необходимы дополнительные усилия, и, в большинстве случаев, такой перенос особой пользы не дает Как правило, C++ не используют для разработки пользовательских графических интерфейсов, вместо этого применяется подход смешения языков, в котором для создания почьзовательского интерфейса используется С#, а в других аспектах разработки проекта из разных соображений используется C++ Поскольку вам, скорее всего, придется часто создавать графический интерфейс пользователя для многих приложений, в этой паве мы преследуем две цели Во-первых, мы хотим научить вас совместно использовать код на С# и C++ Даже если вы в основном программируете на C++, стоит ознакомиться с С# и конструктором форм (Forms Designer), — у вас появится возможность использовать те мощные инструментальные программные средства, которые не поддерживаются в C++ Во-вторых, мы приведем в пример один из немногих случаев, когда перенос кода графического интерфейса пользователя из С# в программу на C++ является целесообразным В главе представлено несколько пар примеров кода графических пользовательских интерфейсов до и после переноса

Ключевым средством взаимодействия пользователя с компьютером является графический пользовательский интерфейс (Graphical User Interface, GUI) Из этой главы вы узнаете, как создавать графический пользовательский интерфейс с помощью классов Windows Forms (Формы Windows), которые находятся в NET Framework На практике программирование Windows-приложений предполагает экстенсивное использование различных инструментальных средств и мастеров, которые намного упрощают этот процесс Однако все указанные средства автоматизации заслоняют то, что лежит в основе создания графического пользовательского интерфейса Поэтому сначала мы рассмотрим основы создания графических пользовательских интерфейсов Иными словами, мы научимся создавать простые приложения Windows с самого начала, пользуясь только комплексом инструментальных средств разработки программ NET Framework SDK Это значит, что вначале мы будем создавать простые приложения Windows без применения каких-либо специальных сервисных программ Будут рассмотрены основы рисования с помощью Windows Forms (Формы Windows) с применением шрифтов и кистей, а также необходимые обработчики событий Мы объясним принципы обработки событий в Windows Forms (Формы Windows) и реализуем обработчики событий мыши. С помощью Windows Forms (Формы Windows) мы также реализуем меню и соответствующие обработчики событий. Кроме того, мы рассмотрим управляющие элементы, а после этого изучим среду Visual Studio.NET, посредством которой можно без труда создать простой графический пользовательский интерфейс на С#. С помощью конструктора форм (Forms Designer) добавим в форму управляющие элементы, создадим меню, добавим обработчики событий и другие полезные функциональные возможности. При желании полученный в результате проект на С# можно потом перенести на C++. В заключение будут рассмотрены диалоговые окна и такой элемент управления, как список.

Windows Forms (Формы Windows) — это та часть каркаса .NET Framework, которая поддерживает создание приложений со стандартным графическим пользовательским интерфейсом (GUI) на платформе Windows. Среди классов Windows Forms (Формы Windows) есть обширный набор классов для создания сложных графических пользовательских интерфейсов. Эти классы можно использовать в приложениях, написанных на любом языке .NET

Рис. 6.1. Упрощенная схема иерархии классов Windows Forms (Формы Windows)

Как правило, ваше приложение будет содержать главное окно, которое реализовано с помощью некоторого класса MyForm, производного от класса Form (Форма). На рис 6.1 изображено, какое место ваш класс MyForm занимает в иерархии классов Windows Forms (Формы Windows).

Для ознакомления с классами Windows Forms (Формы Windows) полезно будет создать простое приложение SimpleForm (Простая форма) в несколько шагов. Ни на одном из этих шагов мы не будем использовать средства проектирования Visual Studio. Используя интерфейс командной строки, необходимо запустить командный файл build. bat.

Приложение SimpleForm (Простая форма) — скелет стандартного приложения Windows. Вот код приложения SimpleForm (Простая форма), созданный на шаге 0:

Класс Forml является производным от System:: Windows:: Forms:: Form (Cистема::Windows::Формы::Форма). В классе System::Windows::Forms::Application (Система::Windows::Формы::Приложение) есть статические методы для управления приложением, например Run (Выполнить) и Exit (Выход). Метод WinMain создает новую форму и запускает ее в качестве главного окна приложения.

Обратите внимание, что в примерах этой главы, написанных на C++, вместо имени функции main (главная) в качестве точки входа используется WinMain. В принципе можно в функции main (главная) в рамках консольного приложения реализовать все возможности графического интерфейса пользователя. Но при этом подходе придется создать бездействующее консольное окно, что в приложениях с графическим пользовательским интерфейсом совсем ни к чему. Если же использовать WinMain вместо main (главная), то в программе не создаются консольные окна, а сразу создается главное окно.

Конструктор формы инициализирует форму. Значение в поле Size (Размер) определяет размер формы в пикселях. Поле Text (Текст) определяет заголовок, который отображается в области заголовка окна новой формы.

Ключевым классом Windows Forms (Формы Windows) является базовый класс Form (Форма). Этот класс содержит обширный набор функций, которые наследуются разрабатываемыми нами классами форм, производными от класса Form (Форма).

Чтобы создать приложение, нужно выполнить из командной строки командный файл build.bat. А чтобы запустить командный файл, откройте окно DOS, перейдите в папку SimpleFormXStep(), и введите в командной строке build (компоновка). Помните, что . перед этим необходимо правильно установить значения переменных среды. Для этого достаточно выполнить Visual Studio.NET Command Prompt.

По умолчанию будет откомпилирован исполняемый файл Windows. В исходном коде приложения находятся директивы fusing, в которых указаны используемые библиотеки .NET: System.dll,System.Drawing.dllи System.Windows.Forms.dll.

После того, как вы откомпилировали приложение с помощью командного файла, можете запустить его, введя в командной строке SimpleForm (Простая форма). Вы также можете запустить приложение в проводнике Windows, дважды щелкнув на файле SimpleForm.exe. На рис. 6.2 изображен внешний вид этого простого приложения. И хотя приложение SimpleForm (Простая форма) совсем примитивное, в нем заложено множество возможностей, унаследованных созданным нами классом, который является производным от класса Form (Форма). Окно приложения можно перетаскивать по экрану, изменять его размер, свертывать, развертывать, в нем можно открывать системное меню (щелкнув кнопкой мыши в верхнем левом углу окна) и т.д.

В Visual Studio.NET есть инструментальное средство под названием Spy++ (Шпион++). Эта программа «шпионит» за окнами, чтобы иметь представление о том, что происходит внутри окна. Чтобы в Visual Studio запустить Spy++ (Шпион++), нужно воспользоваться меню Tools (Сервис). Запустите версию приложения SimpxeForm.exe, полученную на шаге 0, а затем запустите Spy++ (Шпион++). Выберите Spy (Шпион) Find Window (Найти окно) — появится диалоговое окно Find Window (Найти окно). В этом диалоговом окне установите переключатель Messages (Сообщения), как на рис. 6.3

Рис. 6.2. Скелет приложения \Vindowb Forms (Формы Windows) (Шаг 0)

Рис. 6.3. Инструмент Finder Tool (Средство поиска) служит для поиска окна — объекта шпионажа

Левой кнопкой мыши перетащите инструмент Finder Tool (Средство поиска) (в диалоговом окне Find window (Найти окно) этот инструмент отображается в виде специальной пиктограммы — перекрестия) на окно приложения SimpleForm (Простая форма), а потом щелкните на кнопке ОК. Теперь в окно программы-шпиона Spy++ будут выводиться сообщения, информирующие обо всех взаимодействиях с окном. Окно программы-шпиона Spy++ показано на рис. 6.4.

Чтобы обрабатывать события, приложения Windows должны иметь специальную структуру Операционная система Windows в ответ на действие пользователя, например щелчок кнопкой мыши, выбор меню или ввод символов с клавиатуры, посылает приложению сообщение Приложения Windows должны иметь такую структуру, которая позволяет реагировать на эти сообщения

Удобство создания Windows-программ с помощью классов NET Framework состоит в том, что программировать можно на очень высоком уровне абстракции На шаге 0 вы уже убедились, насколько просто создать приложение В последующих разделах мы будем добавлять в приложение новые основные свойства графических пользовательских интерфейсов, и таким образом проиллюстрируем основы создания графических пользовательских интерфейсов с помощью классов Windows Forms (Формы Windows)

В приложении, созданном на первом шаге, будет показано, как отобразить на форме текст На рис. 6.5. можно увидеть, как выглядит это приложение при выполнении

Рис. 6.4. Окно Messages (Сообщения) программы Spy++(Шпион++)

Рис. 6.5. Отображение текста на простой форме (Шаг 1)

Вывод данных в программах Windows сильно отличается от вывода данных в аналогичных консольных приложениях, где для этого используется метод Console: :WriteLine Вычерчивание результата в окне часто называют закрашиванием или закраской Закрашивание выполняется в ответ на сообщение «раскрасить», WM_PAINT Такой способ закрашивания по требованию гарантирует, что если окно будет накрыто каким-либо другим окном, а затем открыто снова, то содержимое окна будет отображено корректно

Еще одно отличие выполнения вывода в Windows-программах от выполнения вывода в консольных приложениях состоит в том, что необходимо определить все детали Например, нужно указать координаты области рисования, «кисть», которой будет выполняться рисование, шрифт текста, и так далее Вот код приложения, созданного на шаге 1

//SimpleForm.cpp - Шаг 1 
// Эта версия отображает приветствие 
fusing  
#using  
#using  
#using  
using namespace System; 
// использование пространства имен Система; 
using namespace System::Windows:.Forms; 
// использование пространства имен Система.:Windows::Формы; 
using namespace System::Drawing; 
// использование пространства имен Система::Рисование; 
_gc class Forml : public Form 
// класс сборщика мусора Forml: общедоступная Форма 
{ 
private: // частный 
float x, у; // с плавающей точкой Brush *pStoget_Graphics()->Graphics::DrawString // Графика 
("Hello, Window Forms", Font, 
// "Привет, Формы Window ", Шрифт, 
pStdBrush, x, у); 
} 
public: 
static void Main() { 
Application.:Ran(new Forml); 
// Приложение:: Выполнить (новая Форма), 
} 
}; 
int _stdcall WinMain( 
long hlnstance, // дескриптор текущего экземпляра 
long hPrevInstance, // дескриптор предыдущего экземпляра 
long IpCmdLine, // командная строка 
int nCmdShow // состояние отображения ) 
{ 
Forml::Main(); 
return 0; 
}

Для того чтобы рисовать с помощью Windows Forms (Формы Windows), нужно переопределить виртуальный метод OnPaint Класс PaintEventArgs содержит объект Graphics в качестве свойства, доступного только для чтения Класс Graphics, который принадлежит пространству имен System: -Drawing (Система Рисунок), содержит методы рисования

Параметры метода Drawstring

  • выводимая строка,
  • шрифт (Font (Шрифт) — свойство класса Form (Форма), которое определяет шрифт, по умолчанию применяемый для вывода текста в форму),
  • используемая кисть,
  • координаты в пикселях (числа типа float (с плавающей точкой))
  • В качестве стандартной кисти используется черная кисть SolidBrush

Графический пользовательский интерфейс (GUI) управляется событиями приложение выполняет действия в ответ на события, вызванные пользователем, например, на щелчок кнопкой мыши или выбор пункта меню Каждая форма или элемент управления имеет заранее определенный набор событий Например, у каждой формы есть код, обрабатывающий событие MouseDown (Кнопка мыши нажата)

В Windows Forms (Формы Windows) применяется модель обработки событий NET, в которой делегаты используются для того, чтобы связать события с обрабатывающими их методами В классах Windows Forms (Формы Windows) используются групповые делегаты Групповой делегат содержит список связанных с ним методов Когда в приложении происходит событие, управляющий элемент возбуждает событие, вызвав делегат для этого события Потом делегат вызывает связанные с ним методы

Для того чтобы добанить де iciar к событию, в C++ используется перегруженный оператор += Мы добавляем метод Forml_MouseDowr к событию MouseDown (Кнопка мыши нажата)

MouseDown += new MouseEventHandler 
(this, Forml_MoaseDown),

Вскоре мы увидим этот код в программе

Документацию, касающуюся событий и их обработки, можно найти в справочнике по NET Framework ( NET Framework Reference) На рис 6 6 показаны предопредепенные события, связанные с классом Form (Форма)

Событие MouseDown (Кнопка мыши нажата) является одним из предопределенных событий класса Control (Элемент управления), от которого порожден класс Form (Форма)

public: _event MouseEventHandler* MouseDown;

А вот и объявление обработчика этого события, MouseEventHandler

public _gc _delegate void MouseEventHandler( 
Object* sender, // отправитель 
MouseEventArgs* e 
) ;

Рис. 6.6. Документация по событиям класса Form (Форма)

В качестве параметра обработчик событий получает объект класса Мои seEventArgs (производный от класса EventArgs) Свойства этого объекта доступны только для чтения и содержат информацию, связанную с данным событием

  • Button (Кнопка) определяет, какая кнопка была нажата,
  • Clicks (Щелчки) определяет, сколько раз была нажата и отпущена кнопка,
  • Свойство Delta (Дельта) является счетчиком оборотов колесика мыши;
  • X и Y — координаты точки, в которой находился указатель в момент нажатия кнопки мыши

На шаге 2 мы внесем изменения в приложение, чтобы при щелчке любой кнопкой мыши строка с приветствием перемещалась на место щелчка На рис. 6.7 можно увидеть, что после щелчка кнопкой мыши строка действительно переместилась

Рис. 6.7. Перемещение текста по щелчку кнопкой мыши (Шаг 2)

//SimpleForm.cpp - Шаг 2 
// SimpleForm.срр - 2 
// Эта версия отображает приветствие, которое может быть перемещено 
// щелчком кнопки мыши 
fusing  
fusing  
fusing  
fusing  
using namespace System; 
// использование пространства имен Система; 
using namespace System::Windows. Forms; 
// использование пространства имен Система::Windows::Формы; 
using namespace System::Drawing; 
// использование пространства имен Система:: Рисунок; 
_go class Forml : public Form 
// класс сборщика мусора Forml: общедоступная Форма 
{ 
private: // частный 
void InitializeComponent() 
{ 
MouseDown += new MouseEventHandler // новый обработчик 
// события 
(this, Forml_MouseDown); 
} 
float x, у; // с плавающей точкой 
Brush *pStdBrush; // Кисть public: 
Forml() 
{ 
InitializeComponent(); Size = // Размер 
*_nogc new System::Drawing::Size(300,200); // Размер 
Text = "Simple Form - Step 2"; // Текст = "Простая Форма - Шаг 2"; x = у = 10; 
pStdBrush = new SolidBrush(Color::Black) ; // Красить::Черным 
} 
protected- // защищенный void Forml_MouseDown 
(Object *pSender, MouseEventArgs *pmea) 
{ 
x = pmea->X; у = pmea->Y; 
Invalidate(); I 
}

Во время инициализации программа связывает метод Forml_MouseDown с событием MouseDown (Кнопка мыши нажата) Этот метод устанавливает координаты текста, х и у, равными координатам точки, в которой находился указатель в момент щелчка Для того чтобы понять роль метода Invalidate (Считать недействительным), закомментируйте его и снова скомпонуйте код Щелкните мышью, чтобы перенести текст Что произойдет в результате9 Приветствие останется на том же месте Потом накройте окно приложения каким-либо другим окном, а потом снова откройте его Теперь вы увидите, что строка перемещена

Метод Invalidate (Считать недействительным) определен в базовом классе Control (Элемент управления) Существует несколько переопределенных версий этого метода Каждая из них заставляет считать недействительной определенную область управляющего элемента и посылает элементу управления сообщение о перерисовывании Метод, который не содержит параметров, заставляет считать недействительным весь управляющий элемент Для того чтобы максимально уменьшить объем перерисовывания, в более сложном приложении можно ограничиться тем, что недействительным будет считаться прямоугольник

На шаге 2М создания нашего приложения мы реализуем два разных обработчика события MouseDown (Кнопка мыши нажата). Второй обработчик по щелчку кнопкой мыши просто отображает окно сообщения.

//SimpleForm.срр - Шаг 2М 
// Эта версия имеет два обработчика событий для MouseDown 
_gc class Forml : public Form 
// класс сборщика мусора Forml: общедоступная Форма 
{ 
private: // частный 
void InitializeComponent() 
{ 
MouseDown += new MouseEventHandler 
(this, Forml__MouseDown) ; 
MouseDown += new MouseEventHandler 
(this, ShowClick); 
} 
void Forml_MouseDown 
(Object *pSender, MouseEventArgs *pmea) 
{ 
x = pmea->X; 
у = pmea->Y; 
Invalidate(); } 
void ShowClick (Object *pSender, MouseEventArgs *pmea) 
{ 
MessageBox::Show("Mouse clicked!!'"); 
// "Мышь щелкнула!!! }

На шаге 3 мы введем в наш пример обработку еще одного события, а именно, события KeyPress (Нажатие клавиши), а также покажем, как в событии MouseDown (Кнопка мыши нажата) различать, какая кнопка была нажата, левая или правая.

Обработка событий, вызванных правой и левой кнопкой мыши

Для того чтобы определить, какая кнопка мыши была нажата, нужно использовать свойство Button (Кнопка) параметра MouseEventArgs. Правую кнопку мыши будем использовать для удаления строки с приветствием, которая хранится в элементе данных str класса StringBuilder.

void Forml_MouseDown 
(Object *pSender, MouseEventArgs *pmea) 
{ 
if (pmea->Button == МоиseButtons::Left) // если левая кнопка 
{ 
x = pmea->X; 
у = pmea->Y; 
} 
else if (pmea->Button == MouseButtons::Right) // если правая 
// кнопка 
{ 
pStr = new StringBuilder(); 
} 
Invalidate() ; 
}

Событие Keypress (Нажатие клавиши)

На шаге 3 мы научимся обрабатывать событие KeyPress (Нажатие клавиши). Каждый раз, когда пользователь нажмет клавишу, в конец строки приветствия будет добавлен соответствующий символ. Обратите внимание, что вместо класса String (Строка) используется класс StringBuilder, который более эффективен в этой ситуации. Объект String (Строка) — стационарный (неизменяемый), то есть, для того, чтобы реализовать добавление символов в конец строки, нужно постоянно удалять одни объекты String (Строка) и создавать другие.

StringBuilder *pStr; 
void Forml_KeyPress 
(Object *pSender, KeyPressEventArgs *pmea) 
{ 
pStr—>Append(pmea->KeyChar) ; // Добавляем в конец 
Invalidate() ; 
}

Также, как и на шаге 2, необходимо вызвать метод Invalidate (Считать недействительным), для того, чтобы принудительно перерисовать окно приложения после сделанных изменений. На рис. 6.8 показано окно приложения SimpleForm (Простая форма), после удаления исходного текста и ввода нового.

Рис. 6.8. Испытываем события мыши и нажатия клавиши на клавиатуре (Шаг 3)

Все пользователи Windows-приложений хорошо знакомы с меню, которые представляют собой простой механизм выбора команд. В языках .NET меню реализуется в самой программе. Иными словами, для меню файл ресурсов не нужен.

На шаге 4 мы добавим в наше приложение Simple Form простое меню Для того чтобы выйти из программы, пользователь должен выбрать File => Exit (Файл => Выход), как на рис. 6.9.

Рис. 6.9. Шаг 4 Добавление в форму меню File => Exit (Файл => Выход)

//SimpleForm.срр - Шаг 4 
_gc class Forml : public Form 
// класс сборщика мусора Forml: общедоступная Форма 
{ 
private: // частный 
void InitializeComponent() 
{ 
pMainMenul = new MainMenu (); 
pMenuFile = new Menultem (); 
pMenuExit = new Menultem (); 
// mainMenul 
Menultem* pMainMenulItems[] = {pMenuFile}; 
pMainMenul->get_MenuItems() 
->AddRange(pMainMenulItems); // Меню File 
pMenuFile->set_Index(0); 
Menultem* pMainFileltems[] = {pMenuExit}; 
pMenuFile->get_MenuItems() 
->AddRange(pMainFileltems); 
pMenuFile->set_Text("File"); // Файл 
// Меню Exit 
pMenuExit->set_Index{0); 
pMenuExit->set_Text("Exit"); // Выход 
pMenuExit->Click += new System::EventHandler // Щелчок (this, MenuExit_Click); 
Menu = pMainMenul; // Меню 
MouseDown += new MouseEventHandler 
(this, Forml_MouseDown); 
KeyPress += new KeyPressEventHandler 
(this, Forml_KeyPress); } 
float x, у; // с плавающей точкой 
Brush *pStdBrush; // Кисть 
StringBuilder *pStr; 
Menultem *pMenuExit; 
Menultem *pMenuFile; 
MainMenu *pMainMenul; 
public: 
private: // частный 
void MenuExit_Cliok( 
Object *pSender, EventArgs *pea) 
{ 
Application::Exit(); // Приложение:: Выход 
}

В методе initializeComponent создается иерархическая структура меню, представленная экземпляром класса MainMenu (Главное меню). Меню состоит из объектов Menultem, каждый из которых является отдельной командой меню Каждый объект Menultem является командой приложения или командой родительского меню для других пунктов подменю В нашем приложении мы связываем объект MainMenu (Главное меню) с объектом Form (Форма), присваивая свойству Menu (Меню) объекта Form (Форма) значение MainMenu (Главное меню)

Когда в этой главе мы позже обсудим конструктор форм (Forms Designer), вы увидите, что меню можно создать и так нужно просто перетянуть элемент управления MainMenu (Главное меню) с панели инструментов на форму. Конструктор форм (Forms Designer) позаботится о генерации нужного шаблонного кода.

Как и в случае других событий Windows Forms (Формы Windows), с событием связывается его делегат Щелчок на пункте меню приводит к выполнению соответствующей команды

void InitializeComponent() 
{ 
pMenuExit->Click += new System::EventHandler // Щелчок 
(this, MenuExit_Click); 
} 
void MenuExit_Click( 
Object *pSender, EventArgs *pea) 
{ 
Application::Exit(); // Приложение::Выход 
}

В программе, которую мы только что рассматривали, объект pMainMenul является управляющим элементом. Данный объект — указатель на экземпляр класса MainMenu (Главное меню). Управляющий элемент — это объект на форме, который придает форме новые функциональные возможности. Управляющий элемент автоматически выполняет много заданий «по поручению» формы. Использование управляющих элементов упрощает программирование, так как программисту не надо беспокоиться о рисовании, о том, следует ли считать представление формы недействительным, не нужно думать о графических элементах и так далее. Для того чтобы реализовать простое меню с самого начала, нам пришлось написать довольно много кода. Управляющие элементы, которые реализуются с помощью объектов, содержат богатый, вполне пригодный для повторного использования код.

На шаге 5 создания приложения SimpleForm (Простая форма) мы используем управляющий элемент TextBox (Поле) для отображения строки с приветствием. В более ранних версиях приложения строку можно было переместить щелчком левой кнопки мыши и удалить щелчком правой кнопки мыши. Можно было также ввести свою собственную строку с приветствием. Теперь, применив управляющий элемент TextBox (Поле), вы получите полноценные возможности редактирования. Управляющий элемент TextBox (Поле) позволяет в любом месте строки вставить символы, вырезать и вставить текст (с помощью комбинаций клавиш Ctrl+X и Ctrl+V соответственно) и так далее. Все возможности редактирования поддерживаются управляющим элементом TextBox (Поле). На рис. 6.10 изображено окно приложения после того, как текст приветствия был перемещен, и мы ввели некий собственный текст.

Это новая версия программы. Обратите внимание на то, что она значительно проще предыдущей, хотя и имеет гораздо более богатые функциональные возможности. Нет больше необходимости использовать переменные экземпляра для координат и текста строки приветствия (теперь эта информация хранится в управляющем элементе pTxtGreeting типа TextBox (Поле)). Не нужен больше метод OnPaint, так как управляющий элемент TextBox (Поле) знает, как нарисовать себя. Можно также избавиться от кисти. Теперь не нужно обрабатывать событие KeyPress (Нажатие клавиши), потому что оно автоматически обрабатывается управляющим элементом TextBox (Поле), притом весьма аккуратно.

Рис. 6.10. Шаг 4: текст приветствия отображается с помощью управляющего элемента Text Box (Поле)

//SimpleForm.cpp - Шаг 5 
_gc class Forml : public Form 
// класс сборщика мусора Forml: общедоступная Форма 
{ 
private: // частный 
void InitializeComponent() 
{ 
// текст приветствия (text greeting) pTxtGreeting = new TextBox; pTxtGreeting->Location =
// Местоположение 
* _nogc new Point(10, 10); // новая точка pTxtGreeting->Size = // Размер 
(*_nogc new struct Size(150, 20)); // новый Размер pTxtGreeting->Text = "Hello, Windows Forms";
 // Текст = "Привет, Формы Windows"; 
Controls->Add(pTxtGreeting); // Добавить 
} 
float x, у; // с плавающей точкой 
Brush *pStdBrush; // Кисть 
Menultem *pMenuExit; 
Menultem *pMenuFile; 
MainMenu *pMainMenul; 
TextBox *pTxtGreeting; 
protected: // защищенный void Forml_MouseDown 
(Object *pSender, MouseEventArgs *pmea) { 
if (pmea->Button == MouseButtons::Left) 
// если кнопка левая 
{ 
pTxtGreeting->Location = // Местоположение 
*_nogc new Point(pmea->X, pmea->Y); // новая точка } 
else if (pmea->Button == MouseButtons::Right) // если кнопка правая { 
pTxtGreeting->Text = ""; // Текст } } 
};

Управляющий элемент TextBox (Поле) удобен в использовании. В инициализирующей части программы мы создаем объект TextBox (Поле) и определяем значения его свойств Location (Местоположение), Size (Размер) и Text (Текст). Мы добавляем новый управляющий элемент к коллекции управляющих элементов Controls (Управляющие элементы) этой формы. В обработчике событий мыши мы перемещаем управляющий элемент, изменив значение свойства Location (Местоположение). С помощью свойства Text (Текст) управляющего элемента TextBox (Поле) можно удалить строку с приветствием.

И хотя вполне реально создать приложение Windows Forms (Формы Windows), используя в командной строке только комплекс инструментальных средств разработки программ .NET Framework SDK, на практике подобную работу намного проще выполнить с помощью Visual Studio.NET. К сожалению, в Visual Studio.NET нет средств для генерирования проекта пусковой системы на управляемом C++ на основе Form (Форма), и управляемый C++ не поддерживает конструктор форм (Forms Designer). Однако для начала можно создать проект Windows-приложения на С# (Windows Application). При этом будет сгенерирован код пусковой системы и будут установлены ссылки на необходимые библиотеки .NET. Затем можно в конструкторе форм (Forms Designer) перетащить управляющие элементы с инструментальной панели на вашу форму. Конструктор форм (Forms Designer) вставит необходимый шаблон кода на С#, который поддерживает функционирование этих управляющих элементов в форме. В окне Properties (Свойства) несложно определить свойства управляющего элемента в процессе проектирования. Можно, конечно, определить эти свойства и во время запуска приложения, как мы это сделали для поля pTxtGreeting в предыдущем примере. После этого можно перенести код С# в программу на управляемом C++, но этого обычно не рекомендуется делать.

Лучший способ научиться создавать приложения Windows с помощью Visual Studio.NET — самостоятельно с самого начала создать небольшое приложение на С#. Для примера мы создадим Windows-приложение, которое позволит вносить деньги на счет и снимать деньги со счета в банке.

1. Создайте на СП новый проект Windows Application (Windows-приложение), как на рис. 6.11, и назовите его BankGui.

Рис. 6.11. Создание проекта Windows Application (Windows-приложение)

2. Раскройте панель инструментов Toolbox, перетянув указатель мыши на вертикальную вкладку Toolbox в левой части главного окна Visual Studio. Если вкладки нет, инструментальную панель Toolbox можно открыть из меню ViewOToolbox (Вид1^ Панель инструментов). Чтобы панель инструментов Toolbox оставалась открытой, щелкните на «канцелярской кнопке», которая находится в заголовке панели инструментов Toolbox рядом с X. Если курсор мыши навести на «канцелярскую кнопку», появится подсказка с надписью «Auto Hide» (Автоматическое свертывание).

3. Перетащите из панели инструментов Toolbox две надписи (Label), два поля (TextBox) и две кнопки (Button) на форму (рис. 6.12).

Рис. 6.12. Перетаскивание управляющих элементов с панели инструментов Toolbox() на форму

4. В конструкторе форм (Forms Designer) щелкните на надписи labell. Тем самым вы выделите этот управляющий элемент в окне Properties (Свойства), которое находится под Solution Explorer (Поиск решения). Окно Properties (Свойства) позволяет изменять свойства управляющих элементов. В поле свойства Text (Текст) объекта labell введите Amount (Сумма). После того, как вы ввели значение, нажмите возврат каретки. Вы увидите, что текст появится на форме. На рис. 6.13 показано окно Properties (Свойства) после изменения свойства Text (Текст) первой надписи.

Рис. 6.13. Изменение значений свойств в окне Properties (Свойства)

5. Точно так же измените текст надписи 1аЬе12 на Balance (Баланс).

6. Введите значения свойств полей и кнопок в соответствии с табл. 6.1.

7. С помощью маркеров размера, которые находятся посредине каждой стороны формы, измените ее размер. При желании, перетащите управляющие элементы на выбранные места, и измените их размер. Если внешний вид формы вас удовлетворяет, сохраните изменения, сделанные в проекте. Ваша форма должна выглядеть примерно так, как на рис. 6.14.

8. Добавьте обработчики событий кнопок, дважды щелкнув на каждой кнопке.

9. Добавьте необходимый код к коду, сгенерированному мастером:

Таблица 6.1. Значения свойств полей (Textbox) и кнопок (Button)

Имя свойства Текст
txtAmount (не заполняется)
txtBalance (не заполняется)
cmdDeposit Deposit (Вклад)
cmdWithdraw Withdraw (Снять)

Рис. 6.14 Форма приложения BankGui

public class Forml : System.Windows.Forms.Form 
// общедоступный класс Forml:Система.Windows.Формы.Форма 
{ 
public Forml() 
{ 
// 
// Требуется для поддержки Windows Form Designer 
// 
InitializeComponent(); 
// 
// TODO: Добавьте любой код конструктора после 
// вызова InitializeComponent 
// 
txtAmount.Text = "25"; 
// Текст txtBalance.Text = "100"; 
// Текст 
} 
///  
/// The main entry point for the application. 
/// Основная точка входа для приложения. 
///  
[STAThread] 
static void Main() 
{ 
Application.Run(new Forml{)); 
} 
private void cmdDeposit_Click(object sender, 
System.EventArgs e) 
{ 
int amount = Convert.Tolnt32(txtAmount.Text); 
int balance = Convert.Tolnt32(txtBalance.Text); // баланс 
balance += amount; 
// баланс + = количество; 
txtBalance.Text = Convert.ToString(balance); // Текст 
} 
private void cmdWithdraw_Click(object sender, 
System.EventArgs e) 
{ 
int amount = Convert.Tolnt32(txtAmount.Text); 
int balance = Convert.Tolnt32(txtBalance.Text); // баланс 
balance = amount; 
txtBalance.Text = Convert.ToString(balance); // Текст 
}

10. Откомпилируйте и выполните приложение. Оно должно вести себя как стандартное приложение Windows. Вы должны без проблем вносить деньги на счет и снимать деньги со счета. На рис. 6.15 показано выполняющееся приложение BankGui.

Рис. 6.15. Windows-приложение BankGui

В данный момент проект реализован на С#. И хотя этого, как правило, не делают, мы перенесем этот проект в C++ для того, чтобы показать, как это нужно делать. Сначала с помощью шаблона Managed C++ Empty Project (Пустой проект на управляемом C++) создадим новый проект C++, который назовем BankGuiPort.

Теперь создадим исходный файл Forml.cpp в проекте BankGuiPort и перенесем (с помощью команд сору (копировать) и paste (вставить)) код С# из исходного файла Forml. cs проекта BankGui.

Перенесите все строки кода из файла Forml.cs проекта BankGui в файл Forml.cpp проекта BankGuiPort. При таком переносе кода могут возникнуть проблемы и непредвиденные ситуации. Эти проблемы не будут рассмотрены в нашей книге и вам придется ознакомиться с ними самостоятельно, если вы и в дальнейшем захотите выполнять подобный перенос кода. Итак, откройте оба проекта — BankGui на С# и BankGuiPort на C++ — в двух копиях Visual Studio.NET и визуально сравните исходные файлы Forml.cs и Forml.cpp, чтобы получить представление о подробностях переноса кода.

//Form1.cpp 
#using  
#using  
#using  
#using  
using namespace System; 
// использование пространства имен Система; 
namespace BankGui 
// пространство имен BankGui 
{ 
_gc class Forml : public System::Windows::Forms::Form 
// класс сборщика мусора Forml: общедоступная Система:: 
// Windows:: Формы:: Форма 
{ 
private: // частный 
System::Windows: Forms::Label *label1; 
System::Windows: Forms::Label *labe!2; 
System::Windows: Forms::TextBox *txtAmount; 
System::Windows: Forms::TextBox *txtBalance; 
System::Windows: Forms::Button *cmdDeposit; // Кнопка 
System::Windows: Forms::Button *cmdWithdraw; // Кнопка 
System::ComponentModel::Container *components; // Контейнер 
public: 
Form1 () 
{ 
components =0; // компоненты 
InitializeComponent(); 
txtAmount->Text = "25"; // Текст 
txtBalance->Text = "100"; // Текст 
} 
private: // частный 
void InitializeComponent() 
{ 
cmdWithdraw = new System: :Windows::Forms::Button; 
// Кнопка 
cmdDeposit = new System::Windows::Forms::Button; 
// Кнопка 
txtBalance = new System::Windows::Forms::TextBox; 
txtAmount = new System::Windows::Forms::TextBox; 
labell = new System::Windows::Forms::Label; // Надпись 
Iabel2 = new System: : Windows :: Forms :-.Label; // Надпись 
SuspendLayout(); 
// 
// cmdWithdraw 
// 
cmdWithdraw->Location = // Местоположение 
* _nogc new System::Drawing::Point(152, 144); 
// Точка 
cmdWithdraw->Name = "cmdWithdraw"; // Имя 
cmdWithdraw->TabIndex = 2; 
cmdWithdraw->Text = "Withdraw"; // Текст = "Снять" 
cmdWithdraw->Click += // Щелчок 
new System::EventHandler(this, cmdWithdraw_Click); 
// 
// Form1 
// 
AutoScaleBaseSize = 
* _nogc new System::Drawing::Size(5, 13); 
// Размер 
ClientSize = 
* _nogc new System::Drawing::Size(280, 189); 
// Размер 
System: :Windows::Forms::Control* pltems[] = { 
cmdDeposit, 
txtAmount, 
label1, 
label2, 
txtBalance, 
cmdWithdraw}; 
Controls->AddRange(pltems); 
"Name = "Forml"; // Имя 
Text = "Forml"; // Текст 
Load += new System::EventHandler(this, Forml_Load); 
ResumeLayout(false); // ложь } void Forml_Load( 
Object *sender, System::EventArgs *e) 
{ 
} 
void cmdWithdraw_Click( 
Object *sender, System::EventArgs *e) 
{ 
int amount = Convert::ToInt32(txtAmount->Text); 
// преобразование текста 
int balance = Convert::ToInt32(txtBalance->Text); 
// преобразование текста 
balance -= amount; 
// -количество 
txtBalance->Text = Convert::ToString(balance); 
// преобразование в текст } 
public: 
[STAThread] static void Main() 
{ 
System::Windows::Forms::Application::Run(new Forml); 
// Приложение:: Выполнить (новая Forml); 
} 
}; 
}

Для работы с проектами Windows Forms (Формы Windows) в Visual Studio очень важно научиться переключаться между окном конструктора (Design window), где вы работаете с управляющими этементами на форме, и окном кода (Code window), где вы работаете с кодом программы Мы можем показать это на примере двух окон проекта VsForm на С#, код стартовой системы этого проекта находится в папке VsForm\Stepl главной папки данной главы Версия этого проекта, перенесенная из С# на C++, находится в папке VsFormPortXStepl Это первая версия проекта стартовой системы, которая отображает одну и ту же строку приветствия Проекты, отвечающие разным стадиям разработки, последовательно пронумерованы, и каждой версии проекта на С# (они содержатся в папке VsForm), созданной с помощью конструктора форм (Forms Designer), соответствует перенесенная версия проекта на C++, которая содержится в папке VsFormPort

Если дважды щелкнуть на файле Forml cs проекта VsForml\Stepl в окне Solution Explorer (Поиск решения), то файл будет открыт в окне конструктора (Design window), как на рис 6 16

Для того чтобы появилось окно кода (Code window), щелкните на кнопке View Code (Просмотреть код), находящейся на инструментальной панепи Таким образом вы откроете исходный код, и вверху главной области окна вы увидите горизонтально расположенные ярлыки, с помощью которых можно выбирать нужные окна В данный момент для этой формы открыты оба окна, — и окно конструктора (Design window), и окно кода (Code window) Можно без труда вернуться в окно конструктора (Design window), щелкнув на кнопке View Designer (Открыть окно Design), находящейся на инструментальной панели На рис 6 17 вы можете увидеть внешний вид окна кода (Code window)

Рис. 6.16. Окно конструктора (Design window) в проекте Windows Forms (Формы Windows)

  • 1. Скомпонуйте и выполните программы (стартовые системы) на С# и C++, находящиеся в папках VsForm\Stepl и VsFormPortXStepl, и убедитесь, что они работают одинаково Это полностью статические приложения, — они просто отображают строку приветствия в фиксированной позиции
  • 2. Откройте форму проекта VsForm\Stepl в окне конструктора (Design window) и щелкните на кнопке Events (События) в окне Properties (Свойства)
  • 3. Найдите событие MouseDown (Кнопка мыши нажата), как на рис. 6.18.
  • 4. В окне Properties (Свойства) дважды щелкните на событии MouseDown (Кнопка мыши нажата) Автоматически будет сгенерирован код, который зарегистрирует делегата для события и образует скелет метода, связанного с делегатом

Рис. 6.17. Окно кода (Code window) в проекте Windows Forms (Формы Windows)

private void InitializeComponent () 
{ 
this.MouseDown = 
new System.WinForms.MouseEventHandler 
(this.Forml_MouseDown); 
} 
protected void Forml_MouseDown (object sender, 
System.WinForms.MouseEventArgs e) 
{ 
}

Рис. 6.18. Добавление события с помощью кнопки Events (События)

1. Чтобы установить координаты строки приветствия, добавьте код в обработчик события мыши (нажатие кнопки мыши) Не забудьте после этого вызвать метод Invalidate (Считать недействительным)

protected void Forml_MouseDown (object sender, 
System WinForms.MouseEventArgs e) 
{ 
x = e X; 
у = e Y; 
Invalidate() ; 
}

2. Скомпонуйте и выполните проект Теперь по щелчку мыши (любой кнопкой) приветствие должно перемещаться Проект сейчас находится на шаге 2 разработки и соответствует проекту, хранящемуся в папке VsForm\Step2

Вместо того, чтобы переносить каждую строчку кода, созданного на СП, в файл Forml cpp проекта VsForm\Step2, просто сделайте копию проекта VsFormPortXStepl, который уже получен с помощью переноса кода Потом перенесите несколько строчек кода, связанных с событием MouseDown (Кнопка мыши нажата) из VsForm\Step2

void InitializeComponent() 
{ 
MouseDown += 
new System::Windows::Forms:-MouseEventHandler 
(this, Forml_MouseDown); 
} 
void Forml_MouseDown (Object *sender, 
System::Windows::Forms::MouseEventArgs *e) 
{ 
x = (float)e->X; // с плавающей точкой 
у = (float)e->Y; // с плавающей точкой 
Invalidate(); 
}

3. Откройте панель инструментов Toolbox, если она до сих пор еще не открыта (щелкните на ярлыке панели инструментов Toolbox в вертикальной линейке) и перетащите управляющий элемент MainMenu (Главное меню) на форму приложения.

4. Для создания выпадающего меню File (Файл) с пунктом Exit (Выход), введите File (Файл) и Exit (Выход), как на рис. 6.19.

Рис. 6.19. Использование управляющего элемента Menu (Меню) для добавления в форму меню

5. В окне Properties (Свойства) измените названия этих пунктов меню на menuFile и menuExit.

6. Дважды щелкните на Exit (Выход), чтобы добавить код в обработчик события File => Exit (Файл => Выход).

7. Добавьте в обработчик код, закрывающий приложение.

protected void menuExit_Click (object sender, 
System.EventArgs e) 
{ 
Application.Exit(); // Приложение.Выход 
}

8. Скомпонуйте и выполните приложение. Меню должно полностью работать. Полученный проект соответствует проекту, который находится в папке VsForm\Step3.

И снова, вместо того, чтобы переносить каждую строчку кода, созданного на С#, из файла Forml.cpp проекта VsForm\step3 в проект на C++, просто сделайте копию проекта VsFormPort\Step2, созданного ранее с помощью переноса кода. Потом перенесите те несколько строчек кода, которые связаны с новыми функциями меню, из VsForm\Step3.

// VSForm - Step3 
_gc class Forml : public System::Windows::Forms::Form 
{ 
private: // частный 
float x, у; // с плавающей точкой 
Brush *pStdBrush; // Кисть 
System: -.Windows: : Forms : :MainMenu *mainMenul; 
System::Windows::Forms::MenuItem *menuFile; 
System::Windows::Forms::MenuItem *menuExit; 
private: // частный 
void InitializeComponent() 
{ 
menuFile = 
new System: -.Windows: : Forms: :MenuItem() ; menuExit = 
new System::Windows::Forms::MenuItem(); mainMenul = 
new System::Windows::Forms::MainMenu() ; 
// 
// menuFile 
// 
menuFile->Index =0; // Индекс 
System::Windows::Forms::MenuItem *pltems[] = 
{menuExit}; 
menuFile->MenuItems->AddRange(pltems) ; 
menuFile->Text = "File"; // menuFile-> Текст = "Файл"; 
// 
// menuExit 
// 
menuExit->Index = 0; 
// Индекс menuExit->Text = "Exit"; 
// menuExit-> Текст = "Выход"; 
menuExit->Click += // Щелчок 
new System::EventHandler 
(this, menuExit_Click); 
// 
// mainMenul 
// System::Windows::Forms::MenuItem *pMenus[] = 
{menuFile}; 
mainMenul->Menu!tems->AddRange(pMenus); 
// 
// Forml 
// AutoScaleBaseSize = 
* _nogc new System::Drawing::Size(5, 13); 
// Размер ClientsTze = 
* _nogc new System::Drawing::Size(248, 181); 
// Размер Menu = mainMenul; 
} 
private: // частный 
void menuExit_Click( 
Object *sender, System::EventArgs *pe) 
{ 
Application::Exit(); // Приложение:: Выход 
} 
};

Давайте сделаем так, что каждый раз, когда пользователь попытается закрыть приложение, приложение будет его спрашивать, действительно ли он хочет выйти Существует несколько способов закрыть окно-

  • щелкнуть на кнопке «X» (Закрыть) в правом верхнем углу окна;
  • закрыть окно из системного меню в левом верхнем углу окна,
  • закрыть окно с помощью комбинации клавиш AU+F4,
  • выйти из приложения с помощью меню FileOExit (ФайлОВыход)

Когда закрывается форма, вызывается событие Closing (Процедура завершения) Можно остановить процедуру завершения, установив в обработчике этого события свойство Cancel (Отмена). (Сначала нужно, как обычно, добавить обработчик события Closing (Процедура завершения) ) Просто напечатайте код MessageBox (Окно сообщения), приведенный ниже

			 
protected void Forml_Closing (object sender. 
System.ComponentModel.CancelEventArgs e) 
{ 
DialogResult status = MessageBox.Show( // состояние 
"Do you want to close", 
// "Хотите закрыть?", 
"Simple Form (VS)", MessageBoxButtons.YesNo); 
if (status == DialogResult.No) 
// если (состояние == DialogResult. Нет) 
{ 
e.Cancel = true; // Отмена = истина 
} 
}

Для того чтобы получить нужное нам поведение приложения, обработчик меню FileOExit (ФайлОВыход) должен не выходить из приложения, а закрыть главное окно, вызвав метод С lose (Закрыть)

protected void menuExit_Click (object sender, 
System.EventArgs e) 
{ 
//Application.Exit(); 
// Приложение.Выход (); 
Close () ; 
}

Теперь проект соответствует проекту, находящемуся в папке VSForm\Step4 Запустите программу, и попытайтесь закрыть окно приложения разными способами В любом случае должно появиться диалоговое окно, показанное на рис. 6.20.

Рис. 6.20. Диалоговое окно, которое спрашивает пользователя, действительно ли он хочет закрыть приложение

И, наконец, как обычно, перенесите код проекта, подготовленного на С#, в проект на C++ VSFormPort\Step4. Скомпонуйте и выполните программу на C++. Убедитесь в том, что она работает так же, как и программа на С#

// VSForm - Step4 
_gc class Forml : public System:-.Windows :: Forms :: Form 
{ 
private: // частный 
void InitializeComponent() 
{ 
Closing += // Закрытие 
new CancelEventHandler(this, Forml_Closing) ; 
} 
void menuExit_Click( 
Object *sender, System::EventArgs *pe) 
{ 
//Application::Exit(); 
// Приложение:: Выход () 
; Close(); 
} 
void Form1_Closing( 
Object *sender, CancelEventArgs *e) 
{ 
int status = MessageBox::Show( // состояние 
"Do you want to close", 
// "Хотите закрыть", 
"Simple Form (VS)", // Простая Форма 
MessageBoxButtons::YesNo); 
if (status == DialogResult::No) 
// если (состояние == DialogResult::No) 
{ 
e->Cancel = true; // Отмена = истина 
} 
} 
};

Все оставшиеся примеры в этой главе написаны исключительно на С#!

Далее в этой главе мы будем рассматривать только программы на С#, поскольку даже в программах, которые в основном написаны на C++, при разработке графических пользовательских интерфейсов, как правило, используется С#. Причина этого проста— в C++ не поддерживается конструктор форм (Forms Designer). Всегда помните о том, что вы можете сначала создать графический пользовательский интерфейс на С#, а потом, если понадобится, перенести этот код в C++. В предыдущих примерах мы показали, как это сделать.

Использование диалоговых окон облегчает процесс взаимодействия пользователя с приложением Windows Диалоговое окно — это набор управляющих элементов, с помощью которых упрощается процесс ввода данных В предыдущем примере было описано, как создать простое диалоговое окно, которое позволяло бы пользователю на вопрос дать ответ «Да» или «Нет» Для создания таких диалоговых окон используется класс MessageBox (Окно сообщения) Более сложные диалоговые окна создаются на основе форм.

Мы проиллюстрируем создание диалоговых окон на примере графического пользовательского интерфейса для бюро путешествий Acme (Acme Travel Agency) Код примера, как обычно, находится в папке CaseStuay для этой главы Давайте рассмотрим простой диалог, с помощью которого можно добавить гостиницу в список гостиниц. Скомпонуйте и запустите пример В главной форме щелкните на кнопке Add… (Добавить ) После этого появится диалоговое окно New Hotel (Новая гостиница), которое показано на рис. 6.21.

Рис. 6.21. Диалоговое окно для добавления новой гостиницы

Теперь пользователь может вводить данные. Щелчок на кнопке ОК. приведет к тому, что информация будет запомнена Щелчок на кнопке Cancel (Отмена) приведет к тому, что данные будут проигнорированы. Это диалоговое окно, так же как и окно сообщения, является модальным Если модальное диалоговое окно открыто, то пользователь может взаимодействовать с приложением только через это окно, — ни с какими иными средствами приложения он взаимодействовать не может. Если вы попытаетесь сделать что-нибудь на главной форме, когда открыто диалоговое окно New Hotel (Новая гостиница), то услышите гудок. Существуют также и немодальные диалоговые окна, которые позволяют взаимодействовать с иными средствами приложения даже тогда, когда немодальное диалоговое окно открыто.

Если рассматривать диалоговое окно как форму, то обычно у него есть свои специфические характеристики. Как правило, у диалоговых окон нет системного меню, нет кнопок свертывания Minimize (Свернуть) и развертывания Maximize (Развернуть), причем размер окна фиксирован Вы можете наблюдать эти свойства на примере диалога New Hotel (Новая гостиница).

Для того чтобы продолжить испытание программы, введите данные о новой гостинице и щелкните на кнопке ОК. Программа вернет вас в главную форму, где новая гостиница будет отображена в списке гостиниц (рис. 6 22). На главной форме реализованы также и другие управляющие элементы графического интерфейса пользователя, например, окно, в котором отображается список гостиниц, а также многострочное текстовое поле для отображения текста, который не помещается в одну строку.

Рис. 6.22. Главная форма для управления гостиницами

Диалоговые окна очень хорошо описаны в документации по комплексу инструментальных средств разработки программ .NET Framework SDK Информацию по диалоговым окнам следует искать в подразделе «Dialog Boxes in Windows Forms» («Диалоговые окна в Формах Windows») раздела «Introduction to Windows Forms» («Введение в Формы Windows») Следует заметить, что во всех языках .NET принципы работы диалоговых окон одни и те же Это является серьезным отличием программирования на NET от традиционного программирования на Visual Basic и программирования с применением библиотеки базовых классов Microsoft (Microsoft Foundation Classes, MFC), где принципы создания диалоговых окон совершенно разные На рис. 6.23. показано, где искать документацию по диалоговым окнам

Рис. 6.23. Документация по диалоговым окнам в NET Framework

Мы продемонстрируем детали реализации диалогового окна на примере создания диалога для изменения информации о гостинице в упрощенной версии нашего примера Код системы запуска нашего примера имеется в папке HotelAdminNStepl, которая находится в главной папке этой главы Окончательная версия программы находится в папке HotelAdmin\Step3 Можно запустить первую версию примера, а можно сразу запустить решение Step 3 и посмотреть, как должно выглядеть диалоговое окно в окончательной версии В главной форме выберите гостиницу, щелкнув на соответствующем элементе списка гостиниц Затем щелкните на кнопке Change (Изменить ) В результате появится диалоговое окно Change Hotel Information (Изменение информации о гостинице), показанное на рис. 6.24 Обратите внимание на то, что поля City (Город) и Hotel Name (Название гостиницы) недоступны Эти поля доступны только для чтения, и их значения изменить нельзя Пользователь может изменить только значения полей Rooms (Количество номеров) и Rate (Стоимость)

Создание модального диалога

В первой части нашей демонстрации мы научим вас создавать модальное диалоговое окно Мы покажем, как устанавливать свойства диалога и как возвращать результаты после щелчка на кнопке (Ж или Cancel (Отмена)

  • 1. Скомпонуйте и запустите стартовую систему приложения Кнопки Add (Добавить ) и Delete (Удалить) работают, но для кнопки Change (Изменить ) имеется только заглушка, которая по щелчку на этой кнопке отображает пустую форму Это обычная форма Ее размер можно изменять, у нее есть системное меню, кнопки свертывания Minimize (Свернуть) и развертывания Maximize (Развернуть)
  • 2. Откройте файл ChangeHotelDialog. cs в режиме Design (Конструктор) В окне Properties (Свойства) установите значение свойства FormBorderStyle равным FixedDialog
  • 3. Установите значение свойств ControlBox, MinimizeBox и MaximizeBox равным False (Ложь) Сейчас можно скомпоновать и запустить приложение Теперь размер диалогового окна будет фиксированным, у него не будет системного меню, а в правом верхнем углу окна не будет кнопки «X», с помощью которой можно закрыть окно.

Рис. 6.24. Диалоговое окно для изменения информации о гостинице

  • 4. Теперь необходимо определить надписи и текстовые поля, содержащие информацию о гостинице Кроме того, необходимо добавить кнопки ОК и Cancel (Отмена) Когда вы будете добавлять эти управляющие элементы, вы можете еще раз попрактиковаться в работе с панелью инструментов Toolbox (Инструментальная панель) Вы можете выбрать и другой подход скопировать эти свойства из файла NewHotelDialog.cs и затем вставить их, для этого нужно открыть оба файла в режиме Design (Конструктор)
  • 5. Если вы использовали копирование и вставку, то у управляющих элементов свойства Name (Имя) и Text (Текст) уже определены правильно. В противном случае установите значения в соответствии с табл. 6.2.
  • 6. Значение свойства Readonly для txtCity и txtHotelName установите равным true (истина).
  • 7. Измените размер формы так, чтобы на ней помещались все добавленные управляющие элементы.
  • 8. Установите значение свойства DialogResult кнопки ОК равным ОК. Точно так же установите значение этого свойства кнопки Cancel (Отмена) равным Cancel (Отмена). Сохраните изменения, сделанные в файле ChangeHotelDialog. cs.
  • 9. В файле MainAdminForm.cs временно добавьте к обработчику cmdChange_Click код, который отвечает за отображение в текстовом поле Messages (Сообщения) строк «ОК» и «Cancel» («Отмена») в зависимости оттого, как был закрыт диалог: с помощью кнопки ОК или Cancel (Отмена). Обратите внимание на то, что диалоговое окно отображается с помощью метода ShowDialog, а не метода Show (Показать), который используется для обычных форм. В качестве результата метод ShowDialog возвращает перечисление типа DialogResult.
private void cmdChange_Click(object sender, 
System.EventArgs e) 
{ 
ChangeHotelDialog dig = new ChangeHotelDialog(); 
DialogResult status = dig.ShowDialog(); // состояние 
if (status == DialogResult.OK) 
// если (состояние == DialogResult. OK) 
{ 
txtMessages.Text = "OK"; // Текст 
} 
else 
{ 
txtMessages.Text = "Cancel"; 
// txtMessages. Текст = "Отмена"; 
} 
}

Таблица 6.2. Значения свойств текстовых полей и кнопок диалога ChangeHotelDialog.cs

Имя (Name) Текст
txtCity (не заполнено)
txtHotelName (не заполнено)
txtlMumberRooms (не заполнено)
txtRate (не заполнено)
cmdOk ОК
cmdCancel Cancel (Отмена)
  • 10. Скомпонуйте и запустите пример. Теперь диалоговое окно уже можно открыть с помощью меню, а закрыть— с помощью любой из кнопок ОК или Cancel (Отмена), причем на экран будет выведено соответствующее сообщение. Можно проверить, что диалоговое окно является модальным, пощелкав мышью где-нибудь еще в приложении. Программа сейчас находится на шаге 2 разработки.

Передача информации между родительской формой и диалогом

Во второй части нашей демонстрации будет показано, как передавать информацию из родительской формы диалогу и как получать информацию от диалога. Для этих целей в классах .NET Framework нет встроенного механизма, но все-таки существует некоторый шаблон проектирования, которому можно следовать. В классе диалога для каждого сообщения (порции информации), которое может передаваться между родительской формой и диалогом, нужно определить некоторое свойство.

В нашем примере мы сделаем свойства City (Город) и HotelName (Название гостиницы) доступными только для записи, а свойства Rate (Стоимость) и NumberRooms — доступными для чтения и записи.

1. Для того чтобы реализовать указанные свойства, добавьте к классу Chan-geHotelDialog в файле ChangeHotelDialog. cs приведенный ниже код.

public string City 
// общедоступный строковый - Город 
{ 
set 
{ 
txtCity.Text = value; // txtCity. Текст = значение; 
} 
} 
public string HotelName // общедоступная строка HotelName 
{ 
set 
{ 
txtHotelName.Text = value; // txtHotelName. Текст = значение; 
} 
} 
public int NumberRooms 
{ 
get 
{ 
return Convert.ToInt32 (txtNumberRooms.Text); 
} 
set 
{ 
} 
} 
public decimal Rate // общедоступная десятичная Цена 
{ 
get 
{ 
return Convert.ToDecimal(txtRate.Text); 
} 
set 
{ 
txtRate.Text = value.ToString(); // Текст = значение 
} 
}

2. Теперь, чтобы установить эти свойства перед вызовом диалогового окна, и для того, чтобы использовать эти свойства перед закрытием диалогового окна с помощью кнопки ОК., добавьте код в главную форму MainAdminForm.cs. Удалите или закомментируйте временно вставленный ранее код, который отображает строки «ОК.» или «Cancel» («Отмена») в поле Messages (Сообщения).

private void cmdChange_Click(object sender, 
System.EventArgs e) 
{ 
ChangeHotelDialog dig = new ChangeHotelDialog(); 
if (currHotel.HotelName != "") 
{ 
dig.City = currHotel.City; // Город 
dig.HotelName = currHotel.HotelName; 
dig.NumberRooms = currHotel.NumberRooms; d 
ig.Rate = currHotel.Rate; 
} 
else 
{ 
MessageBox.Show("Please select a hotel", 
// "Пожалуйста, выберите гостиницу ", 
"Hotel Broker Administration", 
MessageBoxButtons.OK, 
MessageBoxIcon.Exclamation // Восклицательный знак 
) ; 
return; 
} 
DialogResult status = dig.ShowDialog(); // состояние 
if (status == DialogResult.OK) 
// если (состояние == DialogResult. OK) 
{ 
string comment = hotelBroker.ChangeRooms( // строковый 
// комментарий 
currHotel.City, // Город 
currHotel.HotelName, 
dig.NumberRooms, 
dig.Rate); 
if (comment == "OK") 
// если (комментарий == "OK") 
{ 
ShowHotelList(hotelBroker.GetHotels(;) ; 
txtMessages.Text = "Hotel " + currHotel.HotelName 
// txtMessages. Текст = "Гостиница" 
// + currHotel. HotelName 
+ " has been changed"; 
// + " была изменена"; 
} 
else 
txtMessages.Text = comment; // Текст = комментарий 
} 
}

Структура currHotel содержит поля гостиницы, выбранной в данный момент в качестве элемента списка. В следующем разделе вы научитесь извлекать информацию из элемента списка и заполнять элемент списка.

3. Скомпонуйте и испытайте программу. Все функции диалога должны работать корректно. Полученный проект соответствует проекту HotelAdmin\Step3.

В .NET Framework поддерживается несколько управляющих элементов, с помощью которых можно отобразить списки элементов. Эти управляющие элементы позволяют пользователю выбрать элемент списка; обычно для выбора элемента требуется щелкнуть на нем. В этом разделе мы рассмотрим управляющий элемент ListBox (Список элементов).

Пример программы находится в папке HotelAdmm\Step3. Главная форма в файле MainAdminForm. cs содержит список элементов listHotels, в котором хранится список гостиниц. Каждая гостиница представлена в виде строки, а значения атрибутов гостиницы разделены запятыми.

При запуске программы HotelAdmin в процессе инициализации конструктор формы MainAdminForm осуществляет начальную загрузку списка элементов listHotels, — в него загружается список гостиниц.

public MainAdminForm() 
{ 
// 
// Требуется для поддержки конструктора форм Windows 
// (Windows Form Designer) 
// 
InitializeComponent(); 
// 
// TODO: Добавьте любой код конструктора после 
// вызова InitializeComponent 
// 
hotelBroker = new HotelBroker(); 
ShowHotelList(hotelBroker.GetHotels() 
}; 
}

Метод ShowHotelList отображает в списке элементов список массивов, в которых хранится информация о гостиницах. Чтобы получить список массивов, вызывается метод HotelBroker . GetHotels. Ниже приведен метод ShowHotelList.

private void ShowHotelList(ArrayList array) // массив 
{ 
listHotels.Iterns.Clear(); 
if (array == null) 
// если (массив == пустой указатель) 
{ 
return; } 

foreach(HotelListltem hotel in array) // гостиница в массиве 
{ 
string city = hotel.City.Trim(); 
// строковый город = гостиница.Город.Вырезка(); 
string name = hotel.HotelName.Trim(); 
// строковое название = гостиница.HotelName.Вырезка(); 
string rooms = hotel.NumberRooms.ToString(); // гостиница 
string rate = hotel.Rate.ToString(); 
string str = city + "," + name + "," 
// строка str = город +,"" + название +,"" 
+ rooms + "," + rate; 
listHotels.Items.Add(str); // Добавить элементы 
} 
}

Управляющий элемент ListBox (Список элементов) содержит свойство Items (Элементы), которое поддерживает коллекцию объектных ссылок. Сначала мы вызываем метод Items.Clear (Элементы.Очистить), чтобы очистить список элементов от тех элементов, которые отображаются в нем в момент вызова метода. Потом мы с помощью цикла перебираем гостиницы в списке массивов и создаем строку, которая состоит из полей структуры гостиницы, разделенных запятыми. Чтобы добавить эту строку в список элементов, вызывается метод Items .Add (Элементы.Добавить).

Чтобы в списке элементов выбрать какой-нибудь элемент, нужно щелкнуть на нем. Выбор элемента вызовет событие SelectedlndexChanged. Доступ к выбранному элементу можно получить с помощью свойств Selectedlndex и Selectedltem. Если никакой элемент не выбран, значение Selectedltem будет равно -1. Ниже приведен код обработчика события SelectedlndexChanged.

private void listHotels_Selected!ndexChanged(object sender, 
System.EventArgs e) { 
if (listHotels.Selectedlndex != -1) 
{ 
string selected - (string) listHotels.Selectedltem; 
// выбранная строка 
char [ ] sep = new char[] {','}; 
// символ 
string[] fields; 
// строка [] поля; 
fields = selected.Split(sep); 
// поля = выбранное, разбить; 
currHotel = new HotelListltem(); 
currHotel.City = fields[0]; 
// Город = поля [О]; 
currHotel.HotelName = fields[1]; 
// поля [1] 
currHotel.NumberRooms = Convert.Tolnt32(fields[2]); 
// поля [2] 
currHotel.Rate = Convert.ToDecimal(fields[3]); 
// поля [3] 
} 
else 
{ 
currHotel.HotelName = ""; 
} 
}

Поскольку в списке элементов элементы хранятся в виде объектных ссылок, мы выполняем приведение типа выбранного элемента к типу string (Строка). Для того чтобы выделить значения полей, разделенные запятыми, используется метод String. Split (Строка.Разбиение), затем значения сохраняются в массиве строк fields (поля). Из массива эти значения переносятся в currHotel, где они и хранятся. В предыдущем разделе мы использовали currHotel для инициализации диалоговых окон New Hotel (Новая гостиница) и Change Hotel Information (Изменение информации о гостинице).

Пример бюро путешествий Acme (Acme Travel Agency) был представлен в главе 4 «Объектно-ориентированное программирование на управляемом C++», где в качестве структур данных для хранения списков гостиниц, клиентов и резервирований мы использовали массивы. В главе 5 «Управляемый C++ в .NET Framework» мы внесли изменения в реализацию примера, и для хранения информации вместо массивов использовали коллекции. Мы также определили множество интерфейсов и передавали списки в качестве объектных ссылок ArrayList. В предыдущей главе был реализован интерфейс пользователя в виде командной строки. В папке CaseStudy этой главы реализован графический интерфейс пользователя, созданный с помощью Windows Forms (Формы Windows).

Вы уже видели главное окно приложения (рис. 6.22), которое ничем не отличается от главного окна упрощенного варианта программы HotelAdmin, служившей для демонстрации использования диалоговых окон. Кнопка Add… (Добавить…) позволяет добавлять новую гостиницу (рис. 6.21), а кнопка Change… (Изменить…) (рис. 6.24) позволяет изменять число номеров в гостинице и их цену. Кнопка Delete (Удалить) удаляет выбранную в настоящее время гостиницу.

По щелчку на кнопке Customers (Клиенты ) появляется форма Customer Management (Управление клиентами), на которой отображается список клиентов, зарегистрированных на текущий момент Клиента можно выбрать, щелкнув на его имени в списке На рис. 6.25 показана форма после того, как клиент был выбран.

В текстовом окне Id отображается идентификатор клиента Можно отменить регистрацию этого клиента, щелкнув на кнопке Unregister (Отменить регистрацию) Изменить адрес электронной почты клиента можно, щелкнув на кнопке Change Email (Изменить электронную почту (email)), — по щелчку появится диалоговое окно Щелкнув на кнопке One Customer (Этот клиент) можно сделать так, чтобы была отображена информация только об одном этом клиенте Если после этого щелкнуть на кнопке All Customers (Все клиенты), то в списке снова будут отображены все клиенты С помощью кнопки Register (Зарегистрировать) можно добавить нового клиента

Рис. 6.25. Форма управления клиентами Customer Management (Управление клиентами)

Третья основная форма нашего интерфейса пользователя — это форма HotelReserva-tions (Бронирование мест в гостинице), которая вызывается по щелчку на кнопке Reservations . (Резервирование ) главной управляющей формы Для того чтобы сделать резервирование, заполните поля Customer Id (Идентификатор клиента), Checkin Date (Дата прибытия), Number of Days (Количество дней) Поля City (Город) и HotelName (Название гостиницы) можно заполнить автоматически (при выборе гостиницы в списке элементов) И чтобы резервирование было выполнено, просто щелкните на кнопке Make Reservation (Сделать резервирование) Для того чтобы вывести список резервирований клиента по его идентификационному коду (Customer Id), необходимо щелкнуть на кнопке Show Reservations (Отобразить список резервирований) На рис. 6.26. показана эта форма после того, как клиент, идентификатор которого равен 1, сделал резервирование, и оно было отображено

Список резервировании можно очистить, щелкнув на кнопке Clear Reservations (Очистить список резервировании) Кнопка Cancel Reservation (Отмена резервирования) служит для того, чтобы отменить резервирование для клиента с определенным идентификационным кодом, который можно ввести вручную или выбрать в списке элементов Reservations (Резервирования)

Рис. 6.26. Форма Hotel Reservation для бронирования мест в гостинице

Пример бюро путешествии Acme (Acme Travel Agency) рассматривается также в последующих главах, и стоит поэкспериментировать с этим приложением уже сейчас Графический пользовательский интерфейс значительно удобнее, чем пользовательский интерфейс командной строки С другой стороны, в интерфейсе командной строки реализован простой глобальный блок try для цикла по всем командам, облегчающий обнаружение всех искчючений Но этот подход не годится для графических пользовательских интерфейсов В промышленных приложениях необходимо делать проверку исключений везде, где они могут произойти Наш пример упрощен в учебных целях, и мы не пытались тщательно обрабатывать все исключительные ситуации (исключения) Кроме того, мы допустили еще одно упрощение мы не проверяем, является ли идентификатор клиента (Customer ID), который используется при резервировании, кодом реапьно существующего, зарегистрированного клиента Эта проверка реализована в главе 9 «Программирование в ADO NET’, где пример реализуется на основе базы данных

В этой главе мы рассмотрели создание графических пользовательских интерфейсов с помощью классов Windows Forms (Формы Windows) из .NET Framework. Сперва мы рассмотрели основные принципы, и научились создавать с помощью .NET Framework простые Windows-приложения с самого начала, без использования каких-либо специальных средств. Рисование выполняется в переопределенном методе OnPaint с помощью шрифта и кисти. Для управления взаимодействием пользователя с программой используется механизм обработки событий .NET. Он применяется, например, для обработки событий мыши и клавиатуры. Управляющие элементы намного упрощают программирование Windows-приложений. Управляющий элемент меню упрощает процесс добавления меню к Windows-программе. Visual Studio.NET также намного упрощает процесс программирования Windows-приложений. К сожалению, в C++ не поддерживается конструктор форм (Forms Designer). Зато с помощью конструктора форм (Forms Designer) можно очень быстро создать проект на С#, который, при желании, можно перенести в проект на C++. Конструктор форм (Forms Designer) позволяет перетаскивать управляющие элементы с инструментальной панели (Toolbox) на форму и потом в режиме Design (Конструктор) определять свойства этих управляющих элементов. Можно также без труда добавить в приложение обработчики событий. Диалоговое окно — это особый вид формы, с помощью свойств которого можно реализовать обмен информацией между родительской формой и диалоговым окном. Управляющий элемент ListBox (Список элементов) упрощает процесс отображения списков. В заключение мы разработали графический пользовательский интерфейс для программы Acme Travel Agency (бюро путешествий Acme).

Время чтения: 5 минут

Превью к статье о создании C++ Windows Forms проекта

Windows Forms — интерфейс программирования приложений, отвечающий за графический интерфейс пользователя. Он является частью .Net Framework и создан для того, чтобы упростить взаимодействие пользователя с элементами Win API. Причём не просто упростить, а буквально полностью скрыть низкоуровневое взаимодействие с графическими элементами путём создания набора базовых компонентов и классов. При этом используемые классы не привязаны к языку разработки, благодаря чему данный проект может использоваться как на родном для Microsoft C#, так и на других языках, например, C++, VB Net и F#. Но не смотря на свою кроссплатформенность в мире языков программирования, Windows Forms проекты легко создаются на C#, однако при попытке создания проекта на C++ возникает множество проблем.

Шаг 0. А вдруг получится сразу?

В настоящее время IDE, поддерживающих Windows forms, не так много — буквально одна только Visual Studio, более известная как просто «студия». Поэтому будем рассматривать создание и решение проблем именно в этой среде разработки. Первым шагом запустим студию, начнём создавать новый проект и попытаемся найти Windows forms проект для C++:

Создаём новый проект в студии

Создаём новый проект в студии

Ищем Winfows Forms для C++

Ищем Winfows Forms для C++

Если у вас более старая версия Visual Studio, то интерфейс будет выглядеть немного иначе, однако данная функциональность будет той же. Также не исключено, что у Вас может быть данный тип проекта для C++ (на некоторых версиях формы для C++ были доступны сразу после установки IDE). Если же у Вас, как и у нас поиск не дал нужных результатов, то переходим к следующему шагу.

Шаг 1. Создание CLR проекта

Поскольку непосредственно Windows Forms проекта у нас не оказалось, мы обхитрим студию и создадим пустой CLR проект на С++. Для этого в том же окне поиска необходимо найти и выбрать Новый CLR проект, ввести имя (если нужно, то поменять директорию расположения проекта) и немного подождать, пока студия сделает свою работу.

Ищем пустой CLR проект (.Net Framework)

Ищем пустой CLR проект (.Net Framework)

Создаём новый пустой CLR проект

Создаём новый пустой CLR проект

В результате Visual Stido создаст новый C++ CLR проект, который будет выглядеть примерно так:

Результат создания нового CLR проекта

Результат создания нового CLR проекта

Шаг 2. Добавить форму

Чтобы сделать CLR проект проектом Windows Forms, нужно просто добавить в него форму. Для этого в верхнем меню нужно выбрать ПроектДобавить новый элемент и в появившемся окне выбрать категорию Visual C++UI и затем выбрать Форма Windows Forms.

Проект - data-lazy-src=

Проект -> Добавить новый элемент

Visual C++ - data-lazy-src=

Visual C++ -> UI -> Форма Windows Forms

После данной операции нас ждёт разочарование в виде ошибки Исключение из HRESULT: 0x8000000A:

Вместо формы получили ошибку

Вместо формы получили ошибку

Шаг 3. Исправляем появившуюся ошибку

Данная ошибка появляется из-за того, что для создания окна формы приложению необходима основная программа, создающая форму и переключающая управление на неё, однако после добавления новой формы файл Form1.cpp предсказуемо создаётся пустым. Поэтому необходимо добавить основную программу в файл с формой:

#include "Form1.h"

#include <Windows.h>

using namespace имя_вашего_проекта;

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false);
    Application::Run(gcnew Form1);
    return 0;
}

В результате код файла Form1.cpp будет выглядеть следующим образом:

Добавление основной программы к форме

Шаг 4. Переоткрыть проект

Всё, что теперь осталось сделать — это закрыть текущее решение, а затем открыть его снова. Для этого можно закрыть саму студию, а можно выбрать в верхнем меню ФайлЗакрыть решение, после чего в появившемся окне выбрать только что созданный проект и открыть его заново.

Форма создалась, можно добавлять компоненты

Форма создалась, можно добавлять компоненты

Благодаря добавленному коду основной программы, студия сможет создать форму и на экране появится изображение формы, на которую можно будет добавлять компоненты, задавать их свойства, а также свойства самой формы.

Программист, сооснователь programforyou.ru, в постоянном поиске новых задач и алгоритмов

Языки программирования: Python, C, C++, Pascal, C#, Javascript

Выпускник МГУ им. М.В. Ломоносова

Министерство
науки и высшего образования Российской Федерации

Казанский
национальный исследовательский технический университет – КАИ им. А.Н. Туполева

Институт
компьютерных технологий и защиты информации

Отделение
СПО ИКТЗИ «Колледж информационных технологий»

ОСНОВЫ
АЛГОРИТМИЗАЦИИ И ПРОГРАММИРОВАНИЯ

Методические
указания к лабораторной работе №7

Тема:
«Введение в разработку графических пользовательских интерфейсов с
использованием технологии
Windows
Forms»

Казань
2022

Составитель
преподаватель СПО ИКТЗИ Мингалиев Заид Зульфатович

Методические
указания к лабораторным работам по дисциплине «ОСНОВЫ АЛГОРИТМИЗАЦИИ И
ПРОГРАММИРОВАНИЯ» предназначены для студентов направления подготовки 09.02.07
«Информационные системы и программирование»

КНИТУ-КАИ,
2022

ПРОЦЕСС СДАЧИ ВЫПОЛНЕННОЙ РАБОТЫ

По итогам выполнения работы студент:

1.                
демонстрирует преподавателю правильно
работающие программы;

2.                
демонстрирует приобретённые знания и
навыки отвечает на пару небольших вопросов преподавателя по составленной
программе, возможностям её доработки;

3.                
демонстрирует отчет по выполненной
лабораторной работе.

Итоговая оценка
складывается из оценок по трем указанным составляющим.

ЛАБОРАТОРНАЯ РАБОТА №7

ТЕМА: «ВВЕДЕНИЕ В РАЗРАБОТКУ ГРАФИЧЕСКИХ
ПОЛЬЗОВАТЕЛЬСКИХ ИНТЕРФЕЙСОВ С ИСПОЛЬЗОВАНИЕМ ТЕХНОЛОГИИ
WINDOWS
FORMS»

ЦЕЛЬ РАБОТЫ

Научиться размещать и настраивать
внешний вид элементов управления на форме и создавать обработчики событий.

ХОД ВЫПОЛНЕНИЯ РАБОТЫ

Для Windows-приложений,
ориентированных на выполнение в общеязыковой исполняющей среде (CLR), в качестве
основы построения графического пользовательского интерфейса используются классы
Windows Forms из библиотек .NET Framework, т.е. Windows Forms – это набор
средств для создания Windows-приложений, выполняющихся в среде CLR.

Средства Windows Forms обеспечивают
быструю разработку пользовательского интерфейса, поскольку он создается из
стандартных элементов, а соответствующий программный код генерируется
автоматически. Затем требуется лишь доработать сгенерированный код, чтобы
добиться необходимой функциональности.

Применяя Windows Forms, можно
построить удобный пользовательский интерфейс, в том числе главное окно
приложения, выбирая подходящие элементы управления, с которыми взаимодействует
пользователь.

Чтобы создать Windows-приложение на
основе Windows Forms, следует выбрать команду главного меню «Файл» (File)
→ «Создать» (New) → «Проект» (Project),
затем выбрать тип проекта «Рабочий стол» (Windows Desktop), а в качестве
шаблона проекта указать Windows Forms Application (Рисунок 1).

Рисунок
1

После этого следует ввести имя
проекта, например,
PR1,
и указать папку для хранения проекта.

После этого Visual Studio откроет наш
проект с созданными по умолчанию файлами:

Рисунок
2

Большую часть пространства Visual
Studio занимает графический дизайнер, который содержит форму будущего
приложения. Пока она пуста и имеет только заголовок Form1. Справа находится
окно файлов решения/проекта — Solution Explorer (Обозреватель решений). Там и
находятся все связанные с нашим приложением файлы, в том числе файлы формы
Form1.cs.

Внизу справа находится окно свойств —
Properties. Так как в данный момент выбрана форма как элемент управления, то в
этом поле отображаются свойства, связанные с формой.

Размещения элементов управления на форме

Для размещения различных элементов
управления на форме используется Панель инструментов (Toolbox).  Панель
элементов содержит элементы управления, сгруппированные по типу. Каждую группу
элементов можно свернуть, если она в настоящий момент не нужна. (Рисунок 3)

Рисунок
3

         Чтобы
добавить элементы управления на форму, необходимо щелкнуть на добавляемый
элемент в панели инструментов, а затем щелкнуть в нужном месте формы. После
этого элемент появится на форме (Рисунок 4).

Рисунок
4
– Форма с добавленными элементами управления (
TextBox и Button)

Элемент можно перемещать по форме,
схватившись за него левой кнопкой мыши. Если элемент управления позволяет
изменять размеры, то на соответствующих его сторонах появятся белые кружки,
ухватившись за которые и можно изменить размер. При размещении элемента
управления на форме, его можно выделить щелчком мыши и получить доступ к его
свойствам в окне свойств.

Размещение строки ввода

Если необходимо ввести из формы в
программу или вывести в форму информацию, которая вмещается в одну
строку, используют окно однострочного редактора текста, представляемого
элементом управления
TextBox.

Создадим поле для ввода имени
пользователя. Выберем на панели инструментов пиктограмму с названием «
TextBox»,
щелкните мышью в том месте формы, где хотите ее разметить. После размещения
элемента можно в тексте программы использовать переменную
textBox1,
которая соответствует добавленному элементу управления. В этой переменной, в
свойстве
Text будет
содержаться строка символов (типа
string)
и отображаться в соответствующем окне
TextBox.
С помощью окна свойств можно установить шрифт и размер символов, отражаемых в
строке
TextBox
(свойство
Font).

Размещение надписей

На форме могут размещаться
пояснительные надписи. Для размещения таких надписей на форму используется
элемент
Label.
Выберите на панели инструментов пиктограмму с названием
Label,
щелкните на ней мышью. После этого в нужном месте формы щелкните мышью,
появится надпись
label1.
Щелкнув на ней мышью, можно отрегулировать размер и, изменив свойство
Text
в окне свойств, введите строку, например, «Введите свое имя:», а также выберите
размер символов (свойство
Font).
Добавленный элемент представлен в форме на рисунке 5.

Рисунок
5
– Форма с добавленным элементом Label

В тексте программы можно обращаться к
новой переменной типа
Label.
В ней хранится пояснительная строка, которую можно изменять в процессе работы
программы.

Написание программы обработки события

С каждым элементом управления на
форме и с самой формой могут происходить события во время работы программы.
Например, с кнопкой может произойти событие – нажатие кнопки, а с окном,
которое проектируется с помощью формы, может произойти ряд событий: создание
окна, изменение размера окна, щелчок мыши на окне и т.п. Эти события могут
обрабатываться программно. Для обработки таких событий используются специальные
методы – обработчики событий. Создать такие методы можно двумя способами.

Первый способ – создать обработчик
для события по умолчанию. Например, при нажатии кнопки на форме таким образом
создается обработчик события нажатия.

Написание программы обработки события
нажатия кнопки

Поместите на форму кнопку, которая
описывается элементом управления
Button.
С помощью окна свойств измените заголовок (
Text)
на слово «Привет» или другое по вашему желанию. Отрегулируйте положение и
размер кнопки.

После этого два раза щелкните мышью
на кнопке, появится текст программы:

private void button1_Click(object sender, EventArgs e)

{

}

         Этот метод и будет являться обработчиком события нажатия
кнопки. Вы можете добавлять свой код между скобками {…}. Например, наберите:

private void button1_Click(object sender, EventArgs e)

{

     MessageBox.Show(«Привет « + textBox1.Text + «!»);

}

         При
нажатии на кнопку выведется окно с сообщением «Привет», которое соединено с
текстом, записанным пользователем в
textBox1 (Рисунок 6).

Рисунок
6
– Окно с сообщением, появившееся при нажатии кнопки
button1

Написание программы обработки события загрузки
формы

         Второй
способ создания обработчика события заключается в выборе соответствующего
события для выделенного элемента на форме. При этом используется окно свойств и
его закладка
. Рассмотрим этот способ.
Выделите форму щелчком по ней, чтобы вокруг нее появилась рамка из точек. В
окне свойств найдите событие
Load.
Щелкните по данной строчке дважды мышкой. Появится
метод:

private void
Form1_Load(
object sender, EventArgs e)

{

}

     Между скобками {…} вставим
текст программы:

private void
Form1_Load(
object sender, EventArgs e)

{

BackColor = Color.AntiqueWhite;

}

         Свойство BackColor позволяет изменить цвет фона. Результат выполнения данного метода представлен на рисунке 7.

Рисунок
7
– Форма с измененным фоном

Каждый элемент управления имеет свой
набор обработчиков событий, однако некоторые из них присущи большинству
элементов управления. Наиболее часто применяемые события описаны ниже:

1) Load
– событие, возникающее при загрузке формы. В обработчике данного события
следует задавать действия, которые должны происходить в момент создания формы,
например, установка начальных значений.

2) KeyPress
– событие, возникающее при нажатии кнопки на клавиатуре. Параметр
e.KeyChar
имеет
тип
char и
содержит код нажатой клавиши (например, клавиша
Enter
клавиатуры
имеет код #13, клавиша
Esc
— #27 и т.д.). Обычно это событие используется в том случае, когда необходима
реакция на нажатие одной из клавиш.

3) KeyDown
– событие, возникающее при нажатии кнопки на клавиатуре. Обработчик этого
события получает информацию о нажатой клавише и состоянии клавиш
Shift,
Alt и Ctrl,
а также о нажатой кнопке мыши. Информация о клавише передается параметром
e.KeyCode,
который представляет собой перечисление
Keys
с
кодами всех клавиш, а информацию о клавишах-модификаторах
Shift
и
др. можно узнать из параметра
e.Modifiers.

4) KeyUp
– является парным событием для
KeyDown
и
возникает при отпускании ранее нажатой клавиши.

5) Click
– возникает при нажатии кнопкой мыши в области элемента управления.

6) DoubleClick
– возникает при двойном нажатии кнопки мыши в области элемента управления.

Важное примечание
— если какой-то обработчик был добавлен по ошибке или больше не нужен, то для
его удаления нельзя просто удалить программный код обработчика. Сначала нужно
удалить строку с именем обработчика в окне свойств в закладке с событиями. В
противном случае программа может перестать компилироваться и даже отображать
форму в дизайнере
VS.

Динамическое изменение свойств

         Свойства
элементов на окне могут быть изменены динамически во время выполнения
программы. Например, можно изменять текст надписи или цвет формы. Изменение
свойств происходит внутри обработчика события (например, обработчика события
нажатия на кнопку). Для этого используют оператор присвоения вида:

<имя элемента>.<свойство> =
<значение>;

         Например,

label1.Text
= “
Hello”;

         <имя
элемента> определяется на этапе проектирования формы, при размещении
элемента управления на форме. Например, при размещении на форме ряда элементов
TextBox,
эти элемента получают имена
textBox1,
textBox2, textBox3
и т.д., которые могут быть заменены в окне свойств в свойстве (
Name)
для текущего элемента. Список свойств для конкретного элемента можно посмотреть
в окне свойств.

         Если
требуется изменить свойства формы, то никакое имя элемента перед точкой
вставлять не нужно, как и саму точку. Например, чтобы задать цвет формы, нужно
просто написать:

BackColor
=
Color.Green;

Ввод и вывод данных в программу

         Рассмотрим
один из способов ввода данных через элементы, размещенные на форме. Для ввода
данных чаще всего используют элемент управления
TextBox,
через обращение к его свойству
Text.
Свойство
Text хранит в себе
строку введенных символов. Поэтому данные можно считать следующим образом:

private void button1_Click(object sender, EventArgs e)

{

     string s = textBox1.Text;

}

     Однако со строкой
символов трудно производить арифметические операции, поэтому лучше всего при
вводе числовых данных перевести строку в целое или вещественное число. Для
этого у типов
int и double существуют
методы
Parse для преобразования строк в
числа. С этими числами можно производить различные арифметические действия.
Таким образом, предыдущий пример можно переделать следующим образом:

         Перед выводом числовые данные следует преобразовать назад
в строку. Для этого у каждой переменной существует метод
ToString(), который возвращает в
результате строку с символьным представлением значения. Вывод данных можно
осуществлять в элементы
TextBox или Label, используя свойство Text. Например,

private void button1_Click(object sender, EventArgs e)

{

     string s = textBox1.Text;

     int a = int.Parse(s);

     int b = a * a;

     label1.Text = b.ToString();

}

Кнопки-переключатели

При создании программ в Visual
Studio для
организации разветвлений часто используются элементы управления в виде
кнопок-переключателей. Кнопки-переключатели представлены в панели инструментов
как
RadioButton.
Состояние такой кнопки (включено-выключено) визуально отражается на форме, а в
программе можно узнать его помощью свойства
Checked:
если кнопка включена, это свойство будет содержать
True,
в противном случае
False.
Если пользователь выбирает один из вариантов переключателя в группе, все
остальные автоматически отключаются.

Группируются радиокнопки с помощью
какого-либо контейнера – часто это бывает элемент
GroupBox.
Радиокнопки, размещенные в различных контейнерах, образуют независимые группы.

Контейнер с кнопками переключателями
представлен на рисунке 8.

Рисунок
8.
Группа кнопок-переключателей

         Взаимодействие
с кнопками-переключателями описывает следующий код:

if(radioButton1.Checked)

{

            textBox1.Text =
Convert.ToString(Math.Sin(Convert.ToDouble(textBox2.Text)));

}

else if
(radioButton2.Checked)

{

            textBox1.Text =
Convert.ToString(Math.Cos(Convert.ToDouble(textBox2.Text)));

}

else if
(radioButton3.Checked)

{

            textBox1.Text =
Convert.ToString(Math.Exp(Convert.ToDouble(textBox2.Text)));

}

Флажок для множественного
выбора

Элемент CheckBox или флажок
предназначен для установки одного из двух значений: отмечен или не отмечен.
Чтобы отметить флажок, надо установить у его свойства Checked значение true.

Кроме свойства Checked у элемента
CheckBox имеется свойство CheckState, которое позволяет задать для флажка одно
из трех состояний — Checked (отмечен), Indeterminate (флажок не определен —
отмечен, но находится в неактивном состоянии) и Unchecked (не отмечен).

Также следует отметить свойство
AutoCheck — если оно имеет значение false, то мы не можем изменять состояние
флажка. По умолчанию оно имеет значение true. Форма с элементами-флажками
представлена на рисунке 9.

Рисунок
9.
Форма с CheckBox

         Взаимодействие с CheckBox
позволяет описать следующий код:

private void button1_Click(object sender, EventArgs e)
//кнопка «Выбрать«

{

       
textBox1.Text =
«»;

        switch(checkBox1.Checked)

        {

           
case true:

           
{

                   
textBox1.Text += checkBox1.Text +
» «;

                   
break;

            
}

            
case false:

            
{

                   
break;

            
}

        }

        switch(checkBox2.Checked)

   {

           
case true:

           
{

                   
textBox1.Text += checkBox2.Text +
» «;

                   
break;

           
}

           
case false:

           
{

                   
break;

           
}

        }

        switch(checkBox3.Checked)

        {

           
case true:

           
{

                   
textBox1.Text += checkBox3.Text +
» «;

                   
break;

           
}

           
case false:

           
{

                   
break;

           
}

           
}

}

Элемент ListBox

         Элемент
ListBox представляет собой простой список. Ключевым свойством этого элемента
является свойство Items, которое как раз и хранит набор всех элементов списка.

Элементы в список могут добавляться
как во время разработки, так и программным способом. В Visual Studio в окне
Properties (Свойства) для элемента ListBox мы можем найти свойство Items. После
двойного щелчка на свойство нам отобразится окно для добавления элементов в
список (Рисунок 10).

Рисунок 10 – Редактор
коллекции строк в ListBox

В пустое поле мы вводим по одному
элементу списка — по одному на каждой строке. После этого все добавленные нами
элементы окажутся в списке, и мы сможем ими управлять. Создадим форму (Рисунок
11) для управления элементами формы. В качестве функционала добавим возможность
добавления элемента в конец списка, удаление и отображение выделенного
элемента.

Рисунок 11 – Макет формы для
управления содержимым списка

         Итак,
все элементы списка входят в свойство Items, которое представляет собой
коллекцию. Для добавления нового элемента в эту коллекцию, а значит и в список,
надо использовать метод
Add,
например:
listBox1.Items.Add(«Новый элемент»);.
При использовании этого метода каждый добавляемый элемент добавляется в конец
списка.

Можно добавить сразу несколько
элементов, например, массив. Для этого используется метод
AddRange:

string[]
students = {
«Кондратьев», «Дормидонтова», «Валеева»};

listBox1.Items.AddRange(students);

         В отличие от простого добавления вставка производится по
определенному индексу списка с помощью метода
Insert:

listBox1.Items.Insert(listBox1.Items.Count,
textBox1.Text);

         Свойство
listBox1.Items.Count
определяет текущее количество элементов в списке. В вышеописанной строчке кода
вставка элемента будет производится постоянно в конец списка.

Для удаления элемента по его тексту
используется метод
Remove:

listBox1.Items.Remove(«Петров»);

         Чтобы
удалить элемент по его индексу в списке, используется метод
RemoveAt:

listBox1.Items.RemoveAt(1);

         В
качестве параметра передается индекс удаляемого элемента.

Кроме того, можно очистить сразу весь
список, применив метод
Clear:

listBox1.Items.Clear();

         Используя
индекс элемента, можно получить доступ к самому элементу в списке. Например,
получим первый элемент списка:

string firstElement = listBox1.Items[0];

При выделении элементов
списка мы можем ими управлять как через индекс, так и через сам выделенный
элемент. Получить выделенные элементы можно с помощью следующих свойств
элемента ListBox:

·        
SelectedIndex: возвращает или устанавливает номер выделенного элемента
списка. Если выделенные элементы отсутствуют, тогда свойство имеет значение -1

·        
SelectedIndices: возвращает или устанавливает коллекцию выделенных элементов
в виде набора их индексов

·        
SelectedItem: возвращает или устанавливает
текст выделенного элемента

·        
SelectedItems: возвращает или устанавливает выделенные элементы в виде
коллекции

По умолчанию список
поддерживает выделение одного элемента. Чтобы добавить возможность выделения
нескольких элементов, надо установить у его свойства SelectionMode
значение MultiSimple.

Чтобы выделить элемент
програмно, надо применить метод SetSelected(int index, bool value), где
index — номер выделенного элемента. Если второй параметр — value имеет значение
true,
то элемент по указанному индексу выделяется, если false, то выделение
наоборот скрывается:

listBox1.SetSelected(2, true); // будет выделен третий
элемент

Чтобы снять выделение со всех
выделенных элементов, используется метод
ClearSelected.

Из всех событий элемента ListBox надо
отметить в первую очередь событие
SelectedIndexChanged,
которое возникает при изменении выделенного элемента. Рассмотрим реализацию
обработчика события на примере нашего задания.

private void listBox1_SelectedIndexChanged(object sender, EventArgs e)

{

        label2.Text =
Convert.ToString(listBox1.SelectedItem);

        label3.Text =
Convert.ToString(listBox1.SelectedIndex);

}

         Обработчик события изменяет текст в надписях. В label2 выводится текст выбранного
пользователем элемента, а в
label3 – его индекс.

         При нажатии на кнопку «Добавить» будет происходить
добавление нового элемента в конец списка. Конец элемента будет определять
индекс, равный
listBox1.Items.Count – текущему
количеству элементов в списке. Текстовое наполнение нового элемента будет
извлекаться из данных, введенных в
textBox1.

private void
button1_Click(
object sender, EventArgs e)

{

       
listBox1.Items.Insert(listBox1.Items.Count, textBox1.Text);

}

     Кнопка «Удалить» будет
производить удаление выбранного в списке элемента.
listBox1.SelectedIndex будет возвращать индекс выбранного пользователем элемента.

private void
button2_Click(
object sender, EventArgs e)

{

        listBox1.Items.RemoveAt(listBox1.SelectedIndex);

}

Пример написания программы

Выполним следующее задание,
разработав для него интерфейс с помощью
Windows
Forms.

ЗаданиеПоле
шахматной доски определяется парой натуральных чисел, каждое из которых не
превосходит 8: первое число — номер вертикали (при счете слева направо),
второе—номер горизонтали (при счете снизу-вверх). Даны натуральные числа a, b,
c, d, e, f, каждое из которых не превосходит 8. Определить, может ли белая ладья,
расположенная на поле (a, b), одним ходом пойти на поле (e, f), не попав при
этом под удар черной ладьи, находящейся на поле (c, d).

Решение
– Упростим решение задачи. Поля шахматного поля будем представлять в виде
флажков
CheckBox и будем создавать их динамически, то есть
явно через программный код. Финальный макет разрабатываемого приложения показан
на рисунке 12.

Рисунок
12

Промежуточный макет разрабатываемого
приложения, который необходимо собрать вручную, представлен на рисунке 13.

Рисунок
13

Label будет
хранить информацию для взаимодействия с пользователем: «Выберите позицию первой
ладьи», «Выберите позицию первой ладьи» и «Выберите новую позицию первой
ладьи». Контейнер
GroupBox будет
хранить флажки
checkBox,
каждый из которых будет представлять ячейку в шахматном поле. Кнопка «Начать
заново» перезапустит ход игры, вернув в исходное положение все
checkbox.

В разрабатываемой программе можно
выделить три сущности, которые можно представить в виде классов:

1.                
Класс Rook
(Ладья)
– будет описывать объект «Ладья» со свойствами coord_x (Позиция ладьи по
горизонтали») и coord_y (Позиция ладьи по вертикали). Конструктор класса будет
инициализировать новые координаты ладьи в шахматном поле при ее создании.
Создание будет происходить при выборе соответствующего флажка на игровом поле.

1

public class Rook

2

{

3

    public int coord_x { get; set; }

4

    public int coord_y { get; set; }

5

    public Rook(int x, int y)

6

    {

7

       
this.coord_x = x;

8

       
this.coord_y = y;

9

    }

10

}

2. Класс NewCheckBox –
данный класс будет наследоваться от стандартного класса CheckBox и будет
определять для него дополнительные свойства – это координаты (по горизонтали и
вертикали)
checkBox в шахматном поле. Координаты будут инициализироваться при
создании элементов шахматного поля.

1

public class NewCheckBox : CheckBox

2

{

3

    public int Coord_x { get; set; }

4

    public int Coord_y { get; set; }

5

    public NewCheckBox(int x, int y)

6

    {

7

       
this.Coord_x = x;

8

       
this.Coord_y = y;

9

    }

10

}

3. Класс ChessField
(Шахматное поле) – данный класс представляет описание объекта «Шахматное поле»
и будет хранить следующие свойства: это массив флажков
checkBox размерностью 8*8 для
дальнейшего их добавления в форму и переменная
state, отвечающая за этап решения задачи (0 – выбор позиции первой
ладьи, 1 – выбор позиции второй ладьи, 2 – выбор новой позиции первой ладьи).

Конструктор класса будет отвечать за
динамическое создание элементов
checkBox в форме.

1

public class ChessField

2

{

3

    public NewCheckBox[,]
checkBoxes;

4

    public int state;

5

    public ChessField(int x, int y)

6

    {

7

       
state = 0;

8

       
checkBoxes =
new NewCheckBox[x, y];

9

       
for (int i = 0; i < 8;
i++)

10

       
{

11

           
for (int j = 0; j < 8;
j++)

12

           
{

13

               
checkBoxes[i, j] =
new NewCheckBox(i, j)

14

               
{

15

                   
Checked =
false,

16

                   
Location =
new Point(i * 30 + 20, j * 30 + 20),

17

                   
Size =
new Size(20, 20),

18

                   
Name =
«cell» +
Convert.ToString(i + 1) + Convert.ToString(j + 1),

19

                   
Visible
=
true

20

               
};

21

           
}

22

       
}

23

   
}

24

}

         Конструктор в
качестве параметров принимает два целочисленных аргумента (строка 5), которые
отвечают за количество элементов в каждом измерении матрицы
checkboxes при ее создании. Свойство state инициализируется нулем (строка 7), сигнализирующем
о начальном состоянии программы (расстановка на шахматное поле первой ладьи).

         В двойном цикле
происходит обработка двумерного массива
checkboxes. Во вложенном цикле динамически создается элемент checkboxes как экземпляр класса NewCheckBox (строка 13). Далее происходит инициализация свойств экземпляра
класса
NewCheckBox (строка 15-
19). Свойство
Checked (строка
15) задает неотмеченное состояние флажка. Свойство
Location (строка 16), которое создается динамически через вызов
конструктора класса
Point, определяет новое расположение элемента относительно верхнего
левого края формы. Как аргументы конструктора задаются координаты по
горизонтали и координаты по вертикали. Свойство
Size (строка 17) задает размерность элемента по ширине и высоте.
Свойство
Name (строка 18)
изменяет значение названия элемента, по которому в дальнейшем можно к нему
обращаться. Свойство
Visible (строка 19) изменяет видимость и невидимость элемента (если True, то элемент видимый).

         После создания
необходимых для решения задачи классов можно приняться за создание
функциональности генерируемых событий в форме. При загрузке формы необходимо
проинициализировать текстовое наполнение требуемых элементов формы и добавить в
контейнер
groupBox1 все элементы, сгенерированные в классе ChessField. Обработчик события будет выглядеть следующим образом:

1

private void Chess_Load(object sender, EventArgs
e)

2

       
{

3

           
label1.Text =
«Выберите позицию первой ладьи!»;

4

           
groupBox1.Text
=
«Шахматное поле«;

5

           
chessField =
new ChessField(8, 8);

6

           
foreach (NewCheckBox i in
chessField.checkBoxes)

7

           
{

8

               
groupBox1.Controls.Add(i);

9

                i.CheckedChanged += new
System.EventHandler(
this.checkBox_CheckedChanged);

10

           
}

11

        }

В
строке 5 происходит создание экземпляра класса
ChessField, создается «Шахматное поле» размерностью 8*8. Далее, используя
цикл
foreach,
организуем обработку элементов двойного массива
checkboxes, который представляет собой
свойство экземпляра класса
ChessField, где каждый элемент массива добавляем в контейнерgroupBox1 (строка 8). В строке 9 каждому
элементу массива привязывается для его события
CheckedChanged обработчик, представляющий собой метод checkBox_CheckedChanged. Название
метода передается как аргумент при вызове конструктора обработчика события
System.EventHandler.

Самое
интересное кроется в методе обработчике события нажатия на флажок. При
изменении состояния флажка он генерирует событие
CheckedChanged. Обрабатывая это событие, мы можем получать измененный флажок и
производить определенные действия.

1

private void
checkBox_CheckedChanged(
object sender, EventArgs e)

2

{

3

        NewCheckBox
checkBox = (NewCheckBox)sender;
// приводим отправителя к элементу типа CheckBox

Здесь
мы параметр
sender,
представляющий собор элемент отправитель события (флажок), преобразуем в
пользовательский тип
NewCheckBox и сохраняем в локальную переменную checkbox. Проще говоря, в переменную checkbox сохраняется тот элемент-флажок, который выбрал пользователь.

После инициализации
переменной идет проверка состояния
Checked флажка (выбран/не выбран).

4

if (checkBox.Checked == true)

5

{

Далее
идет проверка значения свойства
chessField.state. state = 0 – это выбор позиции первой ладьи в шахматном поле, state = 1 – это выбор позиции
второй ладьи в шахматном поле,
state = 2 – это выбор новой позиции для первой ладьи.

6

if (chessField.state == 0)

7

{

8

        checkBox.AutoCheck = false;

9

        checkBox.BackColor = Color.Aqua;

10

        rookFirst = new
Rook(checkBox.Coord_x, checkBox.Coord_y);

11

        chessField.state = 1;

12

        label1.Text = «Выберите позицию второй
ладьи!»
;

13

}

14

else if
(chessField.state == 1)

15

{

16

        checkBox.AutoCheck = false;

17

        checkBox.BackColor = Color.Blue;

18

        rookSecond = new
Rook(checkBox.Coord_x, checkBox.Coord_y);

19

        chessField.state = 2;

20

        label1.Text = «Выберите
новую позицию первой ладьи!»
;

21

}

22

else if
(chessField.state == 2)

23

{

24

        if (!(rookFirst.coord_x ==
checkBox.Coord_x || rookFirst.coord_y == checkBox.Coord_y))

25

        {

26

                checkBox.Checked = false;

27

                MessageBox.Show(«Ладья так ходить не может!»);

28

         }

29

         else

30

         {

31

                 rookFirst.coord_x = checkBox.Coord_x;

32

                 rookFirst.coord_y = checkBox.Coord_y;

33

                 checkBox.BackColor = Color.Red;

34

         if (rookFirst.coord_x ==
rookSecond.coord_x || rookFirst.coord_y == rookSecond.coord_y)

35

         {

36

                 MessageBox.Show(«Ладья
под ударом!»
);

37

         }

38

         else

39

         {

40

                 MessageBox.Show(«Ладья
в безопасности!»
);

41

         }

42

         checkBox.AutoCheck = false;

43

         chessField.state = 0;

44

         label1.Text = «»;

45

}

46

}

Свойство
флажка
AutoCheck равное false блокирует изменение
состояния флажка. По умолчанию данное свойство имеет значение
true.

Свойство
BackColor позволяет изменить
цветовую заливку элемента управления. Объект
Color представляет собой цвет фона элемента управления.

При
выборе позиции для первой и второй ладьи происходить динамическое создание двух
экземпляров класса
Rook с
передачей в конструктор класса в качестве аргументов координаты по горизонтали
и вертикали элементов управления, символизирующих ячейку в шахматном поле.

При
достижении
state значения 2
идет проверка поставленных в условии задачи ограничений нового расположения
первой ладьи.

Последний
метод, который необходимо добавить – это обработчик события нажатия кнопки
«Нажать заново». При ее нажатии мы изменяем стартовое сообщение, обнуляем
значение поля
state, и с помощью
цикла обходим все хранящиеся в массиве элементы-флажки и изменяем их свойства:
цветовую заливку
BackColor устанавливаем на значение SystemColors.Control (цвет по умолчанию), устанавливаем AutoCheck на значение true, чтобы разрешить изменение состояния всех флажков и устанавливаем
Checked на значение false, чтобы изменить состояние всех флажков в контейнере на «Не
отмеченный».

1

private void button1_Click(object sender, EventArgs
e)

2

{

3

       
label1.Text =
«Выберите позицию первой ладьи!»;

4

       
chessField.state
= 0;

5

       
foreach (NewCheckBox i in
chessField.checkBoxes)

6

       
{

7

           
i.BackColor = SystemColors.Control;

8

           
i.AutoCheck =
true;

9

           
i.Checked
=
false;

10

       
}

11

}

Результат работы
приложения представлен на рисунке 14.

Рисунок
14

ЗАДАНИЕ НА ЛАБОРАТОРНУЮ РАБОТУ

Выбрать пять заданий из лабораторных
работ №2-6 (нельзя брать оба задания из одной лабораторной работы) и решить их,
разработав графический интерфейс с использованием технологии
Windows
Forms.

Вариант заданий необходимо согласовать
у преподавателя перед непосредственной разработкой!

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • C windows system32 taskmgr exe ошибка файловой системы
  • Подробная инструкция как установить windows 10
  • Windows loader by daz отзывы
  • Как поставить пароль на блокировку экрана на windows 10
  • Windows x86 web based installer