Наряду с ручным способом конструирования форм оболочка Visual Studio 2005 (и ряд других современных IDE ) содержит и визуальный редактор диалоговых окон ( Designer - конструктор форм), который обеспечивает условия комфортного программирования на этапе разработки графического интерфейса пользователя ( GUI ) оконных приложений. Конструктор форм очень облегчает эту работу, сопровождая действия программиста генерацией соответствующего кода в исходный текст приложения. Множество мастеров оболочки тоже вшивают свои изменения в исходный код.
Визуальные средства программирования автоматизируют только форму кода, а разработка содержимого в контексте решаемой задачи все равно ложится на плечи программиста. Чтобы грамотно программировать, необходимо понимать, что означает генерируемый оболочкой код. Программист должен быть способен при необходимости выправить автоматически сгенерированный код для более тонкой настройки. Поэтому наша задача овладеть как ручным, так и
Библиотечная сборка System.Windows.Forms.dll содержит несколько пространств имен, основным из которых является пространство имен System.Windows.Forms.

Пространство имен System.Windows.Forms содержит сотни типов, которые позволяют создавать высокоразвитый GUI оконных приложений. Их можно разделить на следующие большие категории:
Класс Application определяет множество статических членов, позволяющих управлять поведением различных низкоуровневых элементов приложения Windows Forms. Этот класс непосредственно взаимодействует с операционной системой по управлению приложением в целом. Класс Application не может иметь наследников ( sealed -
Выполните команду File/New/Project и настройте окно мастера примерно так
В панели Solution Explorer щелкните на пиктограмме Properties и установите тип проекта Console Application
Командой Project/Add Reference добавьте (удерживая клавишу Ctrl ) к проекту ссылки на библиотечные сборки System.dll и System.Windows.Forms.dll
Командой Project/Add New Item добавьте в проект два новых пустых файла с именами Program.cs и GeneralForm.cs
Заполните файлы следующим кодомusing System;
using System.Windows.Forms;
namespace SimpleWinApp
{
class GeneralForm : Form
{
public GeneralForm()
{
}
}
}
using System;
using System.Windows.Forms;
namespace SimpleWinApp
{
class Program
{
static void Main()
{
// Поддержка визуальных стилей Windows XP
Application.EnableVisualStyles();
// Включение формы в цикл сообщений Windows
Application.Run(new GeneralForm());
// Для задержки консольного окна
Console.ReadLine();
}
}
}
В нашем простом приложении класс GeneralForm пока не делает ничего, кроме как инкапсулирует (упаковывает) базовый библиотечный класс Form.
Класс Application определяет ряд статических свойств уровня приложения, многие из которых доступны только для чтения.
| Свойство | Описание |
|---|---|
StartupPath |
Получает путь для исполняемого файла, который запустил приложение, не включая его имя |
ExecutablePath |
Получает путь для исполняемого файла, который запустил приложение, включая имя файла |
ProductName |
Содержит значение атрибута [ AssemblyProduct ] сборки |
ProductVersion |
Содержит значение атрибута [ AssemblyVersion ] сборки |
CompanyName |
Содержит значение атрибута [ AssemblyCompany ] сборки |
Класс Application определяет ряд статических событий уровня приложения.
| Событие | Описание |
|---|---|
ApplicationExit |
Генерируется непосредственно перед тем, как данное приложение завершает работу |
Idle (простой) |
Генерируется тогда, когда цикл сообщений приложения заканчивает обработку текущего пакета сообщений и переходит в состояние ожидания |
ThreadExit |
Генерируется перед тем, как очередной поток приложения завершается. Если завершается основной поток приложения, то предшествует событию ApplicationExit |
Проверьте работу класса Application, скорректировав файл GeneralForm.cs проекта такusing System;
using System.Windows.Forms;
using System.Reflection;// Для атрибутов
// Глобальные атрибуты для сборки
[assembly:AssemblyCompany("Рога и копыта")]
[assembly:AssemblyProduct("Мыльные пузыри")]
[assembly:AssemblyVersion("0.0.0.1")]
namespace SimpleWinApp
{
class GeneralForm : Form
{
int count = 0;
const int MAX_COUNT = 100;
public GeneralForm()
{
// Настройка консольного окна
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
Console.Title = "Вспомогательное окно";
// Использование свойств класса Application
Console.WriteLine("Это приложение
создано компанией: "
+ Application.CompanyName);
Console.WriteLine("Компания производит: "
+ Application.ProductName);
Console.WriteLine("Версия данного продукта: "
+ Application.ProductVersion);
// Использование событий класса Application
Application.Idle += new EventHandler(AppIdle);
Application.ApplicationExit += new EventHandler(AppExit);
}
void AppIdle(object sender, EventArgs e)
{
Console.WriteLine("{0}) Простой
приложения", ++count);
if (count > MAX_COUNT)
{
Console.Clear(); // Очистить буфер консоли
count = 0; // Сбросить счетчик
}
}
void AppExit(object sender, EventArgs e)
{
Console.WriteLine("Графическое окно
приложения закрыто");
}
}
}
Код файла Program.cs останется прежним. Результат выполнения программы будет таким

Атрибуты сборки можно разместить в специальном файле, удалив их из кода приложения.
Выполните команду Project/New Item и выберите шаблон Assembly Information File
Откройте файл на редактирование и скорректируйте соответствующие атрибуты сборки в добавленном файле. Явное задание атрибутов в файле GeneralForm.cs нужно удалитьusing System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Рога и копыта")]
[assembly: AssemblyProduct("Мыльные пузыри")]
[assembly: AssemblyCopyright("Copyright © 2007")]
[assembly: AssemblyVersion("0.0.0.1")]
[assembly: AssemblyTitle("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
[assembly: Guid("01ac5f2e-a47c-424d-9754-ac4be62ae171")]
Класс Control определяет общее поведение элементов управления и самой формы, так как является их базовым классом. Он содержит множество свойств, методов и событий, позволяющих реагировать на изменение состояния мыши, клавиатуры, действия выделения и перетаскивания объектов, другие действия.
| Событие | Описание |
|---|---|
Click, DoubleClick, MouseEnter, MouseLeave, MouseDown, MouseUp, MouseMove, MouseHover, MouseWheel |
События, генерируемые состоянием мыши |
KeyDown, , KeyUp |
События, генерируемые состоянием клавиатуры |
DragDrop, DragEnter, DragLeave, DragOver |
События, связанные с перетаскиванием объектов |
Paint |
Событие, позволяющее взаимодействовать с + ( - интерфейс графического устройства) |
Все события дублируются защищенными ( protected ) виртуальными ( virtual ) перегружаемыми ( override ) методами (готовыми зарегистрированными обработчиками) и даже таких методов значительно больше. Каждый из этих методов снабжен префиксом On (например, OnClick(), OnPaint() и т.д.). Перегрузив такой метод в производном классе, мы получаем возможность выполнить какие-то предварительные действия перед вызовом родительской реализации обработчика события.
Все связанные с состоянием мыши события работают в паре с делегатом MouseEventHandler, способному адресоваться к любому методу, удовлетворяющему сигнатуре
void MyMouseHandler(object sender, MouseEventArgs e);
Поступающий на вход обработчика объект класса MouseEventArgs расширяет наследуемый им класс EventArgs путем добавления целого ряда членов, специально предназначенных для обработки действий мыши.
| Свойство | Описание |
|---|---|
Button |
Содержит информацию о том, какая клавиша мыши была нажата в соответствии с перечислением System.Windows.Forms.MouseButtons |
Clicks |
Возвращает число нажатий и отпусканий клавиши мыши |
Delta |
Содержит цену щелчка при вращении колеса мыши |
X, Y |
Координаты указателя мыши относительно начала координат клиента |
Обработка событий клавиатуры подобна обработке событий мыши. Все связанные с состоянием клавиатуры события работают в паре с делегатом KeyEventHandler, способному адресоваться к любому методу, удовлетворяющему сигнатуре
void MyKeyboardHandler(object sender, KeyEventArgs e);
Поступающий на вход обработчика объект класса KeyEventArgs расширяет наследуемый им класс EventArgs путем добавления целого ряда членов, специально предназначенных для обработки действий клавиатуры.
| Свойство | Описание |
|---|---|
Alt |
Содержит значение, являющееся индикатором нажатия клавиши Alt |
Control |
Содержит значение, являющееся индикатором нажатия клавиши Ctrl |
Handled |
Читает или устанавливает значение, являющееся индикатором полного завершения обработки события обработчиком |
KeyCode |
Возвращает клавишный код для событий KeyDown или KeyUp |
Modifiers |
Указывает, какие модифицирующие клавиши были нажаты ( Ctrl, Shift и/или Alt ) |
Shift |
Содержит значение, являющееся индикатором нажатия клавиши Shift |
Класс Control содержит ряд и других методов, например
Приведем пример приложения, в котором построим новую форму, способную обеспечить следующее
Добавьте к нашему решению MySolution4 новый пустой проект с именем MyEventHandler командой File/Add/New Project
Добавьте к проекту MyEventHandler ссылки на библиотечные сборки System.dll, System.Drawing.dll, System.Windows.Forms.dll
Добавьте два новых пустых файла с именем GeneralForm.cs и Program.cs
Установите тип сборки проекта Windows Application
Заполните файлы проекта следующим кодомusing System;
using System.Windows.Forms;
using System.Drawing;
namespace MyEventHandler
{
class GeneralForm : Form
{
Label lblMouseMove, lblMouseWheel, lblMouseClicks;
string lblTitleMouseMove = "Координаты мыши: ";
string lblTitleMouseWheel = "Цена щелчка поворота колеса: ";
string lblTitleMouseClicks = "Число щелчков мышью: ";
public GeneralForm()
{
// Настройка формы
this.Text = "Обработка событий Control";
this.Width = System.Convert.ToInt32(1.2 * this.Width);
Label lbl = new Label();
// Настройка меток
lbl.Location = new Point(20, 5);
lbl.AutoSize = true;
lbl.Parent = this;
lbl.Text = lblTitleMouseMove;
lblMouseMove = lbl;// Настроили объект и отдали
lbl = new Label();// Прежняя ссылка с новым адресом
lbl.Location = new Point(20, lblMouseMove.Location.Y
+ lbl.Height + 10);
lbl.AutoSize = true;
lbl.Parent = this;
lbl.Text = lblTitleMouseWheel;
lblMouseWheel = lbl;// Настроили объект и отдали
lbl = new Label();// Прежняя ссылка с новым адресом
lbl.Location = new Point(20, lblMouseWheel.Location.Y
+ lbl.Height + 10);
lbl.AutoSize = true;
lbl.Parent = this;
lbl.Text = lblTitleMouseClicks;
lblMouseClicks = lbl;// Настроили объект и отдали
lbl = null; // Больше не нужен
}
protected override void OnMouseMove(MouseEventArgs e)
{
lblMouseMove.Text = String.Format(lblTitleMouseMove
+ "x={0}; y={1}", e.X, e.Y);
base.OnMouseMove(e);
}
int countClicks = 0;
protected override void OnMouseUp(MouseEventArgs e)
{
countClicks += e.Clicks;
lblMouseClicks.Text = String.Format
(lblTitleMouseClicks + countClicks);
string title = "Событие формы";
switch (e.Button)
{
case MouseButtons.Left:
MessageBox.Show("Щелкнули
левой кнопкой", title);
break;
case MouseButtons.Right:
MessageBox.Show("Щелкнули
правой кнопкой", title);
break;
case MouseButtons.Middle:
MessageBox.Show("Щелкнули
средней кнопкой", title);
break;
}
base.OnMouseUp(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
lblMouseWheel.Text = String.Format(lblTitleMouseWheel + e.Delta);
base.OnMouseWheel(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
MessageBox.Show("Нажата клавиша: "
+ e.KeyCode.ToString(), "Нажали клавишу");
base.OnKeyUp(e);
}
}
}
using System;
using System.Windows.Forms;
namespace MyEventHandler
{
class EntryPoint
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new GeneralForm());
}
}
}
Здесь мы воспользовались перегрузкой виртуальных методов-обработчиков событий, в которых выполнили предварительную обработку данных и вызвали обработчик родительского класса для корректного завершения обработки.
Установите проект MyEventHandler стартовым, выполнив на нем в панели Solution Explorer команду Set as StartUp Project контектсного меню
Запустите проект и проверьте его работу
Класс Form добавляет к унаследованным свои свойства, методы и события. Рассмотрим некоторые из них.
| Свойство | Описание |
|---|---|
AcceptButton |
Содержит ссылку на кнопку формы, которая откликнется на нажатие клавиши Enter |
ActiveForm |
Содержит ссылку на текущую активную форму приложения |
ActiveMdiChild, IsMdiChild, IsMdiContainer |
Используется в - приложениях |
CancelButton |
Содержит ссылку на кнопку формы, которая откликнется на нажатие клавиши Esc |
ControlBox |
Управляет системной кнопкой закрытия окна и наличием системного меню |
FormBorderStyle |
Задает стиль границы формы из перечисления FormBorderStyle |
Menu |
Ссылается на MainMenu формы, если оно есть |
MaximizeBox, MinimizeBox |
Управляют кнопками сворачивания и разворачивания формы |
ShowInTaskbar |
Флаг видимости формы в панели задач операционной системы |
StartPosition |
Задает начальную позицию окна формы из перечисления FormStartPosition |
|
Определяет вид формы согласно перечислению FormWindowState |
| Метод | Описание |
|---|---|
Activate() |
Активизирует форму и предоставляет ей фокус ввода |
Close() |
Закрывает форму |
CenterToScreen() |
Размещает форму в центре экрана |
LayoutMdi() |
Размещает все дочерние формы в рамках родительской формы в соответствии с перечислением MdiLayout |
ShowDialog() |
Отображает форму в виде модального диалогового окна |
| Событие | Описание |
|---|---|
Activated |
Форма получила фокус ввода |
FormClosing, FormClosed |
Форма закрывается ( FormClosing ) или уже закрылась ( FormClosed ) |
Deactivate |
Форма утратила текущий фокус ввода |
Load |
Форма разместилась в памяти, но еще не отобразилась |
MdiChildActivate |
Активизировалось дочернее окно |
Жизнь формы начинается тогда, когда вызывается ее конструктор, перед его передачей методу Application.Run(). После размещения объекта в динамической памяти среда генерирует событие Load. В обработчике события Load можно настроить вид и поведение формы, подготовить содержащиеся в форме дочерние элементы управления, организовать доступ к ресурсам, необходимым для работы формы - то есть все то, что можно сделать в конструкторе формы.
Следующим генерируется событие Activated (активизированный). Оно генерируется тогда, когда форма получает фокус ввода. При потере формой фокуса ввода генерируется событие Deactivate. То и другое события происходят всякий раз, когда пользователь переходит от одного приложения рабочего стола к другому.
Пара событий FormClosing и FormClosed генерируются, когда пользователь хочет закрыть форму. Событие FormClosing генерируется первым и дает возможность спросить пользователя, действительно ли он хочет покинуть приложение. Оно работает в паре с делегатом System.ComponentModel.FormClosingEventHandler, определяющим в качестве аргумента экземпляр класса System.ComponentModel.FormClosingEventArgs со свойством Cancel. Если в обработчике установить для этого свойства значение true, форма не закроется и возвратится к нормальному состоянию. Если установить значение false, то форма будет закрыта и сгенерируется событие FormClosed.
Для проверки сказанного приведем пример.
Измените в проекте MyEventHandler код файла GeneralForm.cs на следующийusing System;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using EnumDialogResult = System.Windows.Forms.DialogResult;
namespace MyEventHandler
{
class GeneralForm : Form
{
string lifeTimeInfo = ""; // Вспомогательное поле
public GeneralForm()
{
// Подписка на события
this.FormClosing += new FormClosingEventHandler(OnClosing);
this.Load += new EventHandler(OnLoad);
this.FormClosed += new FormClosedEventHandler(OnClosed);
this.Activated += new EventHandler(OnActivated);
this.Deactivate += new EventHandler(OnDeactivate);
}
void OnClosing(object sender, FormClosingEventArgs e)
{
EnumDialogResult result;
result = MessageBox.Show(
"Вы действительно хотите закрыть приложение?",
"Событие Closing формы",
MessageBoxButtons.YesNo);
if (result == EnumDialogResult.No)
e.Cancel = true;
else
{
e.Cancel = false;// Продолжить закрытие
this.Hide();// Скрыть форму до закрытия
}
lifeTimeInfo += "Произошло событие Closing\n";
}
void OnLoad(object sender, EventArgs e)
{
lifeTimeInfo += "Произошло событие Load\n";
}
void OnClosed(object sender, FormClosedEventArgs e)
{
lifeTimeInfo += "Произошло событие Closed";
MessageBox.Show(lifeTimeInfo, "Отчет о событиях");
}
void OnActivated(object sender, EventArgs e)
{
lifeTimeInfo += "Произошло событие Activated\n";
}
void OnDeactivate(object sender, EventArgs e)
{
lifeTimeInfo += "Произошло событие Deactivate\n";
}
}
}
Результат выполнения будет таким
Консольное приложение - когда программа создает единственное окно ввода-вывода и принимает ввод только с клавиатуры (консольный ввод), а вывод - экран или принтер.
Windows -приложение - когда взаимодействие с пользователем организуется через
Библиотека классов позволяет создавать консольные- и VS.NET

Настройка заставляет компилятор помечать приложение определенным флажком, показывающим операционной системе, как нужно загружать и выполнять приложение. Если исполняемый файл помечен как консольное приложение, Windows при запуске приложения создает окно командной строки, в котором программа запускается и выводит информацию "строка за строкой".
Если исполняемый файл помечен как приложение Windows, то окно командной строки не создается и программа ничего не выводит на консоль. Но разработчик может разрабатывать -приложение как консольное для удобства отладки, поскольку в такой программе он может использовать операторы вывода, чтобы узнать состояние переменных в любом месте (вместо класса System.Diagnostics.Debug ). При этом отладочная информация будет выводиться в окно командной строки. На завершающем этапе перед представлением заказчику следует убрать все консольные операторы и перекомпилировать проект как Windows -приложение.
Написание кода вручную с нуля - наш лозунг на этапе обучения !!!
Мы для изучения C# будем стремиться писать код вручную и нам понадобятся пространства имен как минимум
using System; using System.Windows.Forms; using System.Drawing;
Можно вводить свои псевдонимы пространства имен, например
using SYSTEM = System; using FORMS = System.Windows.Forms; using DRAWING = System.Drawing;
Для поддержки проектов в ссылках компилятора должны быть ссылки на одноименные библиотеки.
Создайте пустое решение с именем Application
Добавьте новый проект
C#, имя FirstProject, пустой проект ( Empty Project ), чтобы мастер не генерировал никакой код
Добавьте к проекту ссылки на библиотеки, необходимые компилятору
Добавим самый минимум необходимых библиотек
System.dll System.Drawing.dll System.Windows.Forms.dll

После щелчка на кнопке OK в окне Solution Explorer появятся добавленные ссылки на соответствующие библиотеки

Добавьте пустой класс к проекту
выбрав шаблон Class и задав имя ShowForm

Мастер сгенерирует код заготовки класса
using System;
namespace FirstProject
{
///
/// Summary description for ShowForm.
///
public class ShowForm
{
public ShowForm()
{
//
// TODO: Add constructor logic here
//
}
}
}
Program 'FirstProject.exe' does not have an entry point defined (Программе 'FirstProject.exe' не определяли точку входа)
Точкой входа в приложение Windows Forms C# является функция Main(). Эта функция обязательно должна быть членом любого класса (метод Main ), поскольку глобальных функций в C# нет. Она обязательно должна быть статической.
static void Main(string[] args) {...} |
public static void Main(string[] args) {...} |
public static void Main() {...} |
private static void Main(string[] args) {...} |
private static void Main() {...} |
static int Main(string[] args) {... return 0; } |
public static int Main(string[] args) {... return 0; } |
public static int Main() {... return 0; } |
private static int Main(string[] args) {... return 0; } |
private static int Main() {... return 0; } |
using System;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
}
}
}
При запуске приложения система создаст пустое окно консольного вывода и завершит работу. Если выполнить запуск командой Debug/Start Without Debugging (Ctrl+F5), то приложение будет ждать нажатия любой клавиши, затем завершится.
В традиционных Windows -приложениях окна называются окнами, а в .NET - формами (аналогично исполнимый файл и сборка ). Для создания формы понадобится класс
System.Windows.Forms.Form
Этот класс является наследником целой цепочки классов, которую можно увидеть в окне Object Browser

Кстати, в C# разрешено только одиночное наследование, а множественное наследование, как это принято в C++,- запрещено.
Создадим в функции Main объект формы и отобразим его с помощью следующего кода
using System;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
System.Windows.Forms.Form frm;
frm = new System.Windows.Forms.Form();
frm.Show();
frm.Text = "Ждем 5 секунд";
System.Threading.Thread.Sleep(5000);
frm.Text = "Ждем еще 5 секунд";
System.Threading.Thread.Sleep(5000);
}
}
}
Здесь мы применили для фиксации формы статический метод Sleep класса потока Thread.Sleep(), приостанавливающий выполнение потока (программы, загруженной в оперативную память) на указанное в аргументе количество миллисекунд. Обратите внимание, форма есть, но она не получает фокуса ввода и с ней ничего нельзя сделать.
Чтобы оживить форму, ее нужно включить в поток как управляемую циклом сообщений Windows. Дополним код статическим методом класса System.Windows.Forms.Application
using System;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
System.Windows.Forms.Form frm;
frm = new System.Windows.Forms.Form();
frm.Show();
frm.Text = "Ждем 5 секунд";
System.Threading.Thread.Sleep(5000);
frm.Text = "Ждем еще 5 секунд";
System.Threading.Thread.Sleep(5000);
frm.Text = "Находимся в цикле сообщений";
System.Windows.Forms.Application.Run();
}
}
}
Запустите приложение и убедитесь, что при закрытии формы консольное окно не закрывается. Это потому, что мы неправильно применили метод Run. При таком подходе форма считается дочерней по отношению к родительскому консольному окну, поскольку приложение объявлено как консольное. Объект формы нужно включить в метод Run как параметр. Новый код
static void Main()
{
System.Windows.Forms.Form frm;
frm = new System.Windows.Forms.Form();
frm.Text = "Находимся в цикле сообщений";
System.Windows.Forms.Application.Run(frm);
}
Теперь нет необходимости специально рисовать форму методом Show() и при закрытии форма отправляет в главный цикл процесса сообщение о завершении приложения, поэтому закрываются оба окна.
Добавим в начало файла пространства имен, чтобы в именах файлов не указывать полные пути. Ненужное пространство имен пока закомментируем.
using System; using System.Windows.Forms; // using System.Drawing;
Код проекта будет выглядеть так
using System;
using System.Windows.Forms;
//using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
Form frm;
frm = new Form();
frm.Text = "Находимся в цикле сообщений";
Application.Run(frm);
}
}
}
В приложении должен быть только один главный цикл сообщений. Форма, которая включена в главный цикл сообщений, считается родительской и при закрытии закрывает все формы, созданные в приложении. Форма, которая создана вне цикла сообщений, считается дочерней и закрывает только саму себя.
using System;
using System.Windows.Forms;
//using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
Form child = new Form(); // Ребенок
child.Text = "Дочерняя форма";
child.Show();
Form parent = new Form(); // Родитель
parent.Text = "Главная форма (в цикле сообщений)";
Application.Run(parent);
MessageBox.Show("Цикл сообщений формы завершен."
+ "\nУправление передано функции Main()",
"Информационное окно MessageBox",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
}
}
В классе Form определяется много своих свойств, кроме того, он наследует ряд свойств от своих предков, например, от класса Control.
using System;
using System.Windows.Forms;
//using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
Form frm = new Form();
frm.Text = "Свойства формы изменены";
frm.BackColor = Color.Red;
frm.Width *= 2;
frm.Height /= 2;
frm.FormBorderStyle = FormBorderStyle.FixedSingle;
frm.MaximizeBox = false;
frm.Cursor = Cursors.Hand;
frm.StartPosition = FormStartPosition.CenterScreen;
Application.Run(frm);
}
}
}
Запустим приложение, оно выдаст ошибку, что не может найти класс или структуру Color. Запустим утилиту WinCV и определим, что эта структура находится в пространстве имен System.Drawing

Раскомментируем инструкцию using, подключающую нужное пространство имен System.Drawing к коду. Запустим проект и получим результат
Когда пользователь стучит по клавишам, двигает мышь, нажимает кнопки, щелкает полосы прокрутки и выбирает пункты меню, то постоянно вызываются методы программы со всяческих направлений. Такая модель управления вводом кажется хаотичной, но на практике все оказывается более упорядоченным, так как все вызовы методов выполняются в одном потоке в порядке следования. Часто удобно представлять программы для Windows Forms как конечные автоматы, состояние которых целиком определяется изменениями, вносимыми событиями.
Одним из важных событий в Windows Forms является событие Paint. Оно сообщает системе о том, что пришла пора перерисовать окно формы, в том числе ее клиентскую часть. Программист сам должен заботится о том, чтобы было чем перерисовать клиентскую область. Для этих целей нужно запоминать всю нужную информацию, чтобы обработчик Paint мог в любой момент ее использовать для перерисовке окна.
Событие Paint происходит перед первым появлением формы на экране, при заслонении окна другим окном, при развертывании окна после его сворачивания. Приведем пример создания обработчика вручную, при этом уберем пока конструктор класса по-умолчанию, чтобы не засорять код лишним
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
static void Main()
{
Form frm = new Form();
frm.Text = "Испытание события Paint";
frm.BackColor = Color.Yellow;
frm.Width *= 2;
frm.Height /= 2;
frm.Paint += new PaintEventHandler(frm_Paint); // Набрать вручную !!!
Application.Run(frm);
}
static int count = 0;
private static void frm_Paint(object sender,
PaintEventArgs e)
{
Color color;
if(count == 0)
{
count++;
return;
}
else if(count % 2 == 0)
color = Color.Red;
else
color = Color.Blue;
System.Drawing.Graphics gr = e.Graphics;
gr.Clear(color);
Console.WriteLine("Произошло {0} событие Paint формы",
++count);
}
}
}
Принято (но не обязательно) каждый новый класс размещать в отдельном файле.
Переименуйте функцию Main() в классе ShowForm на Execute () с заменой static на public
Переименуйте первый класс с ShowForm на BaseClass
Переименуйте файл ShowForm.cs на BaseClass.cs
Добавьте к проекту новый класс
Задайте имя класса StartClass
Удалите конструктор по-умолчанию из класса StartClass и добавьте функцию Main(), в которой разместите код создания экземпляра ( instance ) первого класса и вызов функции Execute() using System;
namespace FirstProject
{
public class StartClass
{
// Создать экземпляр базового класса
BaseClass instance = new BaseClass();
// Выполнить экземпляр базового класса
instance.Execute();
}
}
Окончательный код будет таким
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class BaseClass
{
public void Execute()
{
Form frm = new Form();
frm.Text = "Разбивка кода на два класса";
frm.BackColor = Color.Yellow;
frm.Width *= 2;
frm.Height /= 2;
frm.Paint += new PaintEventHandler(frm_Paint);
Application.Run(frm);
}
static int count = 0;
private static void frm_Paint(object sender,
PaintEventArgs e)
{
Color color;
if(count == 0)
{
Console.WriteLine("Произошло {0} событие Paint формы",
++count);
return;
}
else if(count % 2 == 0)
color = Color.Red;
else
color = Color.Blue;
System.Drawing.Graphics gr = e.Graphics;
gr.Clear(color);
Console.WriteLine("Произошло {0} событие Paint формы",
++count);
}
}
}
Для вывода текста в клиентскую область формы применяется функция формы DrawString. Она имеет параметры
Font - стиль текстаBrush - заливкаPoint.x - координата привязкиPoint.y - координата привязкиusing System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class FirstClass
{
static void Main()
{
// Создаем рабочий экземпляр на управляемой куче
new FirstClass();
}
public FirstClass()
{
StartFun();
}
static void StartFun()
{
Form frm = new Form();
frm.Text = "Вывод текста";
frm.BackColor = Color.Blue;
frm.Paint += new PaintEventHandler(frm_Paint);
Application.Run(frm);
}
static int count = 0;
private static void frm_Paint(object sender,
PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки
// на форму - отправителя
Control refControl = (Control) sender;
gr.DrawString("Привет из обработчика Paint !!!",
refControl.Font, Brushes.White, 0, 0);
Console.WriteLine("Перерисовка текста {0}",
++count);
}
}
}
Измените код класса FirstClass как показаноusing System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class FirstClass
{
private static Form parent, child;
static void Main()
{
parent = new Form();
parent.Text = "Главное окно";
parent.Height /= 4;
parent.BackColor = Color.Red;
parent.Paint += new PaintEventHandler(frm_Paint);
child = new Form();
child.Text = "Дочернее окно";
child.Height /= 4;
child.BackColor = Color.Yellow;
child.Show();
// Набрать вручную !!!
child.Paint += new PaintEventHandler(frm_Paint);
Application.Run(parent);
}
private static void frm_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки на форму
Form refForm = (Form) sender;
System.String str, strWhat;
if(refForm == parent)
{
str = "Привет из главной формы";
strWhat = "главная";
}
else
{
str = "Привет из дочерней формы";
strWhat = "дочерняя";
}
gr.DrawString(str, refForm.Font, Brushes.Black, 0, 0);
Console.WriteLine("Перерисована {0} форма", strWhat);
}
}
}
Сейчас мы продемонстрируем интересную особенность подключенных обработчиков:
Если для обработки одного и того же события к форме подключено несколько обработчиков, то обработчики вызываются все в порядке их создания!
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class FirstClass
{
static void Main()
{
Form frm = new Form();
frm.Text = "Несколько обработчиков Paint";
frm.BackColor = Color.White;
frm.Width = 250;
frm.Height = 100;
frm.Paint += new PaintEventHandler(frm_Paint1);
frm.Paint += new PaintEventHandler(frm_Paint2);
Application.Run(frm);
}
private static void frm_Paint1(object sender, PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки на форму
Form refForm = (Form) sender;
System.String str = "Привет из обработчика Paint1";
System.Int32 x = 0, y = 0;
gr.DrawString(str, refForm.Font, Brushes.Red, x, y);
Console.WriteLine("Сработал обработчик Paint1");
}
private static void frm_Paint2(object sender, PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки на форму
Form refForm = (Form) sender;
System.String str = "Привет из обработчика Paint2";
System.Int32 x = 0, y = 40;
gr.DrawString(str, refForm.Font, Brushes.Blue, x, y);
Console.WriteLine("Сработал обработчик Paint2");
}
}
}
Обратите внимание, что когда закрывается заголовок формы, событие Paint не происходит и обработчики не срабатывают, поскольку о восстановлении заголовка заботится сама операционная система. Мы же должны позаботиться о восстановлении клиентской области формы.
До сих пор мы делали все неправильно, потому, что создавали в своем классе композицию объектов Form. Поэтому мы имели возможность использовать только общедоступные ( public ) методы и свойства этого класса. Но класс Form имеет подавляющее большинство свойств и методов, доступных для наследников ( protected ). Поэтому более правильным будет подход создания нашего класса как наследника класса Form с последующим расширением его функциональности. Следует помнить, что C# поддерживает только одиночное наследование.
Переименуйте в панели Solution Explorer файл FirstClass в DerivedClass
То же самое сделайте в коде этого файла ( либо через панель Properties, предварительно выделив через Object Browser или через Class View ), переименовав имя класса в DerivedClass
Объявите класс DerivedClass наследующим базовому классу Form public class DerivedClass : Form
Внесите изменения, чтобы класс выглядел такusing System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
static void Main()
{
DerivedClass frm = new DerivedClass();
frm.Text = "Наш класс - наследник от Form";
frm.BackColor = Color.Red;
Application.Run(frm);
}
}
}
При создании экземпляра нашего класса в функции Main() мы использовали синтаксис вызова конструктора по умолчанию. Круглые скобки в этом случае всегда обязательны при создании экземпляра класса. Но если бы мы определили в своем классе хотя бы один конструктор, то система по умолчанию нам его уже не предоставит и нужно объявлять свой конструктор по умолчанию, пусть даже с пустым телом.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
int width = 600, height = 300;
// Общий конструктор
public DerivedClass(int width, int height)
{
Width = width;
Height = height;
Text = "Общий конструктор";
BackColor = Color.Red;
}
// Конструктор по умолчанию
public DerivedClass()
{
Width = width;
Height = height;
Text = "Конструктор по умолчанию";
BackColor = Color.Blue;
}
static void Main()
{
DerivedClass frm = new DerivedClass(300, 600);
Application.Run(frm);
}
}
}
Разгрузим функцию Main от лишнего кода, да и вынесем ее в другой класс (свой единоличный), чтобы не засорять пользовательский класс несвойственным ему кодом. Функция Main должна обязательно принадлежать какому-нибудь классу, поскольку глобальные функции в C# запрещены.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
public DerivedClass()// Конструктор
{
// Прямое обращение к свойствам класса Form
Text = "Наследник от Form";
BackColor = Color.Red;
StartPosition = FormStartPosition.CenterScreen;
this.Height /= 2; // this - удобно для IntelliSense
}
}
class EntryPoint
{
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass());
}
}
}
В программе функция Main может размещаться в нескольких класса. В этом случае разработчик должен определить стартовый класс.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
int width = 600, height = 300;
// Общий конструктор
public DerivedClass(int width, int height)
{
Width = width;
Height = height;
Text = "Общий конструктор";
BackColor = Color.Red;
}
// Конструктор по умолчанию
public DerivedClass()
{
Width = width;
Height = height;
Text = "Конструктор по умолчанию";
BackColor = Color.Blue;
}
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass(300, 600));
}
}
class EntryPoint
{
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass());
}
}
}
При запуске приложения будет ошибка о неопределенной точке входа. Теперь нужно определить, какой из классов, содержащих точки входа, будет стартовым в приложении.
Выполните команду главного меню оболочки Project/Properties и установите в открывшемся окне вкладки Common Properties/General параметр Startup Object в значение класса с нужной точкой входа.
Операционная система при запуске нашего приложения параллельно создает и консольное окно, поскольку имеет установку на то, что тип приложения является консольным. Отменим эту установку для компилятора.
Выполните команду главного меню оболочки Project/Properties
Установите параметр Output Type компилятора в значение Windows Application. Примените установку и закройте окно.
Запустите приложение и убедитесь, что консольное окно больше не появляется.
Верните таким же способом режим компилятора в значение Console Application, поскольку для испытаний средств языка C# такой режим нам еще нужен.Теперь, вместо того, чтобы самому регистрировать событие Paint и конструировать его обработчик, мы воспользуемся унаследованным от класса System.Windows.Forms.Control обработчиком OnPaint(),который вызывается формой автоматически при разрушении ее вида. Мы хотим контролировать этот обработчик своим кодом. В базовом классе этот обработчик объявлен виртуальным ( virtual ), поэтому его нужно переопределить в наследнике с ключевым словом override.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
public DerivedClass()// Конструктор
{
// Прямое обращение к свойствам класса Form
Text = "Переопределение наследуемого "
+ "обработчика OnPaint";
BackColor = Color.Blue;// Синий фон
StartPosition = FormStartPosition.CenterScreen;
// this - удобнее для вызова IntelliSense
this.Height /= 2;
this.Width *= 2;
}
// Замещающий метод обработки события Paint
// Переопределение наследуемого обработчика OnPaint()
protected override void OnPaint(PaintEventArgs e) // Набрать вручную !!!
{
System.Drawing.Graphics gr = e.Graphics;
System.String str = "Привет из замещенного "
+ "обработчика OnPaint";
System.Int32 x = 0, y = 0; // то же, что int - псевдоним
gr.DrawString(str, Font, // Применить как у владельца (формы)
Brushes.White, // Белый текст
x, y); // Привязка текста к клиентской области
Console.WriteLine("Сработал замещенный "
+ "обработчик OnPaint");
// Обработчик базового класса
base.OnPaint (e);
}
}
class EntryPoint
{
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass());
}
}
}
Наряду с ручным способом конструирования форм оболочка Visual Studio 2005 (и ряд других современных IDE ) содержит и визуальный редактор диалоговых окон ( Designer - конструктор форм), который обеспечивает условия комфортного программирования на этапе разработки графического интерфейса пользователя ( GUI ) оконных приложений. Конструктор форм очень облегчает эту работу, сопровождая действия программиста генерацией соответствующего кода в исходный текст приложения. Множество мастеров оболочки тоже вшивают свои изменения в исходный код.
Визуальные средства программирования автоматизируют только форму кода, а разработка содержимого в контексте решаемой задачи все равно ложится на плечи программиста. Чтобы грамотно программировать, необходимо понимать, что означает генерируемый оболочкой код. Программист должен быть способен при необходимости выправить автоматически сгенерированный код для более тонкой настройки. Поэтому наша задача овладеть как ручным, так и
Библиотечная сборка System.Windows.Forms.dll содержит несколько пространств имен, основным из которых является пространство имен System.Windows.Forms.

Пространство имен System.Windows.Forms содержит сотни типов, которые позволяют создавать высокоразвитый GUI оконных приложений. Их можно разделить на следующие большие категории:
Класс Application определяет множество статических членов, позволяющих управлять поведением различных низкоуровневых элементов приложения Windows Forms. Этот класс непосредственно взаимодействует с операционной системой по управлению приложением в целом. Класс Application не может иметь наследников ( sealed -
Выполните команду File/New/Project и настройте окно мастера примерно так
В панели Solution Explorer щелкните на пиктограмме Properties и установите тип проекта Console Application
Командой Project/Add Reference добавьте (удерживая клавишу Ctrl ) к проекту ссылки на библиотечные сборки System.dll и System.Windows.Forms.dll
Командой Project/Add New Item добавьте в проект два новых пустых файла с именами Program.cs и GeneralForm.cs
Заполните файлы следующим кодомusing System;
using System.Windows.Forms;
namespace SimpleWinApp
{
class GeneralForm : Form
{
public GeneralForm()
{
}
}
}
using System;
using System.Windows.Forms;
namespace SimpleWinApp
{
class Program
{
static void Main()
{
// Поддержка визуальных стилей Windows XP
Application.EnableVisualStyles();
// Включение формы в цикл сообщений Windows
Application.Run(new GeneralForm());
// Для задержки консольного окна
Console.ReadLine();
}
}
}
В нашем простом приложении класс GeneralForm пока не делает ничего, кроме как инкапсулирует (упаковывает) базовый библиотечный класс Form.
Класс Application определяет ряд статических свойств уровня приложения, многие из которых доступны только для чтения.
| Свойство | Описание |
|---|---|
StartupPath |
Получает путь для исполняемого файла, который запустил приложение, не включая его имя |
ExecutablePath |
Получает путь для исполняемого файла, который запустил приложение, включая имя файла |
ProductName |
Содержит значение атрибута [ AssemblyProduct ] сборки |
ProductVersion |
Содержит значение атрибута [ AssemblyVersion ] сборки |
CompanyName |
Содержит значение атрибута [ AssemblyCompany ] сборки |
Класс Application определяет ряд статических событий уровня приложения.
| Событие | Описание |
|---|---|
ApplicationExit |
Генерируется непосредственно перед тем, как данное приложение завершает работу |
Idle (простой) |
Генерируется тогда, когда цикл сообщений приложения заканчивает обработку текущего пакета сообщений и переходит в состояние ожидания |
ThreadExit |
Генерируется перед тем, как очередной поток приложения завершается. Если завершается основной поток приложения, то предшествует событию ApplicationExit |
Проверьте работу класса Application, скорректировав файл GeneralForm.cs проекта такusing System;
using System.Windows.Forms;
using System.Reflection;// Для атрибутов
// Глобальные атрибуты для сборки
[assembly:AssemblyCompany("Рога и копыта")]
[assembly:AssemblyProduct("Мыльные пузыри")]
[assembly:AssemblyVersion("0.0.0.1")]
namespace SimpleWinApp
{
class GeneralForm : Form
{
int count = 0;
const int MAX_COUNT = 100;
public GeneralForm()
{
// Настройка консольного окна
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
Console.Title = "Вспомогательное окно";
// Использование свойств класса Application
Console.WriteLine("Это приложение
создано компанией: "
+ Application.CompanyName);
Console.WriteLine("Компания производит: "
+ Application.ProductName);
Console.WriteLine("Версия данного продукта: "
+ Application.ProductVersion);
// Использование событий класса Application
Application.Idle += new EventHandler(AppIdle);
Application.ApplicationExit += new EventHandler(AppExit);
}
void AppIdle(object sender, EventArgs e)
{
Console.WriteLine("{0}) Простой
приложения", ++count);
if (count > MAX_COUNT)
{
Console.Clear(); // Очистить буфер консоли
count = 0; // Сбросить счетчик
}
}
void AppExit(object sender, EventArgs e)
{
Console.WriteLine("Графическое окно
приложения закрыто");
}
}
}
Код файла Program.cs останется прежним. Результат выполнения программы будет таким

Атрибуты сборки можно разместить в специальном файле, удалив их из кода приложения.
Выполните команду Project/New Item и выберите шаблон Assembly Information File
Откройте файл на редактирование и скорректируйте соответствующие атрибуты сборки в добавленном файле. Явное задание атрибутов в файле GeneralForm.cs нужно удалитьusing System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Рога и копыта")]
[assembly: AssemblyProduct("Мыльные пузыри")]
[assembly: AssemblyCopyright("Copyright © 2007")]
[assembly: AssemblyVersion("0.0.0.1")]
[assembly: AssemblyTitle("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
[assembly: Guid("01ac5f2e-a47c-424d-9754-ac4be62ae171")]
Класс Control определяет общее поведение элементов управления и самой формы, так как является их базовым классом. Он содержит множество свойств, методов и событий, позволяющих реагировать на изменение состояния мыши, клавиатуры, действия выделения и перетаскивания объектов, другие действия.
| Событие | Описание |
|---|---|
Click, DoubleClick, MouseEnter, MouseLeave, MouseDown, MouseUp, MouseMove, MouseHover, MouseWheel |
События, генерируемые состоянием мыши |
KeyDown, , KeyUp |
События, генерируемые состоянием клавиатуры |
DragDrop, DragEnter, DragLeave, DragOver |
События, связанные с перетаскиванием объектов |
Paint |
Событие, позволяющее взаимодействовать с + ( - интерфейс графического устройства) |
Все события дублируются защищенными ( protected ) виртуальными ( virtual ) перегружаемыми ( override ) методами (готовыми зарегистрированными обработчиками) и даже таких методов значительно больше. Каждый из этих методов снабжен префиксом On (например, OnClick(), OnPaint() и т.д.). Перегрузив такой метод в производном классе, мы получаем возможность выполнить какие-то предварительные действия перед вызовом родительской реализации обработчика события.
Все связанные с состоянием мыши события работают в паре с делегатом MouseEventHandler, способному адресоваться к любому методу, удовлетворяющему сигнатуре
void MyMouseHandler(object sender, MouseEventArgs e);
Поступающий на вход обработчика объект класса MouseEventArgs расширяет наследуемый им класс EventArgs путем добавления целого ряда членов, специально предназначенных для обработки действий мыши.
| Свойство | Описание |
|---|---|
Button |
Содержит информацию о том, какая клавиша мыши была нажата в соответствии с перечислением System.Windows.Forms.MouseButtons |
Clicks |
Возвращает число нажатий и отпусканий клавиши мыши |
Delta |
Содержит цену щелчка при вращении колеса мыши |
X, Y |
Координаты указателя мыши относительно начала координат клиента |
Обработка событий клавиатуры подобна обработке событий мыши. Все связанные с состоянием клавиатуры события работают в паре с делегатом KeyEventHandler, способному адресоваться к любому методу, удовлетворяющему сигнатуре
void MyKeyboardHandler(object sender, KeyEventArgs e);
Поступающий на вход обработчика объект класса KeyEventArgs расширяет наследуемый им класс EventArgs путем добавления целого ряда членов, специально предназначенных для обработки действий клавиатуры.
| Свойство | Описание |
|---|---|
Alt |
Содержит значение, являющееся индикатором нажатия клавиши Alt |
Control |
Содержит значение, являющееся индикатором нажатия клавиши Ctrl |
Handled |
Читает или устанавливает значение, являющееся индикатором полного завершения обработки события обработчиком |
KeyCode |
Возвращает клавишный код для событий KeyDown или KeyUp |
Modifiers |
Указывает, какие модифицирующие клавиши были нажаты ( Ctrl, Shift и/или Alt ) |
Shift |
Содержит значение, являющееся индикатором нажатия клавиши Shift |
Класс Control содержит ряд и других методов, например
Приведем пример приложения, в котором построим новую форму, способную обеспечить следующее
Добавьте к нашему решению MySolution4 новый пустой проект с именем MyEventHandler командой File/Add/New Project
Добавьте к проекту MyEventHandler ссылки на библиотечные сборки System.dll, System.Drawing.dll, System.Windows.Forms.dll
Добавьте два новых пустых файла с именем GeneralForm.cs и Program.cs
Установите тип сборки проекта Windows Application
Заполните файлы проекта следующим кодомusing System;
using System.Windows.Forms;
using System.Drawing;
namespace MyEventHandler
{
class GeneralForm : Form
{
Label lblMouseMove, lblMouseWheel, lblMouseClicks;
string lblTitleMouseMove = "Координаты мыши: ";
string lblTitleMouseWheel = "Цена щелчка поворота колеса: ";
string lblTitleMouseClicks = "Число щелчков мышью: ";
public GeneralForm()
{
// Настройка формы
this.Text = "Обработка событий Control";
this.Width = System.Convert.ToInt32(1.2 * this.Width);
Label lbl = new Label();
// Настройка меток
lbl.Location = new Point(20, 5);
lbl.AutoSize = true;
lbl.Parent = this;
lbl.Text = lblTitleMouseMove;
lblMouseMove = lbl;// Настроили объект и отдали
lbl = new Label();// Прежняя ссылка с новым адресом
lbl.Location = new Point(20, lblMouseMove.Location.Y
+ lbl.Height + 10);
lbl.AutoSize = true;
lbl.Parent = this;
lbl.Text = lblTitleMouseWheel;
lblMouseWheel = lbl;// Настроили объект и отдали
lbl = new Label();// Прежняя ссылка с новым адресом
lbl.Location = new Point(20, lblMouseWheel.Location.Y
+ lbl.Height + 10);
lbl.AutoSize = true;
lbl.Parent = this;
lbl.Text = lblTitleMouseClicks;
lblMouseClicks = lbl;// Настроили объект и отдали
lbl = null; // Больше не нужен
}
protected override void OnMouseMove(MouseEventArgs e)
{
lblMouseMove.Text = String.Format(lblTitleMouseMove
+ "x={0}; y={1}", e.X, e.Y);
base.OnMouseMove(e);
}
int countClicks = 0;
protected override void OnMouseUp(MouseEventArgs e)
{
countClicks += e.Clicks;
lblMouseClicks.Text = String.Format
(lblTitleMouseClicks + countClicks);
string title = "Событие формы";
switch (e.Button)
{
case MouseButtons.Left:
MessageBox.Show("Щелкнули
левой кнопкой", title);
break;
case MouseButtons.Right:
MessageBox.Show("Щелкнули
правой кнопкой", title);
break;
case MouseButtons.Middle:
MessageBox.Show("Щелкнули
средней кнопкой", title);
break;
}
base.OnMouseUp(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
lblMouseWheel.Text = String.Format(lblTitleMouseWheel + e.Delta);
base.OnMouseWheel(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
MessageBox.Show("Нажата клавиша: "
+ e.KeyCode.ToString(), "Нажали клавишу");
base.OnKeyUp(e);
}
}
}
using System;
using System.Windows.Forms;
namespace MyEventHandler
{
class EntryPoint
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new GeneralForm());
}
}
}
Здесь мы воспользовались перегрузкой виртуальных методов-обработчиков событий, в которых выполнили предварительную обработку данных и вызвали обработчик родительского класса для корректного завершения обработки.
Установите проект MyEventHandler стартовым, выполнив на нем в панели Solution Explorer команду Set as StartUp Project контектсного меню
Запустите проект и проверьте его работу
Класс Form добавляет к унаследованным свои свойства, методы и события. Рассмотрим некоторые из них.
| Свойство | Описание |
|---|---|
AcceptButton |
Содержит ссылку на кнопку формы, которая откликнется на нажатие клавиши Enter |
ActiveForm |
Содержит ссылку на текущую активную форму приложения |
ActiveMdiChild, IsMdiChild, IsMdiContainer |
Используется в - приложениях |
CancelButton |
Содержит ссылку на кнопку формы, которая откликнется на нажатие клавиши Esc |
ControlBox |
Управляет системной кнопкой закрытия окна и наличием системного меню |
FormBorderStyle |
Задает стиль границы формы из перечисления FormBorderStyle |
Menu |
Ссылается на MainMenu формы, если оно есть |
MaximizeBox, MinimizeBox |
Управляют кнопками сворачивания и разворачивания формы |
ShowInTaskbar |
Флаг видимости формы в панели задач операционной системы |
StartPosition |
Задает начальную позицию окна формы из перечисления FormStartPosition |
|
Определяет вид формы согласно перечислению FormWindowState |
| Метод | Описание |
|---|---|
Activate() |
Активизирует форму и предоставляет ей фокус ввода |
Close() |
Закрывает форму |
CenterToScreen() |
Размещает форму в центре экрана |
LayoutMdi() |
Размещает все дочерние формы в рамках родительской формы в соответствии с перечислением MdiLayout |
ShowDialog() |
Отображает форму в виде модального диалогового окна |
| Событие | Описание |
|---|---|
Activated |
Форма получила фокус ввода |
FormClosing, FormClosed |
Форма закрывается ( FormClosing ) или уже закрылась ( FormClosed ) |
Deactivate |
Форма утратила текущий фокус ввода |
Load |
Форма разместилась в памяти, но еще не отобразилась |
MdiChildActivate |
Активизировалось дочернее окно |
Жизнь формы начинается тогда, когда вызывается ее конструктор, перед его передачей методу Application.Run(). После размещения объекта в динамической памяти среда генерирует событие Load. В обработчике события Load можно настроить вид и поведение формы, подготовить содержащиеся в форме дочерние элементы управления, организовать доступ к ресурсам, необходимым для работы формы - то есть все то, что можно сделать в конструкторе формы.
Следующим генерируется событие Activated (активизированный). Оно генерируется тогда, когда форма получает фокус ввода. При потере формой фокуса ввода генерируется событие Deactivate. То и другое события происходят всякий раз, когда пользователь переходит от одного приложения рабочего стола к другому.
Пара событий FormClosing и FormClosed генерируются, когда пользователь хочет закрыть форму. Событие FormClosing генерируется первым и дает возможность спросить пользователя, действительно ли он хочет покинуть приложение. Оно работает в паре с делегатом System.ComponentModel.FormClosingEventHandler, определяющим в качестве аргумента экземпляр класса System.ComponentModel.FormClosingEventArgs со свойством Cancel. Если в обработчике установить для этого свойства значение true, форма не закроется и возвратится к нормальному состоянию. Если установить значение false, то форма будет закрыта и сгенерируется событие FormClosed.
Для проверки сказанного приведем пример.
Измените в проекте MyEventHandler код файла GeneralForm.cs на следующийusing System;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using EnumDialogResult = System.Windows.Forms.DialogResult;
namespace MyEventHandler
{
class GeneralForm : Form
{
string lifeTimeInfo = ""; // Вспомогательное поле
public GeneralForm()
{
// Подписка на события
this.FormClosing += new FormClosingEventHandler(OnClosing);
this.Load += new EventHandler(OnLoad);
this.FormClosed += new FormClosedEventHandler(OnClosed);
this.Activated += new EventHandler(OnActivated);
this.Deactivate += new EventHandler(OnDeactivate);
}
void OnClosing(object sender, FormClosingEventArgs e)
{
EnumDialogResult result;
result = MessageBox.Show(
"Вы действительно хотите закрыть приложение?",
"Событие Closing формы",
MessageBoxButtons.YesNo);
if (result == EnumDialogResult.No)
e.Cancel = true;
else
{
e.Cancel = false;// Продолжить закрытие
this.Hide();// Скрыть форму до закрытия
}
lifeTimeInfo += "Произошло событие Closing\n";
}
void OnLoad(object sender, EventArgs e)
{
lifeTimeInfo += "Произошло событие Load\n";
}
void OnClosed(object sender, FormClosedEventArgs e)
{
lifeTimeInfo += "Произошло событие Closed";
MessageBox.Show(lifeTimeInfo, "Отчет о событиях");
}
void OnActivated(object sender, EventArgs e)
{
lifeTimeInfo += "Произошло событие Activated\n";
}
void OnDeactivate(object sender, EventArgs e)
{
lifeTimeInfo += "Произошло событие Deactivate\n";
}
}
}
Результат выполнения будет таким
Консольное приложение - когда программа создает единственное окно ввода-вывода и принимает ввод только с клавиатуры (консольный ввод), а вывод - экран или принтер.
Windows -приложение - когда взаимодействие с пользователем организуется через
Библиотека классов позволяет создавать консольные- и VS.NET

Настройка заставляет компилятор помечать приложение определенным флажком, показывающим операционной системе, как нужно загружать и выполнять приложение. Если исполняемый файл помечен как консольное приложение, Windows при запуске приложения создает окно командной строки, в котором программа запускается и выводит информацию "строка за строкой".
Если исполняемый файл помечен как приложение Windows, то окно командной строки не создается и программа ничего не выводит на консоль. Но разработчик может разрабатывать -приложение как консольное для удобства отладки, поскольку в такой программе он может использовать операторы вывода, чтобы узнать состояние переменных в любом месте (вместо класса System.Diagnostics.Debug ). При этом отладочная информация будет выводиться в окно командной строки. На завершающем этапе перед представлением заказчику следует убрать все консольные операторы и перекомпилировать проект как Windows -приложение.
Написание кода вручную с нуля - наш лозунг на этапе обучения !!!
Мы для изучения C# будем стремиться писать код вручную и нам понадобятся пространства имен как минимум
using System; using System.Windows.Forms; using System.Drawing;
Можно вводить свои псевдонимы пространства имен, например
using SYSTEM = System; using FORMS = System.Windows.Forms; using DRAWING = System.Drawing;
Для поддержки проектов в ссылках компилятора должны быть ссылки на одноименные библиотеки.
Создайте пустое решение с именем Application
Добавьте новый проект
C#, имя FirstProject, пустой проект ( Empty Project ), чтобы мастер не генерировал никакой код
Добавьте к проекту ссылки на библиотеки, необходимые компилятору
Добавим самый минимум необходимых библиотек
System.dll System.Drawing.dll System.Windows.Forms.dll

После щелчка на кнопке OK в окне Solution Explorer появятся добавленные ссылки на соответствующие библиотеки

Добавьте пустой класс к проекту
выбрав шаблон Class и задав имя ShowForm

Мастер сгенерирует код заготовки класса
using System;
namespace FirstProject
{
///
/// Summary description for ShowForm.
///
public class ShowForm
{
public ShowForm()
{
//
// TODO: Add constructor logic here
//
}
}
}
Program 'FirstProject.exe' does not have an entry point defined (Программе 'FirstProject.exe' не определяли точку входа)
Точкой входа в приложение Windows Forms C# является функция Main(). Эта функция обязательно должна быть членом любого класса (метод Main ), поскольку глобальных функций в C# нет. Она обязательно должна быть статической.
static void Main(string[] args) {...} |
public static void Main(string[] args) {...} |
public static void Main() {...} |
private static void Main(string[] args) {...} |
private static void Main() {...} |
static int Main(string[] args) {... return 0; } |
public static int Main(string[] args) {... return 0; } |
public static int Main() {... return 0; } |
private static int Main(string[] args) {... return 0; } |
private static int Main() {... return 0; } |
using System;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
}
}
}
При запуске приложения система создаст пустое окно консольного вывода и завершит работу. Если выполнить запуск командой Debug/Start Without Debugging (Ctrl+F5), то приложение будет ждать нажатия любой клавиши, затем завершится.
В традиционных Windows -приложениях окна называются окнами, а в .NET - формами (аналогично исполнимый файл и сборка ). Для создания формы понадобится класс
System.Windows.Forms.Form
Этот класс является наследником целой цепочки классов, которую можно увидеть в окне Object Browser

Кстати, в C# разрешено только одиночное наследование, а множественное наследование, как это принято в C++,- запрещено.
Создадим в функции Main объект формы и отобразим его с помощью следующего кода
using System;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
System.Windows.Forms.Form frm;
frm = new System.Windows.Forms.Form();
frm.Show();
frm.Text = "Ждем 5 секунд";
System.Threading.Thread.Sleep(5000);
frm.Text = "Ждем еще 5 секунд";
System.Threading.Thread.Sleep(5000);
}
}
}
Здесь мы применили для фиксации формы статический метод Sleep класса потока Thread.Sleep(), приостанавливающий выполнение потока (программы, загруженной в оперативную память) на указанное в аргументе количество миллисекунд. Обратите внимание, форма есть, но она не получает фокуса ввода и с ней ничего нельзя сделать.
Чтобы оживить форму, ее нужно включить в поток как управляемую циклом сообщений Windows. Дополним код статическим методом класса System.Windows.Forms.Application
using System;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
System.Windows.Forms.Form frm;
frm = new System.Windows.Forms.Form();
frm.Show();
frm.Text = "Ждем 5 секунд";
System.Threading.Thread.Sleep(5000);
frm.Text = "Ждем еще 5 секунд";
System.Threading.Thread.Sleep(5000);
frm.Text = "Находимся в цикле сообщений";
System.Windows.Forms.Application.Run();
}
}
}
Запустите приложение и убедитесь, что при закрытии формы консольное окно не закрывается. Это потому, что мы неправильно применили метод Run. При таком подходе форма считается дочерней по отношению к родительскому консольному окну, поскольку приложение объявлено как консольное. Объект формы нужно включить в метод Run как параметр. Новый код
static void Main()
{
System.Windows.Forms.Form frm;
frm = new System.Windows.Forms.Form();
frm.Text = "Находимся в цикле сообщений";
System.Windows.Forms.Application.Run(frm);
}
Теперь нет необходимости специально рисовать форму методом Show() и при закрытии форма отправляет в главный цикл процесса сообщение о завершении приложения, поэтому закрываются оба окна.
Добавим в начало файла пространства имен, чтобы в именах файлов не указывать полные пути. Ненужное пространство имен пока закомментируем.
using System; using System.Windows.Forms; // using System.Drawing;
Код проекта будет выглядеть так
using System;
using System.Windows.Forms;
//using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
Form frm;
frm = new Form();
frm.Text = "Находимся в цикле сообщений";
Application.Run(frm);
}
}
}
В приложении должен быть только один главный цикл сообщений. Форма, которая включена в главный цикл сообщений, считается родительской и при закрытии закрывает все формы, созданные в приложении. Форма, которая создана вне цикла сообщений, считается дочерней и закрывает только саму себя.
using System;
using System.Windows.Forms;
//using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
Form child = new Form(); // Ребенок
child.Text = "Дочерняя форма";
child.Show();
Form parent = new Form(); // Родитель
parent.Text = "Главная форма (в цикле сообщений)";
Application.Run(parent);
MessageBox.Show("Цикл сообщений формы завершен."
+ "\nУправление передано функции Main()",
"Информационное окно MessageBox",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
}
}
В классе Form определяется много своих свойств, кроме того, он наследует ряд свойств от своих предков, например, от класса Control.
using System;
using System.Windows.Forms;
//using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
public ShowForm()
{
}
static void Main()
{
Form frm = new Form();
frm.Text = "Свойства формы изменены";
frm.BackColor = Color.Red;
frm.Width *= 2;
frm.Height /= 2;
frm.FormBorderStyle = FormBorderStyle.FixedSingle;
frm.MaximizeBox = false;
frm.Cursor = Cursors.Hand;
frm.StartPosition = FormStartPosition.CenterScreen;
Application.Run(frm);
}
}
}
Запустим приложение, оно выдаст ошибку, что не может найти класс или структуру Color. Запустим утилиту WinCV и определим, что эта структура находится в пространстве имен System.Drawing

Раскомментируем инструкцию using, подключающую нужное пространство имен System.Drawing к коду. Запустим проект и получим результат
Когда пользователь стучит по клавишам, двигает мышь, нажимает кнопки, щелкает полосы прокрутки и выбирает пункты меню, то постоянно вызываются методы программы со всяческих направлений. Такая модель управления вводом кажется хаотичной, но на практике все оказывается более упорядоченным, так как все вызовы методов выполняются в одном потоке в порядке следования. Часто удобно представлять программы для Windows Forms как конечные автоматы, состояние которых целиком определяется изменениями, вносимыми событиями.
Одним из важных событий в Windows Forms является событие Paint. Оно сообщает системе о том, что пришла пора перерисовать окно формы, в том числе ее клиентскую часть. Программист сам должен заботится о том, чтобы было чем перерисовать клиентскую область. Для этих целей нужно запоминать всю нужную информацию, чтобы обработчик Paint мог в любой момент ее использовать для перерисовке окна.
Событие Paint происходит перед первым появлением формы на экране, при заслонении окна другим окном, при развертывании окна после его сворачивания. Приведем пример создания обработчика вручную, при этом уберем пока конструктор класса по-умолчанию, чтобы не засорять код лишним
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class ShowForm
{
static void Main()
{
Form frm = new Form();
frm.Text = "Испытание события Paint";
frm.BackColor = Color.Yellow;
frm.Width *= 2;
frm.Height /= 2;
frm.Paint += new PaintEventHandler(frm_Paint); // Набрать вручную !!!
Application.Run(frm);
}
static int count = 0;
private static void frm_Paint(object sender,
PaintEventArgs e)
{
Color color;
if(count == 0)
{
count++;
return;
}
else if(count % 2 == 0)
color = Color.Red;
else
color = Color.Blue;
System.Drawing.Graphics gr = e.Graphics;
gr.Clear(color);
Console.WriteLine("Произошло {0} событие Paint формы",
++count);
}
}
}
Принято (но не обязательно) каждый новый класс размещать в отдельном файле.
Переименуйте функцию Main() в классе ShowForm на Execute () с заменой static на public
Переименуйте первый класс с ShowForm на BaseClass
Переименуйте файл ShowForm.cs на BaseClass.cs
Добавьте к проекту новый класс
Задайте имя класса StartClass
Удалите конструктор по-умолчанию из класса StartClass и добавьте функцию Main(), в которой разместите код создания экземпляра ( instance ) первого класса и вызов функции Execute() using System;
namespace FirstProject
{
public class StartClass
{
// Создать экземпляр базового класса
BaseClass instance = new BaseClass();
// Выполнить экземпляр базового класса
instance.Execute();
}
}
Окончательный код будет таким
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class BaseClass
{
public void Execute()
{
Form frm = new Form();
frm.Text = "Разбивка кода на два класса";
frm.BackColor = Color.Yellow;
frm.Width *= 2;
frm.Height /= 2;
frm.Paint += new PaintEventHandler(frm_Paint);
Application.Run(frm);
}
static int count = 0;
private static void frm_Paint(object sender,
PaintEventArgs e)
{
Color color;
if(count == 0)
{
Console.WriteLine("Произошло {0} событие Paint формы",
++count);
return;
}
else if(count % 2 == 0)
color = Color.Red;
else
color = Color.Blue;
System.Drawing.Graphics gr = e.Graphics;
gr.Clear(color);
Console.WriteLine("Произошло {0} событие Paint формы",
++count);
}
}
}
Для вывода текста в клиентскую область формы применяется функция формы DrawString. Она имеет параметры
Font - стиль текстаBrush - заливкаPoint.x - координата привязкиPoint.y - координата привязкиusing System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class FirstClass
{
static void Main()
{
// Создаем рабочий экземпляр на управляемой куче
new FirstClass();
}
public FirstClass()
{
StartFun();
}
static void StartFun()
{
Form frm = new Form();
frm.Text = "Вывод текста";
frm.BackColor = Color.Blue;
frm.Paint += new PaintEventHandler(frm_Paint);
Application.Run(frm);
}
static int count = 0;
private static void frm_Paint(object sender,
PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки
// на форму - отправителя
Control refControl = (Control) sender;
gr.DrawString("Привет из обработчика Paint !!!",
refControl.Font, Brushes.White, 0, 0);
Console.WriteLine("Перерисовка текста {0}",
++count);
}
}
}
Измените код класса FirstClass как показаноusing System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class FirstClass
{
private static Form parent, child;
static void Main()
{
parent = new Form();
parent.Text = "Главное окно";
parent.Height /= 4;
parent.BackColor = Color.Red;
parent.Paint += new PaintEventHandler(frm_Paint);
child = new Form();
child.Text = "Дочернее окно";
child.Height /= 4;
child.BackColor = Color.Yellow;
child.Show();
// Набрать вручную !!!
child.Paint += new PaintEventHandler(frm_Paint);
Application.Run(parent);
}
private static void frm_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки на форму
Form refForm = (Form) sender;
System.String str, strWhat;
if(refForm == parent)
{
str = "Привет из главной формы";
strWhat = "главная";
}
else
{
str = "Привет из дочерней формы";
strWhat = "дочерняя";
}
gr.DrawString(str, refForm.Font, Brushes.Black, 0, 0);
Console.WriteLine("Перерисована {0} форма", strWhat);
}
}
}
Сейчас мы продемонстрируем интересную особенность подключенных обработчиков:
Если для обработки одного и того же события к форме подключено несколько обработчиков, то обработчики вызываются все в порядке их создания!
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class FirstClass
{
static void Main()
{
Form frm = new Form();
frm.Text = "Несколько обработчиков Paint";
frm.BackColor = Color.White;
frm.Width = 250;
frm.Height = 100;
frm.Paint += new PaintEventHandler(frm_Paint1);
frm.Paint += new PaintEventHandler(frm_Paint2);
Application.Run(frm);
}
private static void frm_Paint1(object sender, PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки на форму
Form refForm = (Form) sender;
System.String str = "Привет из обработчика Paint1";
System.Int32 x = 0, y = 0;
gr.DrawString(str, refForm.Font, Brushes.Red, x, y);
Console.WriteLine("Сработал обработчик Paint1");
}
private static void frm_Paint2(object sender, PaintEventArgs e)
{
System.Drawing.Graphics gr = e.Graphics;
// Приведение типа переданной ссылки на форму
Form refForm = (Form) sender;
System.String str = "Привет из обработчика Paint2";
System.Int32 x = 0, y = 40;
gr.DrawString(str, refForm.Font, Brushes.Blue, x, y);
Console.WriteLine("Сработал обработчик Paint2");
}
}
}
Обратите внимание, что когда закрывается заголовок формы, событие Paint не происходит и обработчики не срабатывают, поскольку о восстановлении заголовка заботится сама операционная система. Мы же должны позаботиться о восстановлении клиентской области формы.
До сих пор мы делали все неправильно, потому, что создавали в своем классе композицию объектов Form. Поэтому мы имели возможность использовать только общедоступные ( public ) методы и свойства этого класса. Но класс Form имеет подавляющее большинство свойств и методов, доступных для наследников ( protected ). Поэтому более правильным будет подход создания нашего класса как наследника класса Form с последующим расширением его функциональности. Следует помнить, что C# поддерживает только одиночное наследование.
Переименуйте в панели Solution Explorer файл FirstClass в DerivedClass
То же самое сделайте в коде этого файла ( либо через панель Properties, предварительно выделив через Object Browser или через Class View ), переименовав имя класса в DerivedClass
Объявите класс DerivedClass наследующим базовому классу Form public class DerivedClass : Form
Внесите изменения, чтобы класс выглядел такusing System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
static void Main()
{
DerivedClass frm = new DerivedClass();
frm.Text = "Наш класс - наследник от Form";
frm.BackColor = Color.Red;
Application.Run(frm);
}
}
}
При создании экземпляра нашего класса в функции Main() мы использовали синтаксис вызова конструктора по умолчанию. Круглые скобки в этом случае всегда обязательны при создании экземпляра класса. Но если бы мы определили в своем классе хотя бы один конструктор, то система по умолчанию нам его уже не предоставит и нужно объявлять свой конструктор по умолчанию, пусть даже с пустым телом.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
int width = 600, height = 300;
// Общий конструктор
public DerivedClass(int width, int height)
{
Width = width;
Height = height;
Text = "Общий конструктор";
BackColor = Color.Red;
}
// Конструктор по умолчанию
public DerivedClass()
{
Width = width;
Height = height;
Text = "Конструктор по умолчанию";
BackColor = Color.Blue;
}
static void Main()
{
DerivedClass frm = new DerivedClass(300, 600);
Application.Run(frm);
}
}
}
Разгрузим функцию Main от лишнего кода, да и вынесем ее в другой класс (свой единоличный), чтобы не засорять пользовательский класс несвойственным ему кодом. Функция Main должна обязательно принадлежать какому-нибудь классу, поскольку глобальные функции в C# запрещены.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
public DerivedClass()// Конструктор
{
// Прямое обращение к свойствам класса Form
Text = "Наследник от Form";
BackColor = Color.Red;
StartPosition = FormStartPosition.CenterScreen;
this.Height /= 2; // this - удобно для IntelliSense
}
}
class EntryPoint
{
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass());
}
}
}
В программе функция Main может размещаться в нескольких класса. В этом случае разработчик должен определить стартовый класс.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
int width = 600, height = 300;
// Общий конструктор
public DerivedClass(int width, int height)
{
Width = width;
Height = height;
Text = "Общий конструктор";
BackColor = Color.Red;
}
// Конструктор по умолчанию
public DerivedClass()
{
Width = width;
Height = height;
Text = "Конструктор по умолчанию";
BackColor = Color.Blue;
}
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass(300, 600));
}
}
class EntryPoint
{
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass());
}
}
}
При запуске приложения будет ошибка о неопределенной точке входа. Теперь нужно определить, какой из классов, содержащих точки входа, будет стартовым в приложении.
Выполните команду главного меню оболочки Project/Properties и установите в открывшемся окне вкладки Common Properties/General параметр Startup Object в значение класса с нужной точкой входа.
Операционная система при запуске нашего приложения параллельно создает и консольное окно, поскольку имеет установку на то, что тип приложения является консольным. Отменим эту установку для компилятора.
Выполните команду главного меню оболочки Project/Properties
Установите параметр Output Type компилятора в значение Windows Application. Примените установку и закройте окно.
Запустите приложение и убедитесь, что консольное окно больше не появляется.
Верните таким же способом режим компилятора в значение Console Application, поскольку для испытаний средств языка C# такой режим нам еще нужен.Теперь, вместо того, чтобы самому регистрировать событие Paint и конструировать его обработчик, мы воспользуемся унаследованным от класса System.Windows.Forms.Control обработчиком OnPaint(),который вызывается формой автоматически при разрушении ее вида. Мы хотим контролировать этот обработчик своим кодом. В базовом классе этот обработчик объявлен виртуальным ( virtual ), поэтому его нужно переопределить в наследнике с ключевым словом override.
using System;
using System.Windows.Forms;
using System.Drawing;
namespace FirstProject
{
public class DerivedClass : Form
{
public DerivedClass()// Конструктор
{
// Прямое обращение к свойствам класса Form
Text = "Переопределение наследуемого "
+ "обработчика OnPaint";
BackColor = Color.Blue;// Синий фон
StartPosition = FormStartPosition.CenterScreen;
// this - удобнее для вызова IntelliSense
this.Height /= 2;
this.Width *= 2;
}
// Замещающий метод обработки события Paint
// Переопределение наследуемого обработчика OnPaint()
protected override void OnPaint(PaintEventArgs e) // Набрать вручную !!!
{
System.Drawing.Graphics gr = e.Graphics;
System.String str = "Привет из замещенного "
+ "обработчика OnPaint";
System.Int32 x = 0, y = 0; // то же, что int - псевдоним
gr.DrawString(str, Font, // Применить как у владельца (формы)
Brushes.White, // Белый текст
x, y); // Привязка текста к клиентской области
Console.WriteLine("Сработал замещенный "
+ "обработчик OnPaint");
// Обработчик базового класса
base.OnPaint (e);
}
}
class EntryPoint
{
static void Main()
{
// Создание экземпляра и передача
// его в главный цикл сообщений
// Так все и делают !!!
Application.Run(new DerivedClass());
}
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.