Исследование MS Visual С#, страница 2

Application.Run(form);

и запустите. Сейчас приложение связано с окном form (оно стало главным окном приложения) и все происходит по привычному сценарию! Профессионалы советуют поиграться с двумя окнами.

static void Main()

{

  Form f1 = new Form(), f2 = new Form();

  f1.Text = "First"; f2.Text = "Second";

  f2.Visible = true;

  Application.Run (f1);

  MessageBox.Show("App has ended");

}

Равноправны ли окна? Меняйте порядок закрытия окон и объясняйте происходящее. Какая из форм является главной? Сделайте глвной другую форму и сравните результат.

Класс окна

Обычно вместо надуманного, ни с кем (кроме класса Object) не связанного, класса A, в Windows-приложениях используется класс, который происходит от Windows.Forms. В этом случае многое упрощается. Создайте новую версию кода (наверное, Wnd4.cs).

using System;

using System.Drawing;

using System.Windows.Forms;

class MyForm : Form

{

static void Main()

{

  Application.Run (new MyForm());

  Console.WriteLine("App has ended");

}

MyForm()

{

  Text = "MyForm";

  BackColor = Color.AliceBlue;

}

}

Как создавать элементы управления

Класс Form (а, следовательно, и класс MyForm) происходит от класса Control (см. MSDN). Он унаследовал функциональность элемента управления. Более того, класс Form является контейнером элементов управления, то есть он способен вместить множество (коллекцию) элементов управления. Смотрите, как просто добавить в эту коллекцию новую кнопку. Вставьте этот код в конструктор формы и запустите на выполнение.

Button b = new Button();

b.Text ="Click Me"; b.Size = new Size(80,25); b.Location = new Point(5,10);

Controls.Add(b);

Обработка сообщений

Вы знаете, что Windows-приложения должны реагировать на сообщения о событиях (обрабатывать их), иначе они выглядят мертвыми. В .NET принято говорить об обработке событий (event handling). Сообщения (messages) подразумеваются, но не упоминаются. Самым распространенным событием является посылка системой сообщения о перерисовке. Рассмотрим как ввести реакцию на это событие.

Каждый объект класса Form уже имеет в своем составе событие Paint. Класс Form получил его в наследство от класса Control. Событие можно представить себе в виде некоего объекта, который содержит множество вспомогательных объектов (делегатов), которые по очереди вступают в игру при наступлении события. Каждый делегат содержит список того, что ему нужно сделать — некий перечень заданий делегата, которые он должен выполнить. Каждое задание можно представить себе как вызов функции обработки (event handler).

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

Функция обработки каждого события имеет свой собственный прототип (теперь, надо говорить — свою собственную сигнатуру), но большинство из них стандартно. Так, обработчик WM_PAINT должен иметь сигнатуру:

void MyPaintHandler (object sender, PaintEventArgs e);

Первый параметр содержит ссылку на того, кто сгенерировал событие (это наша форма), второй — сопровождает событие и содержит данные, сформированные системой в момент возникновения события. В этом параметре скрывается информация, сопутствующая событию (вспомните wParam и lParam в каркасе API). Так, в аргументе PaintEventArgs спрятан контекст устройства для рисования (вспомните HDC). Он нас интересует, так как позволяет рисовать в контексте, не зависящем от физического устройства (монитора или принтера). Контекст устройства для рисования замаскирован в объекте класса Graphics. Мы должны его выудить из параметра для того, чтобы получить возможность для рисования. Итак, мы должны:

¨  Создать своего делегата и дать ему задание (указать адрес функции обработки события),

¨  Добавить его в список делегатов события Paint,

¨  Добавить в класс MyForm саму функцию обработки события.

После выполнения этих действий класс будет выглядеть так.

class MyForm : Form

{

static void Main()

{

  Application.Run (new MyForm());

}

MyForm()

{

  Text = "MyForm";

  BackColor = Color.LavenderBlush;

  Paint += new PaintEventHandler (MyPaintHandler); // Добавляем делегата

}

private void MyPaintHandler (object sender, PaintEventArgs e)

{

  Form f = (Form)sender;

  Graphics g = e.Graphics;

  Brush br = new LinearGradientBrush (new Point(10,10), new Point(300,10),

       Color.Violet, Color.Yellow);

  g.DrawString ("We are painting", new Font("Arial",28), br, 20, 10);

  g.DrawBezier (new Pen(br, 10), new Point(20,250), new Point(5,50),

     new Point(300,300), new Point(250,50)); }

}

}

Контекст устройства система, как и ранее (в каркасе MFC), присылает нам в параметре. Но теперь он (вместо объекта класса CDC) спрятан в объекте класса Graphics и передан в функцию с помощью PaintEventArgs.

Вы заметили, сколько много "новых", то есть операций захвата памяти new? При рисовании, как видите, память не жалеют. В API нас учат запоминать старый инструмент, создавать новый, выбирать его в контекст устройства, использовать, затем восстанавливать старый и уничтожать вновь созданный. Теперь каждый раз, когда надо что-то сделать, все инструменты создаются заново. Это напоминает одноразовую посуду и горы мусора, которые остаются, после того как "новые" попировали. Но, как утверждает Microsoft, это не приводит к потерям — выручает инструмент под названием сборщик мусора (garbage collector).

Теперь покажем, как ввести обработчик сообщения о нажатии кнопки. Вновь вставьте в конструктор код создания кнопки и добавьте туда строку: