Создание Windows-приложений на основе Visual C#

Введение в windows-формы

Показывать лекцию целиком
Для работы с данной лекцией используйте примеры.

Основы работы с Visual Studio .NET

Microsoft Visual Studio .NET - это интегрированная среда разработки (Integrated Development Environment (IDE)) для создания, документирования, запуска и отладки программ, написанных на языках .NET. Это мощный инструмент профессиональной разработки сложных приложений, один из лучших во всем мире. Набор его функций чрезвычайно велик, сейчас мы рассмотрим его основные элементы. Другие пункты меню и управляющие окна будут описаны в тексте следующих лекций.

Когда мы запускаем Visual Studio .NET, то видим следующее окно (рис.1.1):

(рис 1.1) Главное окно программы

Начальная страница (Start Page) содержит следующие вкладки: Projects, Online Resources и My Profile. По умолчанию мы находимся на вкладке Projects, на которой расположен список недавних проектов. Мы можем перейти по гиперссылке от названия проекта и сразу продолжить работу над ним. На этой же вкладке расположены кнопки New Project и Open Project , по нажатию на которые мы запустим диалоговые окна создания нового или открытия существующего документа соответственно.

На вкладке Online Resources отображаются группы новостей, заголовки и ссылки ресурсов разработчиков. Эта опция доступна, когда компьютер подключен к Интернету.

Вкладка My Profile позволяет настроить интерфейс Visual Studio .NET по своему вкусу. Если имеется опыт работы с другими пакетам разработки, то можно выбрать привычную раскладку клавиатуры, расположение окон в этой категории. Здесь же можно задать действие при запуске, например, открытие диалогового окна создания проекта (рис. 1.2).

(рис 1.2) Определение действия при запуске программы

Если после внесенных изменений и при последующем запуске Visual Studio .NET обнаруживается непривычный вид программы, можно настроить его заново, запустив Start Page из меню Help/Show Start Page.

Скрывающиеся панели, расположенные по бокам окна, — такие как изображенные на рис. 1.1 Server Explorer или Toolbox, — можно выдвинуть, просто щелкнув на них. Мы можем закрепить их на экране, нажав на значок, или совсем убрать с экрана, а затем снова отобразить, используя соответствующий пункт меню View (или эквивалентное сочетание клавиш).

Главное окно Visual Studio.NET, подобно другим приложениям Windows, содержит строку меню, включающую в себя следующие категории (когда мы находимся на Start Page, часть категорий не видна – она появится позднее, когда будет создан проект) (рис. 1.3):

(рис 1.3) Строка меню Visual Studio .NET

В этих категориях расположены следующие команды:

  • File — открытие, создание, добавление, закрывание, печать и проч.
  • Edit — стандартные команды правки: копирование, вставка, вырезание и проч.
  • View — команды для скрытия и отображения всех окон и панелей инструментов.
  • Project — команды для работы с проектом: добавление элементов, форм, ссылок и проч.
  • Build — команды компиляции программы.
  • Debug — команды для отладки программы.
  • Data — команды для работы с данными.
  • Format — команды форматирования располагаемых элементов (выравнивание, интервал и проч.).
  • Tools — команды дополнительных инструментов и настройки Visual Studio .NET.
  • Window — управление расположением окон.
  • Help — справка.
  • Детальная работа с пунктами меню будет рассматриваться по мере использования соответствующих опций.

    Формы

    Что такое форма?

    Всякий раз, когда вы работаете с какой-либо программой, вы видите на экране прямоугольное окно. В этом окне и располагается вся информация, которая вам нужна. Формы могут иметь стандартный вид, такой, например, как у программы Microsoft Word, или причудливый — как у Winamp. Итак, форма — это экранный объект, обеспечивающий функциональность программы.

    Мы с вами приступаем к созданию Windows-приложений, поэтому первое, чему нам предстоит научиться, — создавать проекты, которые будут содержать наши собственные формы.

    Запускаем Visual Studio .NET, выбираем File/New/Project — появляется диалоговое окно (Ctrl+Shift+N приводит к тому же результату), в котором выбираем Visual C# Project и Windows Application (рис. 1.4).

    (рис 1.4) Создание нового проекта

    В поле Name задаем имя проекта — FirstForm и сохраняем его в папку, определяемую полем Location. Полученную папку вы сможете впоследствии переместить на другой компьютер и продолжить работу — в ней будут находиться все создаваемые нами файлы этого проекта. На экране появилась пустая Windows-форма.

    После того как мы создали новый проект, можно видеть основные части среды разработки (рис. 1.5).

    (рис 1.5) Главное окно программы в режиме разработки приложения

    Solution Explorer

    Окно Solution Explorer (проводник проекта, View —> Solution Explorer, или сочетание клавиш Ctrl+Alt +L) содержит компоненты, входящие в состав проекта. Пункты контекстного меню этого окна (вызывающегося нажатием правой кнопки мыши) позволяют изменять содержимое проекта, а также добавлять новые компоненты (рис. 1.6):

    (рис 1.6) Контекстное меню окна Solution Explorer

    При создании нового проекта Solution Explorer содержит компоненты, созданные шаблоном (рис. 1.7).

    (рис 1.7) Компоненты, входящие в состав нового приложения

    Папка References содержит ссылки на классы, используемые в проекте по умолчанию. Двойной щелчок мыши на подпапках References запускает окно Object Browser (проводник объектов, View —> Object Browser, или сочетание клавиш Ctrl+Alt+J). Окно Object Browser, в свою очередь, является исчерпывающим средством получения информации о свойствах объектов, как абстрактный класс brush наследуется от класса System.MarshalByRefObject и содержит методы Clone, Dispose(bool), Dispose и Finalize (рис рис. 1.8).

    (рис 1.8) Окно Object Browser

    Можно получать краткое описание любого метода, класса или свойства, просто щелкнув на нем, — на информационной панели немедленно отобразится краткая справка. Для опытного разработчика Object Browser — незаменимый помощник в работе, гораздо более удобный, чем справка.

    Файл App.ico содержит изображение иконки, которое на форме расположено в верхнем левом углу. Более подробно о создании и редактировании иконок — в конце этой лекции.

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

    Class View

    Окно Class View — (обзор классов, View —> Class View, или сочетание клавиш Ctrl+Shift+C), позволяет перемещаться в коде по выбранному объекту; содержит методы, классы, данные всего листинга проекта. Для перехода, например, в метод Main щелкаем на соответствующем названии в окне Class View (рис. 1.9).

    (рис 1.9) Окно Class View. Позволяет быстро перемещаться по коду всего проекта

    Properties Window

    Окно свойств Properties — основной инструмент настройки формы и ее компонентов. Содержимое этого окна представляет собой весь список свойств выбранного в данный момент компонента или формы. Вызывается это окно несколькими способами — в меню View выбираем пункт Properties Window (или используем клавишу F4), на выбранном объекте щелкаем правой кнопкой мыши и в контекстном меню пункт Properties выбираем объект и нажимаем F4 или просто выбираем объект и переходим в окно Properties. Когда вы только создали проект, в окне Properties отображаются свойства самой формы (рис. 1.10).

    (рис 1.10) Окно свойств Properties

    В таблице 1.1 приводится описание интерфейса самого окна Properties.

    Элемент Изображение Описание
    Object name В поле этого списка выводится название данного выбранного объекта, который является экземпляром какого-либо класса. Здесь Form1 — название формы по умолчанию, которая наследуется от класса System.Windows.Forms.Form
    Categorized При нажатии на эту кнопку производится сортировка свойств выбранного объекта по категориям. Можно закрывать категорию, уменьшая число видимых элементов. Когда категория скрыта, вы видите знак (+), когда раскрыта — (–)
    Alphabetic Сортировка свойств и событий объекта в алфавитном порядке
    Properties При нажатии на эту кнопку отображается перечисление свойств объекта
    Events При нажатии на эту кнопку отображается перечисление событий объекта
    Description Pane Панель, на которую выводится информация о выбранном свойстве. В данном случае в списке свойств формы было выбрано свойство Text

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

    Некоторые свойства формы
    Свойство Описание Значение по умолчанию
    Name Название формы в проекте. Это не заголовок формы, который вы видите при запуске формы, а название формы внутри проекта, которое вы будете использовать в коде Form1, Form2 и т.д.
    AcceptButton Устанавливается значение кнопки, которая будет срабатывать при нажатии клавиши Enter. Для того чтобы это свойство было активным, необходимо наличие по крайней мере одной кнопки, расположенной на форме None
    BackColor Цвет формы. Для быстрого просмотра различных вариантов просто щелкайте прямо на названии "BackColor" Control
    BackgroundImage Изображение на заднем фоне None
    CancelButton Устанавливается значение кнопки, которая будет срабатывать при нажатии клавиши Esc. Для того чтобы это свойство было активным, необходимо наличие по крайней мере одной кнопки, расположенной на форме None
    ControlBox Устанавливается наличие либо отсутствие трех стандартных кнопок в верхнем правом углу формы: "Свернуть", "Развернуть" и "Закрыть"
    Cursor Определяется вид курсора при его положении на форме Default
    DrawGrid Устанавливается наличие либо отсутствие сетки из точек, которая помогает форматировать элементы управления. В любом случае сетка видна только на стадии создания приложения True
    Font Форматирование шрифта, используемого для отображения текста на форме в элементах управления Microsoft Sans Serif ; 8,25pt
    FormBorderStyle Определение вида границ формы. Возможные варианты:
  • None — форма без границ и строки заголовка;
  • FixedSingle — тонкие границы без возможности изменения размера пользователем;
  • Fixed3D — границы без возможности изменения размера с трехмерным эффектом;
  • FixedDialog — границы без возможности изменения, без иконки приложения;
  • Sizable — обычные границы: пользователь может изменять размер границ;
  • FixedToolWindow — фиксированные границы, имеется только кнопка закрытия формы. Такой вид имеют панели инструментов в приложениях;
  • SizableToolWindow — границы с возможностью изменения размеров, имеется только кнопка закрытия формы
  • Sizable
    Icon Изображение иконки, располагаемой в заголовке формы. Поддерживаются форматы .ico
    MaximizeBox Определяется активность стандартной кнопки "Развернуть" в верхнем правом углу формы True
    MaximumSize Максимальный размер ширины и высоты формы, задаваемый в пикселях. Форма будет принимать указанный размер при нажатии на стандартную кнопку "Развернуть" 0;0 (Во весь экран)
    MinimizeBox Определяется активность стандартной кнопки "Свернуть" в верхнем правом углу формы True
    MinimumSize Минимальный размер ширины и высоты формы, задаваемый в пикселях. Форма будет принимать указанный размер при изменении ее границ пользователем (если свойство FormBorderStyle имеет значение по умолчанию Sizable) 0;0
    Size Ширина и высота формы 300; 300
    StartPosition Определение расположения формы при запуске приложения. Возможны следующие значения:
  • Manual — форма появляется в верхнем левом углу экрана;
  • CenterScreen — в центре экрана;
  • WindowsDefaultLocation — расположение формы по умолчанию. Если пользователь изменил размеры формы, то при последующем ее запуске она будет иметь тот же самый вид и расположение;
  • WindowsDefaultBounds — границы формы принимают фиксированный размер;
  • CenterParent — в центре родительской формы
  • WindowsDefaultLocation
    Text Заголовок формы. В отличие от свойства Name, именно это название формы, которое не используется в коде Form1, Form 2 и т.д.
    WindowState Определение положения формы при запуске. Возможны следующие значения:
  • Normal — форма запускается с размерами, указанными в свойстве Size;
  • Minimized — форма запускается с минимальными размерами, указанными в свойстве MinimumSize;
  • Maximized — форма разворачивается на весь экран
  • Normal

    Кнопка окна свойств Events (События)переключает окно Properties в режим управления обработчиками различных событий (например, мыши, клавиатуры) и одновременно выводит список всех событий компонента. Двойной щелчок мыши в поле значения события генерирует обработчик для него и переключает в режим кода.

    Toolbox

    Окно Toolbox (панель инструментов, View —> Toolbox, или сочетание клавиш Ctrl+Alt+X) содержит компоненты Windows-форм, называемые также элементами управления, которые размещаются на форме. Оно состоит из нескольких закладок: My User Controls, Components, Data, Windows Forms и General (рис. 1.11):

    (рис 1.11) Окно Toolbox

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

    (рис 1.13) Представление элементов в виде списка(рис 1.12) Представление элементов в виде значков

    В окне Toolbox доступно отображение всех закладок (рис. 1.14 и рис. 1.15).

    (рис 1.15) Устанавливаем галочку "Показать все закладки"(рис 1.14) Полный список закладок

    Закладка My User Controls позволяет хранить собственные списки элементов управления — если вы наиболее часто используете лишь несколько из них, пожалуй, имеет смысл перетащить на эту закладку нужные элементы. Или создать свою собственную закладку (рис. 1.16 и рис. 1.17):

    (рис 1.17) Добавление закладки(рис 1.16) Собственная закладка "Моя закладка для First Form"(рис 1.18) Восстановление значений по умолчанию

    Созданные таким образом закладки можно переименовать или удалить, выбрав в контекстном меню пункты Rename Tab и Delete Tab соответственно.

    Если в результате всех экспериментов вы обнаружите, что текущий вид окна Toolbox сильно отличается от первоначального, для восстановления значений по умолчанию выберите в контекстном меню любой закладки пункт Add/Remove Items… . В появившемся окне нажмите на кнопку Reset. Появляется окно предупреждения — "Настройки Toolbox будут восстановлены. Все пользовательские закладки будут удалены. Продолжать?" Согласившись с предупреждением, вы увидите вид Toolbox по умолчанию.

    Все закладки, кроме Clipboard Ring и General, содержат компоненты, которые можно перетащить на форму. Закладка Clipboard Ring представляет собой аналог буфера обмена в Microsoft Office 2003, отображающего содержимое буфера за несколько операций копирования или вырезания. Для вставки фрагмента достаточно дважды щелкнуть по нему.

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

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

    (рис 1.19) Эргономичное расположение окон, максимально освобождающее рабочую область

    Режимы дизайна и кода

    При создании нового проекта запускается режим дизайна — форма представляет собой основу для расположения элементов управления. Для работы с программой следует перейти в режим кода. Это можно сделать нескольким способами: щелкнуть правой кнопкой мыши в любой части формы и в появившемся меню выбрать View Code, в окне Solution Explorer сделать то же самое на компоненте Form 1.cs или просто дважды щелкнуть на форме — при этом сгенерируется метод Form1_Load. После хотя бы однократного перехода в режим кода в этом проекте появится вкладка Form1.cs* (см. рис. рис. 1.19), нажимая на которую, тоже можно переходить в режим кода. Для перехода в режим кода также можно использовать клавишу F7, а для возврата в режим дизайна — сочетание Shift+F7.

    Переключимся в режим кода и рассмотрим некоторые блоки.

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

    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;

    Для просмотра информации о содержимом каждого из этих пространств можно воспользоваться окном Object Browser.

    Далее определяется собственное пространство имен, имя которого совпадает с названием проекта:

    namespace FirstForm

    При необходимости это название можно менять.

    Класс формы Form1, наследуемый от System.Windows.Forms.Form, содержит в себе почти весь код:

    public class Form1 : System.Windows.Forms.Form
    {
    …
    }

    Внутри этого класса находится конструктор формы:

    public Form1()
    {
    	//
    	// Required for Windows Form Designer support
    	//
    	InitializeComponent();
    	//
    	// TODO: Add any constructor code after InitializeComponent call
    	//
    }

    Событие Initiliaze происходит в момент запуска приложения; код, добавляемый после InitializeComponent, может изменять содержимое формы или элементы управления в момент запуска.

    Область Windows Form Designer generated code содержит код графического интерфейса элементов управления и формы, автоматически генерируемый средой. Пустая форма содержит описание размеров и заголовка. Щелкните на знак (+) для просмотра этой области:

    #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.Size = new System.Drawing.Size(300,300); // размер формы в пикселях
    	this.Text = "Form1";// заголовок формы.
    }
    #endregion

    Можно менять значения параметров, создаваемые средой, и тогда изменения немедленно отразятся на графическом интерфейсе. Концепция области Windows Form Designer generated code схожа с концепцией WYSIWYG WYSIWYG — "What You See Is What You Get" — "Что видишь, то и получаешь". редакторов HTML-кода, где можно размещать компоненты перетаскиванием, а среда сама генерирует HTML-код.

    Метод Main реализует главную точку входа в программу — то есть место, откуда начинается выполнение написанного нами кода:

    static void Main() 
    {
    	Application.Run(new Form1());
    }

    При отладке больших программ удобно использовать нумерацию строк, которую можно включить в пункте меню Tools/Options…/Text Editor/C# — на форме Display — галочка Line Numbers (рис. 1.20).

    (рис 1.20) Отображение нумерации строк кода

    При долгой работе над кодом контрастные черные буквы на белом фоне вызывают усталость, поэтому желательно в качестве фонового цвета установить другой — например, серый. Это можно сделать в Options на вкладке Environments/Font and Colors (рис. 1.21).

    (рис 1.21) Вкладка Environments/Font and Colors. Здесь же можно сменить шрифт кода — например, установить Times New Roman

    В результате режим кода принимает следующий вид (рис. 1.22):

    (рис 1.22) Эргономичный вид режима кода

    Свойства проекта

    Рассмотрим свойства проекта. В окне Solution Explorer выделяем название проекта — FirstForm, щелкаем правой кнопкой мыши и выбираем в меню пункт Properties. В появившемся окне содержатся все свойства текущего проекта (рис. 1.23).

    (рис 1.23) Окно Common Properties / General

    В окне свойств содержится довольно много параметров. Рассмотрим некоторые, наиболее употребительные.

    Вкладка Common Properties / General включает в себя следующие свойства:

  • Assembly Name — название сборки (подробнее о сборках см. лекцию 9)
  • Output Type — тип компилируемого приложения. По умолчанию для Windows-приложений стоит Windows Application.
  • Default Namespace — название пространства имен в коде. По умолчанию совпадает с именем проекта.
  • Startup Object — название класса, содержащего точку входа в программу – метод Main.
  • Application Icon — путь к файлу с иконкой приложения.
  • Project File — имя файла с информацией о проекте. Находится внутри папки с проектом;
  • Project Folder — путь к файлу с проектом.
  • Output File — название файла, создаваемого при компиляции, — выходного файла. Совпадает с именем проекта.
  • На вкладке Configuration Properties / Build рассмотрим некоторые свойства (рис. 1.24).

    (рис 1.24) Окно Configuration Properties / Build
  • Optimize Code — оптимизация программы, значение этого свойства true может значительно увеличить производительность приложения.
  • Allow Unsafe Code Blocks — разрешить использование ключевого слова unsafe в коде проекта.
  • Warning Level — уровень предупреждений, отображаемых при компиляции программы.
  • Treat Warnings As Errors — воспринимать все предупреждения как ошибки. Если объявить переменную в коде, но нигде не использовать ее, при значении этого свойства False приложение скомпилируется, при значении True — нет.
  • Output Path — путь, где формируется выходной файл. Папка bin находится внутри папки проекта.
  • Generate Debugging Information — выводить информацию при отладке. Это свойство должно быть включено: именно эти сообщения помогают исправлять код.
  • Компиляция программы

    Для проверки программы используются два способа отладки. Первый способ — Build Solution, который проверяет код и компилирует программу, не запуская ее (рис. 1.22). Это очень удобно, когда вы работаете над отдельным модулем большой разработки, и нет возможности проверять весь продукт в целом. Для запуска этого способа выбираем в главном меню пункт Build —> Build Solution (или сочетание клавиш Ctrl+Shift+B) (рис. 1.25).

    (рис 1.25) Пункт главного меню Build

    При этом появляется окно Output, в котором выводится информация о всех стадиях компиляции (рис. 1.26)

    (рис 1.26) Окно Output, приложение не содержит ошибок

    Если в коде имеются ошибки, окно Output выведет сообщение об ошибках, затем появится их список, причем для отладки достаточно дважды щелкнуть на соответствующей ошибке для перехода к нужному участку кода (рис. 1.27):

    (рис 1.27) Окно Output, в коде не хватает фигурной скобки

    Другой способ компиляции программы — Debug, при котором проверяется код, компилируется программа и формируется пользовательский интерфейс (рис. 1.28).

    (рис 1.28) Пункт главного меню Debug

    Для запуска этого способа нажимаем клавишу F5. На экране снова появляется окно Output, информирующее нас о ходе компиляции. Если приложение не содержит ошибок, то на экране появится готовая форма (рис. 1.29):

    (рис 1.29) Приложение запущено

    При запуске приложения в папке bin\Debug внутри папки проекта возникает файл FirstForm.exe и файлы, необходимые для отладки. Файл FirstForm.exe и представляет собой готовое приложение. Готовое приложение для распространения необходимо скомпилировать в режиме Release — тогда появится папка bin\Release, которая будет содержать только FirstForm.exe. Вы можете просто скопировать его на другой компьютер, и если там имеется .NET Framework, все будет работать.

    В меню Debug также расположены все средства для пошаговой отладки кода, которые вы уже встречали при работе с консольными приложениями.

    Какова разница между компиляциями Build и Debug? Предположим, что вы разрабатываете приложение, которое будет подключаться к базе данных на удаленном компьютере, недоступном в момент разработки. Приложение будет получать данные сразу же при запуске. Для отладки кода программы удобнее использовать режим Build, поскольку отладка в режиме Debug будет сопровождаться сообщениями об ошибках, возникающих из-за невозможности подключения к другому компьютеру. Само подключение также будет занимать некоторое время.

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

    Делегаты

    Когда вы изучали синтаксис языка C#, то проходили понятие делегатов и событий. Изучение синтаксиса обычно проходит в консольных приложениях, где смысл использования делегатов и событий зачастую остается неясным. Между тем, это основа всего Windows-программирования. Рассмотрим вначале еще раз, что же это такое, а затем интерпретируем наши представления для создания Windows-форм.

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

    Когда мы располагаем на форме кнопку, мы знаем, что при наступлении события — нажатия на кнопку — что-то должно произойти: кнопка должна сообщить объекту или объектам, что мы нажали на нее. Однако, эти объекты еще могут быть не определены. Вместо того чтобы привязывать кнопку к конкретному объекту, мы связываем событие кнопки с делегатом, а когда приложение будет выполняться, назначим делегатом тот метод, который будет необходим.

    Запустите Visual Studio .NET. Создайте новое консольное приложение и назовите его Delegate. В классе Class1 объявляем делегат:

    class Class1
      {
        delegate void Mydelegate (string s);
    
    //…продолжение кода.
    }

    Синтаксис объявления делегата такой: сначала пишем ключевое слово delegate, затем — тип возвращаемого значения ( void в нашем примере), потом — произвольное имя делегата (у нас это Mydelegate ), после которого в круглых скобках перечисляем параметры (у нас только один параметр типа string ). Объявленный делегат появится в окне Class View (со специальным значком для делегатов) (рис. 1.30).

    (рис 1.30) Созданный делегат в окне Class View

    В этом же классе добавим метод, который просто-напросто будет выводить некоторую переменную s на экран:

    class Class1
    	{
    		delegate void Mydelegate( string s);
    
    		static void  MetodВ большинстве учебников по программированию, даже написанных на русском языке, принято давать названия объектов на английском языке. В результате достигается железная строгость изложения и путаница у новичков, которые тратят дополнительные усилия на разбор  названий, предопределенных средой слов, и поиском их перевода в Lingvo.  Конечно, в коммерческих распределенных проектах следует придерживаться общих правил, но  для большей ясности далее в некоторых местах  я буду писать "Metod"  а не "Method", "Chelovek" а не "Man". ( string s)
    		{
    			Console.WriteLine(s);
    		}
    //…продолжение кода.
    }

    Когда мы создаем класс, то вначале мы его объявляем, а затем создаем объект класса (или его экземпляр). Мы объявили делегат Mydelegate, а теперь создадим экземпляр этого делегата del:

    [STAThread]
    	static void Main(string[] args)
    	{
    		Mydelegate del = new Mydelegate(Metod);
    
    //…продолжение кода.
    }

    Делегат Mydelegate связывается здесь с методом Metod. Теперь созданному экземпляру делегата передадим в качестве параметра переменную типа string, заключенную в кавычки:

    [STAThread]
    	static void Main(string[] args)
    	{
      Mydelegate del = new Mydelegate(Metod);
      del("переменная типа string, которую принимает Metod, вызываемый делегатом del");
    }

    Запустите приложение (Ctrl+F5 — для консольных приложений — чтобы иметь возможность его разглядеть) (рис. 1.31).

    (рис 1.31) Передача переменной делегатом методу

    Как это работает? Экземпляру делегата del мы передаем переменную типа string, заключенную в скобках. Экземпляр del принадлежит делегату Mydelegate, который принимает метод Metod. Этот метод, в свою очередь, принимает переменную string s, которая выводится затем на экран. Обратите внимание, что сигнатуры делегата Mydelegate и метода Metod одинаковы! Если мы изменим тип принимаемой методом переменной на, например, int, среда выдаст ошибку.

    using System;
    
    namespace Delegate
    {
    	
    	class Class1
    	{
    		delegate void Mydelegate( string s);
    
    		static void Metod( string s)
    		{
    			Console.WriteLine(s);
    		}
    
    
    	
    		[STAThread]
    		static void Main(string[] args)
    		{
    			Mydelegate del = new Mydelegate(Metod);
    			del("переменная типа string, которую принимает Metod, вызываемый делегатом del");
    		}
    	}
    }

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

    using System;
    
    namespace Delegate2
    {
    	//Создаем новый класс chelovek
    	class chelovek
    	{
    		//Объявляем переменную slovo
    		public string slovo;
    		//Создаем метод govorit
    		public void govorit( string s)
    		{
    			Console.WriteLine( slovo + s);
    		}
    		class Class1
    		{
    			//Объявляем делегат Mydelegate,  который имеет ту же саму сигнатуру, что и метод  govorit
    			delegate void Mydelegate(string s);
    
    			[STAThread]
    			static void Main(string[] args)
    			{
    				//Создаем экземпляр student класса chelovek
    				chelovek student = new chelovek();
    				//Создаем экземпляр del делегата  Mydelegate
    				Mydelegate del = new Mydelegate(student.govorit);
    				//Переменной slovo экземпляра student присваиваем значение "привет"
    				student.slovo = "привет";
    				// Экземпляру делегата del передаем переменную в кавычках
    				del("  - Сказал студент через делегат");
    				//Переменной slovo экземпляра student присваиваем значение "пока"
    				student.slovo = "пока";
    				// Экземпляру делегата del передаем переменную в кавычках
    				del(" - Сказал студент через делегат");
    			}
    		}
    	}
    }

    В этом примере мы дважды передаем переменную одному экземпляру делегата (рис. 1.32).

    (рис 1.32) Передача переменной одному экземпляру делегата дважды

    Конечно же, здесь тот же самый результат можно было получить без всякого введения делегатов — просто инициализируя переменную slovo. Но пока для нас важно не найти наилучший способ получения заданного результата, а разобраться, как же работают делегаты. Создайте новое консольное приложение и назовите его Matem. Далее опять привожу полный код с комментариями:

    using System;
    
    namespace Matem
    {
    	//Создаем новый класс matematika
    	class matematika
    	{
    		//Объявляем переменную a
    		public int a;
    		//Создаем метод calculate
    		public void calculate()
    		{
    			int b = a*a;
    			int c = a*a*a;
    			int d = a*a*a*a;
    			Console.WriteLine ("Само число: " + a + "\nКвадрат: " + b + "\nКуб: " + c + " \nЧетвертая степень:" + d);
    		}
    	}
    	class Class1
    	{
    		[STAThread]
    		static void Main(string[] args)
    			{
    			//Создаем экземпляр primer класса matematika
    				matematika primer = new matematika();
    			//Переменной a экземпляра primer присваиваем значение 2
    				primer.a = 2;
    			//Вызываем метод calculate
    				primer.calculate();
    				
    			}
    	}
    }

    Результатом выполнения этой программы будет вычисление квадрата, куба и четвертой степени значения переменной (рис. 1.33):

    (рис 1.33) Результат приложения Matem

    Теперь создадим делегата и его экземпляр (можно создать новое консольное приложение — Matem2 — или изменить существующее):

    using System;
    
    namespace Matem2
    {
    	//Создаем новый класс matematika
    	class matematika
    	{
    		//Объявляем переменную a
    		public int a;
    		//Создаем метод calculate
    		public void calculate(int chislo)
    		{
    			int b = a*a;
    			int c = a*a*a;
    			int d = a*a*a*a;
    			Console.WriteLine ("Само число: " + a + "\nКвадрат: " + b + "\nКуб: " + c + " \nЧетвертая степень:" + d);
    		}
    	}
    	class Class1
    	{
    		delegate void Mydelegate(int chislo);
    		[STAThread]
    		static void Main(string[] args)
    		{
    			//Создаем экземпляр primer класса matematika
    			matematika primer = new matematika();
    			//Создаем экземпляр del делегата Mydelegate
    			Mydelegate del = new Mydelegate(primer.calculate);
    			//Переменной a экземпляра primer присваиваем значение 2
    			primer.a = 2;
    			// Экземпляру делегата del передаем переменную
    			del(1);
    			//Переменной a экземпляра primer присваиваем значение 3
    			primer.a = 3;
    			// Экземпляру делегата del передаем переменную
    			del(2);
    			//Переменной a экземпляра primer присваиваем значение 4
    			primer.a = 4;
    			// Экземпляру делегата del передаем переменную
    			del(3);
    				
    		}
    	}
    }

    Результатом выполнения этой программы будет последовательный вывод самого числа, его квадрата, куба и четвертой степени (рис. 1.34):

    (рис 1.34) Результат приложения Matem2

    Многообъектные делегаты

    В коде Matem2 делегат Mydelegate и метод calculate имеют одинаковую сигнатуру в соответствии с общими правилами. Для обращения через делегат к методу используется переменная int chislo, не содержащая вводимых значений для вычислений. Мы использовали один экземпляр делегата для обращения к одному методу, причем для вычисления значения новой переменной мы каждый раз ее объявляли. Однако делегат может вмещать в себя несколько методов. Такой делегат называется многообъектным (множественным) — при обращении к нему он последовательно вызывает каждый метод.

    Создайте новое консольное приложение. Назовите его Matem3. Рассмотрим листинг с комментариями этого приложения (результат — рис. 1.35):

    using System;
    
    namespace Matem3
    {
    		class matematika
    	{
    		
    		public int a;
    
    		public void calculatesamochislo(int chislo)
    		{
    			Console.WriteLine ("Само число: " + a);
    		}
    		public void calculatecvadrat(int chislo)
    		{
    			int b = a*a;
    			Console.WriteLine ("\nКвадрат: " + b);
    		}
    		public void calculatecub(int chislo)
    		{
    			int c = a*a*a;
    			Console.WriteLine ("\nКуб: " + c);
    		}
    	}
    
    	class Class1
    	{
    		delegate void Mydelegate(int chislo);
    		
    		[STAThread]
    		static void Main(string[] args)
    		{
    			matematika primer = new matematika();
    			Mydelegate del = new Mydelegate(primer.calculatesamochislo);
    			//Экземпляру делегата del добавляем метод calculatecvadrat
    			del+= new Mydelegate(primer.calculatecvadrat);
    			//Экземпляру делегата del добавляем метод calculatecub
    			del+= new Mydelegate(primer.calculatecub);
    			primer.a = 2;
    			del(1);
    //		//Развернутый вариант
    //			del=del + new Mydelegate(primer.calculatecvadrat);
    //			del = del+ new Mydelegate(primer.calculatecub);
    //			primer.a = 2;
    //			del(1);
    
    		}
    	}
    }

    В языке С# вы уже сталкивались с сокращенной записью операторов, так, выражение x += y в развернутом виде означает x = x + y. Делегаты понимают синтаксис + и +=, и поэтому в комментарии указывается развернутый вариант добавления методов. Делегаты понимают также операции – и –= для удаления вызовов методов из делегата.

    (рис 1.35) Результат приложения Matem3

    События

    С помощью событий приложения Windows получают уведомления о том, что что-то произошло. Например, при нажатии на кнопку мыши приложение, в окне которого вы произвели это действие, будет уведомлено об этом событии. То же самое происходит, когда вы делаете что-то с помощью клавиатуры. Событийная модель стала основой современного программирования. Обработчик события реализуется на языке С#, как правило, в виде делегата.

    Создайте новое консольное приложение и назовите его Event (Событие). Рассмотрим листинг этого приложения:

    using System;
    
    namespace Event
    {
    	//Объявляем делегат Mydelegate
    	delegate void Mydelegate();
    	//Создаем класс Button, в котором будет находится событие и метод  для него
    	class Button
    	{
    		// Объявляем событие Sobitie на основе делегата
    		public event Mydelegate Sobitie;
    		//Cоздаем метод для события, который просто будет обращаться к событию
    		public void MetoddlyaSobitiya()
    
    		{
    			//Можно вставить проверку наличия события 
    		//if (Sobitie !=null)
    			Sobitie();
    		}
    
    
    		class Class1
    		{
    			[STAThread]
    			static void Main(string[] args)
    			{
    				// Создаем экземпляр btn класса Button
    				Button btn = new Button();
    				//привязываем обработчика  для события Sobitie экземпляра btn. Когда в //скобках укажете 
    				// Metodobrabotchik, нажмите дважды клавишу Tab
    				btn.Sobitie += new Mydelegate(Metodobrabotchik);
    				//Развернутая запись строки выше
    				//btn.Sobitie =  btn.Sobitie + new Mydelegate(Metodobrabotchik);
    				//вызываем метод для события
    				btn.MetoddlyaSobitiya();
    			}
    				// Создаем метод-обработчик, если среда сгенерировала его сама — добавляем //строку вывода
    				private static void Metodobrabotchik ()
    				{
    						Console.WriteLine("Произошло событие");
    				}
    			}
    		}
    	}

    Сначала мы в строке

    delegate void Mydelegate();

    объявляем делегат. Параметров у него нет (хотя могут и быть), тип — void.

    Затем мы объявляем класс Button, внутри которого объявляем событие Sobitie (имя произвольное):

    ...
        public event Mydelegate Sobitie;
        ...

    Синтаксис следующий: модификатор доступа (здесь — public), затем ключевое слово event, потом имя делегата, на основании которого мы создаем наше событие (здесь Mydelegate) и, наконец, имя события (Sobitie). Обратите внимание, что событие появится на вкладке Class View (рис. 1.36).

    (рис 1.36) Событие в окне Class View. Значок события имеет характерный вид молнии

    Далее в нашем классе с событиями мы в некотором методе это событие вызываем:

    ...
    public void MetoddlyaSobitiya()
    
    		{
    			Sobitie();
    		}

    Класс с событием создан. Далее в классе Class1 мы объявляем экземпляр btn нашего класса Button (того самого, в котором мы объявили событие):

    Button btn = new Button();

    Остается теперь указать, что за обработчик будет у события Sobitie класса Button. Это мы делаем в строке

    btn.Sobitie += new Mydelegate(Metodobrabotchik);

    Этой строчкой мы указываем, что событие Sobitie будет обрабатывать метод Metodobrabotchik класса Class1. Когда мы указываем этот метод, IntellSense предложит дважды нажать клавишу Tab для создания шаблона метода Metodobrabotchik. Сделав это, нам останется только добавить код в методе — здесь вывод строки "Произошло событие":

    …
    
    private static void Metodobrabotchik ()
    {
    		Console.WriteLine("Произошло событие");
    }
    
    …

    Результатом выполнения этой программы станет вывод на экран строки "Произошло событие" (рис. 1.37).

    (рис 1.37) Результат приложение Event

    Для того чтобы лучше разобраться в логике этого листинга, желательно просмотреть ее выполнение в пошаговом режиме (используйте клавишу F11).

    Класс с событием

    Даже в консольных приложениях использование делегатов и событий — необходимый и удобный способ реализации многих идей. Рассмотрим приложение, имитирующее выпадение игральной кости. Если выпадет число 6, сгенерируется событие — max. Создайте новое консольное приложение, назовите его ClasswithEvent и воспользуйтесь следующим кодом:

    using System;
    
    namespace ClasswitnEvent
    {
    	//Объявляем делегат по имени EventHandler (Event Handler — oбработчик события)
    	public delegate void EventHandler();
    	// Создаем класс "Игральная кость"
    	class IgralnayaKost
    	{
    		Random r;
    		//Объявляем событие на основе делегата
    		public event EventHandler max;
    		//В конструкторе класса объявляем переменную r, генерируемую встроеным объектом Random
    		public IgralnayaKost()
    		{
    			r=new Random();
    		}
    		//Создаем метод, в котором будет вызываться событие
    		public int random()
    		{
    			//Случайное число от 1 до 6
    			int res = r.Next(6)+1;
    			if(res==6)
    			{
    				//Вызываем событие
    				max();
    			}
    			return res;
    		}
    	}
    
    
    	class Class1
    	{
    		/// <summary>
    		/// The main entry point for the application
    		/// </summary>
    		[STAThread]
    		static void Main(string[] args)
    		{		
    		
    			//Создаем экземпляр brosok класса IgralnayaKost
    			IgralnayaKost brosok=new IgralnayaKost();
    			//Добавляем  обработчика события
    			brosok.max+=new EventHandler(Metodobrabotchik);
    			//Вызываем  метод, в котором вызывается событие
    			for(int i=0; i<15; i++)
    			{
    				Console.WriteLine("{0}", brosok.random());
    			}
    
    		}
    		//Обработчик для события
    		public static void Metodobrabotchik()
    		{
    			Console.WriteLine("Выпало максимальное количество очков");
    		}
    	}
    }

    В классе Class1 создается новый экземпляр brosok класса IgralnayaKost, затем событию max определяется обработчик Metodobrabotchik и запускается метод random 15 раз. Если выпадет шестерка, то возникнет событие max и сработает обработчик для него. Результат выполнения программы будет каждый раз разный и будет выглядеть примерно так (рис. 1.38):

    (рис 1.38) Результат приложения ClasswithEvent

    События в Windows-приложениях

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

    Откройте снова приложение FirstForm. Из окна Toolbox перетащите элемент управления Button на форму. Дважды щелкните на кнопке button1. В коде найдите область Windows Form Designer generated code. В таблице 1.3 сравниваются листинги приложений Event и FirstForm c кнопкой.

    Консольное приложение EventWindows-приложение FirstForm
    using System;
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    namespace Event
    {
    //Объявляем делегат Mydelegate
    delegate void Mydelegate();
    //Создаем класс Button, в котором 
    //будет находится событие 
    //и метод  для него
    class Button
    {
    // Объявляем событие Sobitie 
    // на основе делегата
    public event Mydelegate Sobitie;
    //Cоздаем метод для события, 
    //который просто будет 
    //обращаться к событию
    public void MetoddlyaSobitiya()
    {
      //Можно вставить проверку наличия события. 
      //if (Sobitie !=null)
      Sobitie();
    }
    class Class1
    {
    namespace FirstForm
    {
    	
    public class Form1 : System.Windows.Forms.Form
    {
      private System.Windows.Forms.Button button1;
    	
    private System.ComponentModel.Container 
      components = null;
    		
    public Form1()
    {
    			
    InitializeComponent();
    			
    }
    
    protected override void Dispose(bool disposing)
    {
    	if( disposing )
    {
    	if (components != null) 
    {
    	components.Dispose();
    }
    }
    base.Dispose( disposing );
    }
    
    #region Windows Form Designer generated code
    	
    private void InitializeComponent()
    {
    // Среда автоматически создает экземпляр 
    // button1 класса Button при перетаскивании 
    // элемента управления на форму
    this.button1 = 
          new System.Windows.Forms.Button();
    this.SuspendLayout();
    // 
    // button1
    // 
    this.button1.Location = 
          new System.Drawing.Point(104, 144);
    this.button1.Name = "button1";
    this.button1.TabIndex = 0;
    this.button1.Text = "button1";
    // Среда автоматически привязывает 
    // обработчик для события Click экземпляра 
    // button1. EventHandler – это делегат.
    this.button1.Click += 
     new System.EventHandler(this.button1_Click);
    // 
    // Form1
    // 
    this.AutoScaleBaseSize = 
          new System.Drawing.Size(5, 13);
    this.ClientSize = 
          new System.Drawing.Size(292, 266);
    this.Controls.Add(this.button1);
    this.Name = "Form1";
    this.Text = "Fom1";
    this.ResumeLayout(false);
    
    }
    #endregion
    [STAThread]
    static void Main(string[] args)
    {
     // Создаем экземпляр btn класса Button
     Button btn = new Button();
     //привязываем обработчик для события 
     //Sobitie экземпляра btn.
     //Когда в скобках укажете 
     //Metodobrabotchik, нажмите 
     //дважды клавишу Tab
     btn.Sobitie += 
           new Mydelegate(Metodobrabotchik);
     //Развернутая запись строки выше
     //btn.Sobitie =  btn.Sobitie + 
     //     new Mydelegate(Metodobrabotchik);
     //вызываем метод для события
     btn.MetoddlyaSobitiya();
    }
    [STAThread]
    static void Main() 
    {
    Application.Run(new Form1());
    }
    // Создаем метод-обработчик, если среда 
    // сгенерировала его сама – 
    // добавляем строку вывода.
    private static void Metodobrabotchik ()
    {
     Console.WriteLine("Произошло событие");
    }
    }
    }
    }
    // Метод-обработчик для нажатия на кнопку; 
    // когда мы щелкаем по элементу управления 
    // в режиме дизайна, среда генерирует этот
    //метод и курсор оказывается уже здесь
    private void button1_Click(object sender, 
               System.EventArgs e)
    {
    MessageBox.Show("Произошло событие");
    }
    }
    }

    Сравнивая листинги, замечаем, что для Windows-приложения First Form не нужно объявлять делегат, событие, метод для обращения к событию и затем вызывать этот метод. Почему же это тогда работает? Дело в том, что среда .NET содержит огромное количество встроенных событий, доступ к которым осуществляется по их названиям. Более того, среда сама привязывает обработчика для события Click (нажатие на кнопку) и нужный метод, используя встроенный делегат EventHandler:

    this.button1.Click += new System.EventHandler(this.button1_Click);

    Платформа .NET требует точной сигнатуры для любого обработчика событий. button1_Click () и все остальные обработчики событий обязаны выглядеть следующим образом:

    void button1_Click (object sender, EventArgs e)//е также может быть производным от EventArgs
    {
    // код для обработки события
    }

    Обработчики событий не могут возвращать ничего, кроме void. В них отсутствует точка, которая могла бы служить для возврата значения. Обработчики должны принимать два параметра. Первый параметр является ссылкой на объект, который сгенерировал событие. Второй параметр должен быть ссылкой либо на базовый класс .NET System.EventArgs, либо на производный класс. Класс EventArgs представляет собой общий базовый класс для всех уведомлений о произошедших событиях.

    В окне свойств каждого элемента управления на вкладке событий перечислены все доступные события для этого элемента (рис. 1.39).

    (рис 1.39) Вкладка событий элемента button в окне свойств Properties

    Двойной щелчок в поле выбранного свойства перемещает нас в режим дизайна, где уже сгенерированы все объекты для обработки данного события и нам остается только написать код для метода-обработчика. На рис. рис. 1.39 выбрано событие Click, это же событие выбирается по умолчанию при двойном щелчке на элементе управления "кнопка".

    События мыши

    В Интернете часто встречается шуточная программка, представляющая собой диалоговое окно с двумя кнопками. Для ответа на предлагаемый вопрос следует нажать на одну из двух кнопок, причем вторая кнопка при наведении на нее курсора начинает "убегать" от него. Вы можете встретить реализацию этой шутки, написанную на многих языках — от C до Flash-приложений. Сделаем что-то подобное на C#. Создаем новое Windows-приложение и называем его SocOpros. Из окна Toolbox перетаскиваем на форму две кнопки Button и надпись Label. Устанавливаем следующие свойства элементов управления и формы:

    Form1, форма, свойствоЗначение
    FormBorderStyle Fixed3D
    Icon Путь E:\Program Files\Microsoft Visual Studio .NET2003\Common7\Graphics\icons\Computer\W95MBX02.ICO
    Size 344; 176
    Text Социологический опрос
    label1, свойствоЗначение
    Size 12
    Bold true
    Location 32; 28
    Size 272; 32
    Text Вы довольны своей зарплатой?
    Button1, свойствоЗначение
    Name btnyes
    Location 67; 92
    Text Да
    Button2, свойствоЗначение
    Name btnno
    Location 195; 92
    Text Нет

    Щелкаем дважды по кнопке "Да". В обработчике этой кнопки вставляем следующий код:

    private void btnyes_Click(object sender, System.EventArgs e)
    {
    	MessageBox.Show("Мы и не сомневались, что Вы так думаете!");
    }

    Выделяем кнопку "Нет". Открываем окно Properties. Переключаемся в окно событий и дважды щелкаем в поле MouseMove (рис. 1.40).

    (рис 1.40) Событие MouseMove для кнопки btnno Надпись на информационной панели — "Происходит, когда мышь перемещается"

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

    private void btnno_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
    {
    		btnno.Top -= e.Y;
    		btnno.Left += e.X;
    		if (btnno.Top < -10 || btnno.Top > 100)
    		btnno.Top = 60;
    		if (btnno.Left < -80 || btnno.Left > 250)
    		btnno.Left = 120;
    }

    Запустите приложение. Теперь, при выборе "Да" появляется окно с надписью, а при попытке нажать на кнопку "Нет" она "убегает" (рис. 1.41).

    (рис 1.41) Готовое приложение SocOpros

    С событиями мыши связано большинство инструментов во многих программах, а для некоторых, например, графических, — это основа всего взаимодействия с пользователем. Другие события мыши — такие как MouseDown, MouseEnter, MouseUp — могут быть использованы для получения необычной реакции на действия пользователя в этом приложении.

    На диске, прилагаемом к книге, вы найдете приложение SocOpros (Code\Glava1\ SocOpros ).

    Форматирование элементов управления

    Расположение элементов на форме во многом определяет удобство работы с готовым приложением. Пункт главного меню Format содержит опции выравнивания, изменения размера и блокировки элементов управления (рис. 1.42):

    (рис 1.42) Пункт главного меню Format

    При выделении нескольких элементов управления около одного из них появляются темные точки маркера (рис. 1.42). Свойства выбранных элементов будут изменяться относительно этого, главного элемента управления. Для последовательного выделения нескольких элементов удерживаем клавишу Shift, главным элементом будет последний выделенный элемент. Значение пунктов меню Format приводятся в таблице 1.4.

    Пункт меню FormatОписание
    Align Выравнивание выбранных элементов управления
    Make Same Size Установка равного размера
    Horizontal Spacing Пробел между элементами по горизонтали
    Vertical Spacing Пробел между элементами по вертикали
    Center in Form Расположение элементов управления относительно формы
    Order Вертикальный порядок элементов управления
    Lock Controls Блокировка элементов

    При создании дизайна формы всегда следует использовать возможности студии для расположения элементов. При создании приложения SocOpros, например, было использовано расположение элементов управления относительно центра формы (Center in Form).

    Приложение "калькулятор"

    Создайте новый проект (Windows Application) и назовите его Calculator. Устанавливаем следующие свойства формы:

    Form1, форма, свойствоЗначение
    Name Calc
    FormBorderStyle Fixed3D
    Icon Путь E:\Program Files\Microsoft Visual Studio .NET2003\Common7\Graphics\icons\Computer\KEYS01.ICO
    MaximizeBox False
    Size 280;280
    Text Калькулятор 1.0

    Запускаем приложение. Возникает ошибка — "D:\Code\Glava1\Calculator\Form1.cs(75): The type or namespace name 'Form1' could not be found (are you missing a using directive or an assembly reference?)"

    Мы изменили свойство Name, а в коде метода Main, сгенерированном Мастером, осталось старое имя формы. Исправляем на новое:

    [STAThread]
    static void Main() 
    	{
    		Application.Run(new Calc());
    	}

    Теперь все работает. Вернемся в режим дизайна и расположим на форме кнопки калькулятора и TextBox, который будет играть роль дисплея (рис. 1.43):

    (рис 1.43) Расположение элементов управления в режиме дизайна

    Для кнопок устанавливаем следующие свойства:

    ЭлементNameText
    button1 btn1 1
    button2 btn2 2
    button3 btn3 3
    button4 btn4 4
    button5 btn5 5
    button6 btn6 6
    button7 btn7 7
    button8 btn8 8
    button9 btn9 9
    button10 btn10 0
    button11 btnChangesign +/–
    button12 btnpoint ,
    button13 btn_plus +
    button14 btn_minus
    button15 btn_umnojenie *
    button16 btn_delenie /
    button17 btn_sbros C
    button18 btn_ravno =

    Установим свойство Size одной кнопки 40;40, а затем, выделив все остальные кнопки, сделаем их одинакового размера — пункт меню Format/Make Same Size/Both. Затем высоту кнопок btn_sbros и btn_ravno сделаем сдвоенной, как показано на рис. рис. 1.43.

    Для текстового поля устанавливаем следующие свойства:

    textBox1, свойствоЗначение
    Name txtOutput
    Font/Size 12
    Size 250; 26
    Location 10;24
    TextAlign Right

    Дизайн нашего приложения готов. Теперь можно приступать к коду. Состоять он будет из двух классов — public class Calc : System.Windows.Forms.Form —основного, в котором будет реализовываться вся обработка нажатия клавиш, и class mehanizmcalkulyatora, в котором будет находиться вся логика его работы (рис. 1.44):

    (рис 1.44) Для просмотра структуры кода удобно сворачивать классы, щелкая на знаке (+)

    Далее привожу листинг класса mehanizmcalkulyatora с комментариями:

    class mehanizmcalkulyatora
    	{
    	/// <summary>
    	///Создаем перечисление Deystvie для определения одного из четырех действий калькулятора
    	/// </summary>
    		public enum Deystvie:int
    		{
    			NeopredelDeystvie = 0,
    			Slojenie = 1,
    			Vichitanie = 2,
    			Umnojenie = 3,
    			Delenie = 4
    		}
    
    	/// <summary>
    	/// Объявляем и инициализируем переменную, 
    	/// которая будет использоваться для смены знака при нажатии клавиши (+/–)
    	/// </summary>
    
    		private static double peremennayaMinus = -1;
    
    		/// <summary>
    		/// Объявляем переменные для работы калькулятора:
    		/// resultatVichisleniy — переменная для хранения
    		///  промежуточного результата в механизме калькулятора;
    		///  resultatOutput — переменная, значение которой будет сниматься с экрана и  выводиться на него;
    		///  tekusheeDeystvieCalculatora — хранение одного из действий калькулятора;
    		///  pervoeChislo — переменная, в которую будет записываться число на экране
    		///   до нажатия на одну из четырех кнопок с действием;
    		///  vtoroeChislo — второе число на экране;
    		///  dobavlenierazryada — при добавлении следующего  разряда эта переменная примет значение true;
    		///  ChislosTochkoy — при добавлении десятичного разряда (знака точки) эта переменная примет значение true
    		/// </summary>
    	
    		private static double resultatVichisleniy;
    		private static string resultatOutput;
    		private static Deystvie tekusheeDeystvieCalculatora;
    		private static double pervoeChislo;
    		private static double vtoroeChislo;
    		private static bool dobavlenierazryada;
    		private static bool ChislosTochkoy;
     
    		/// <summary>
    		/// В конструкторе класса mehanizmcalkulyatora инициализируем переменные 
    		/// ChislosTochkoy и dobavlenierazryada — при запуске калькулятора на экране 
    		/// нет ни разрядности, ни десятичной части
    		/// </summary>
    
    		public mehanizmcalkulyatora ()
    		{
    			ChislosTochkoy = false;
    			dobavlenierazryada = false;
    		}
    
    			
    		/// <summary>
    		/// В этом методе переменная resultatOutput изменяется — при вводе числа ее значение перезаписывается
    		/// </summary>
    		
    
    		public static string chislonaEkrane (string najatayaKlavisha)
    		{
    			resultatOutput = resultatOutput + najatayaKlavisha;
    			return (resultatOutput);
    		}
    		/// <summary>
    		/// Метод, в котором определяется peremenDeystviya, — одно значение перечисления ///Deystvie,
    		///в зависимости от выбора  клавиши +, –, *  или /
    		/// </summary>
    	
    		public static void DeystvieCalculatora (Deystvie peremenDeystviya)
    		{
    			try
    			{
    				if (resultatOutput != ""  !dobavlenierazryada)
    				{
    					pervoeChislo = System.Convert.ToDouble (resultatOutput);
    					tekusheeDeystvieCalculatora = peremenDeystviya;
    					resultatOutput = "";
    					ChislosTochkoy = false;
    				}			
    			}
    		
    			catch (Exception ex)
    			{
    				MessageBox.Show(ex.ToString());
    			}
    			finally
    			{
    			
    			}
    		}
    
    
    
    	/// <summary>
    	/// При нажатии  кнопки +/– число на экране — tekusheeChislo умножается на –1,
    	///  а затем результат снова присваивается переменной resultatOutput
    	/// </summary>
    	
    
    		public static string ZnakChisla ()
    		{
    			double tekusheeChislo;
    
    			if (resultatOutput != "")
    			{
    				tekusheeChislo = System.Convert.ToDouble (resultatOutput);
    				resultatOutput = System.Convert.ToString(tekusheeChislo * peremennayaMinus);
    			}
    		
    			return (resultatOutput);
    		}
    
    		/// <summary>
    		/// При нажатии кнопки ( , ) переменная resultatOutput приобретает дробную часть
    		/// </summary>
    	
    
    		public static string ZnakTochki ()
    		{
    			if (!ChislosTochkoy  !dobavlenierazryada)
    			{
    				if (resultatOutput != "")
    					resultatOutput = resultatOutput + ",";
    				else
    					resultatOutput = "0,";
    
    				ChislosTochkoy = true;
    			}
    
    			return (resultatOutput);
    		}
    
    	/// <summary>
    	/// При нажатии кнопки ZnakRavno обрабатываются значения 
    	/// переменнных pervoeChislo и vtoroeChislo, результат присваивается переменной 				///resultatVichisleniy, 
    	/// которая  затем преобразуется в resultatOutput
    	/// </summary>
    
    
    		public static string ZnakRavno ()
    		{
    			bool proverkaOshibok = false;
    
    			if (resultatOutput != "")
    			{
    				vtoroeChislo = System.Convert.ToDouble (resultatOutput);
    				dobavlenierazryada = true;
    
    				switch (tekusheeDeystvieCalculatora)
    				{
    					case Deystvie.NeopredelDeystvie:
    						proverkaOshibok = false;
    						break;
    
    					case Deystvie.Slojenie:
    						resultatVichisleniy = pervoeChislo + vtoroeChislo;
    						proverkaOshibok = true;
    						break;
    
    					case Deystvie.Vichitanie:
    						resultatVichisleniy = pervoeChislo – vtoroeChislo;
    						proverkaOshibok = true;
    						break;
    
    					case Deystvie.Umnojenie:
    						resultatVichisleniy = pervoeChislo * vtoroeChislo;
    						proverkaOshibok = true;
    						break;
    
    					case Deystvie.Delenie:
    						resultatVichisleniy = pervoeChislo / vtoroeChislo;
    						proverkaOshibok = true;
    						break;
    
    					default:
    						proverkaOshibok = false;
    						break;
    				}
    
    				if (proverkaOshibok)
    					resultatOutput = System.Convert.ToString (resultatVichisleniy);
    			}
    			
    			return (resultatOutput);
    		}
    
    	/// <summary>
    	/// При нажатии кнопки  С (сброс) значения переменных обнуляются
    	/// </summary>
    
    		public static void Sbros ()
    		{
    			resultatVichisleniy = 0;
    			pervoeChislo = 0;
    			vtoroeChislo = 0;
    			resultatOutput = "";
    			tekusheeDeystvieCalculatora = Deystvie.NeopredelDeystvie;
    			ChislosTochkoy = false;
    			dobavlenierazryada = false;			
    		}
    	}

    Механизм калькулятора готов. Для создания обработчиков кнопок в режиме дизайна дважды щелкаем по каждой кнопке, для быстрого возврата к режиму дизайна используем сочетания клавиш Shift+F7. Теперь в классе public class Calc : System.Windows.Forms.Form добавляем код для работы интерфейса приложенияЛистинг приводится без области Windows Form Designer generated code :

    public class Calc : System.Windows.Forms.Form
    	{
    		private System.Windows.Forms.TextBox txtOutput;
    		private System.Windows.Forms.Button btn_sbros;
    		private System.Windows.Forms.Button btnChangesign;
    		private System.Windows.Forms.Button btnpoint;
    		private System.Windows.Forms.Button btn_ravno;
    		private System.Windows.Forms.Button btn_delenie;
    		private System.Windows.Forms.Button btn_umnojenie;
    		private System.Windows.Forms.Button btn_plus;
    		private System.Windows.Forms.Button btn_minus;
    		private System.Windows.Forms.Button btn0;
    		private System.Windows.Forms.Button btn9;
    		private System.Windows.Forms.Button btn8;
    		private System.Windows.Forms.Button btn7;
    		private System.Windows.Forms.Button btn6;
    		private System.Windows.Forms.Button btn5;
    		private System.Windows.Forms.Button btn4;
    		private System.Windows.Forms.Button btn3;
    		private System.Windows.Forms.Button btn2;
    		private System.Windows.Forms.Button btn1;
    		private System.ComponentModel.Container components = null;
    		/// <summary>
    		/// Объявляем и инициализируем переменные, которые будут
    		///  возникать на экране при нажатии соответствующих кнопок
    		/// </summary>
    		private const string odin = "1";
    		private const string dva = "2";
    		private const string tri = "3";
    		private const string chetire = "4";
    		private const string pyat = "5";
    		private const string shest = "6";
    		private const string sem = "7";
    		private const string vosem = "8";
    		private const string devyat = "9";
    		private const string nul = "0";
    
    		public Calc()
    		{
    			InitializeComponent();
    			// При запуске приложения на экране будет нуль
    			txtOutput.Text = "0";
    		
    		}
    
    		
    		protected override void Dispose( bool disposing )
    		{
    			if( disposing )
    			{
    				if (components != null) 
    				{
    					components.Dispose();
    				}
    			}
    			base.Dispose( disposing );
    		}
    
    		Windows Form Designer generated code
    
    		
    		[STAThread]
    		static void Main() 
    		{
    			Application.Run(new Calc());
    		}
    		/// <summary>
    /// Обработчики для кнопок  обращаются к методу chislonaEkrane класса    ///mehanizmcalkulyatora
    		/// и передают ему одну из постоянных (odin, dva, tri  и т.д.). Результат, 
    ///возвращаемый методом,
    		/// присваивается  свойству Text текстового поля txtOutput
    		/// </summary>
    
    			private void btn1_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (odin);
    			}
    
    			private void btn2_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (dva);
    			}
    
    			private void btn3_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (tri);
    			}
    
    			private void btn4_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (chetire);
    			}
    
    			private void btn5_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (pyat);
    			}
    
    			private void btn6_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (shest);
    			}
    
    			private void btn7_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (sem);
    			}
    
    			private void btn8_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (vosem);
    			}
    
    			private void btn9_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (devyat);
    			}
    
    			private void btn10_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.chislonaEkrane (nul);
    			}
    
    			private void btnChangesign_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.ZnakChisla();
    			}
    
    			private void btnpoint_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.ZnakTochki();
    			}
    			/// <summary>
    			/// Обработчики кнопок действия калькулятора передают 
    			/// методу DeystvieCalculatora класса mehanizmcalkulyatora 
    ///переменную перечисления Deystvie
    			/// </summary>
    		
    			private void btn_delenie_Click(object sender, System.EventArgs e)
    			{
    				mehanizmcalkulyatora.DeystvieCalculatora (mehanizmcalkulyatora.Deystvie.Delenie);
    			}
    
    			private void btn_umnojenie_Click(object sender, System.EventArgs e)
    			{
    				mehanizmcalkulyatora.DeystvieCalculatora (mehanizmcalkulyatora.Deystvie.Umnojenie);
    			}
    
    			private void btn_minus_Click(object sender, System.EventArgs e)
    			{
    				mehanizmcalkulyatora.DeystvieCalculatora (mehanizmcalkulyatora.Deystvie.Vichitanie);
    			}
    
    			private void btn_plus_Click(object sender, System.EventArgs e)
    			{
    				mehanizmcalkulyatora.DeystvieCalculatora (mehanizmcalkulyatora.Deystvie.Slojenie);
    			}
    
    			private void btn_ravno_Click(object sender, System.EventArgs e)
    			{
    				txtOutput.Text = mehanizmcalkulyatora.ZnakRavno();
    				mehanizmcalkulyatora.Sbros();
    			}
    
    			private void btn_sbros_Click(object sender, System.EventArgs e)
    			{
    				mehanizmcalkulyatora.Sbros();
    				txtOutput.Text = "0";
    			}
    	}

    Приложение готово (рис. 1.45).

    (рис 1.45) Приложение Calculator

    На диске, прилагаемом к книге, вы найдете приложение Calculator (Code\Glava1\ Calculator).

    Перемещение по коду, окна Types и Members

    При просмотре даже не очень большого листинга, такого как рассмотренный выше, пользоваться полосой прокрутки не слишком удобно. Среда Visual Studio .NET предоставляет окна Types и Members для просмотра типа объектов и их содержимого. При выборе данного объекта в окне Types мы перемещаемся в коде к началу этого объекта (рис. 1.46) , а в окне Members приводятся члены этого объекта (рис. 1.47):

    (рис 1.47) Окно Types. Перечисление Deystvie(рис 1.46) Окно Members. Перечисление Deystvie

    Если объект состоит из большого количества членов, удобнее выбирать в окне Members нужный член и перемещаться к его коду в листинге. Окна Types и Members, наряду с окном Class View, используются также для анализа листинга.

    XML-документирование кода

    При написании комментариев к коду приложения Calculator я использовал XML-комментарии. Для вставки XML-комментария достаточно трижды поставить знак слэша — ///, среда сама откроет тег, где уже можно вводить текст комментария. Использование такого способа документирования более удобно, чем обычное комментирование, — можно переносить слова на следующую строку без добавления знака комментария. Но самое главное — компилятор транслирует эти комментарии в отдельный XML-файл. Откройте окно Solution Explorer проекта Calculator и щелкните правой кнопкой на его названии. В появившемся контекстном меню выберите пункт Properties. На вкладке Configuration Properties/Build (см. рис. рис. 1.24) в поле XML Documentation File введите название выходного файла — CalcDescription.xml. Скомпилируйте приложение. Запустите проводник Windows (клавиша Windows+E) и перейдите в папку, содержащую проект Calculator. В ней появился созданный нами файл CalcDescription.xml, содержимое которого представляет собой обычный XML- документ:

    <?xml version="1.0" ?> 
    <doc>
    	<assembly>
    		<name>Calculator</name> 
    	</assembly>
    	<members>
    		<member name="F:Calculator.Calc.odin">
    			<summary>Объявляем и инициализируем переменные, 
    которые будут возникать на экране при нажатии соответствующих кнопок.</summary> 
      		</member>
    		<member name="M:Calculator.Calc.Dispose(System.Boolean)">
    			<summary>Clean up any resources being used.</summary> 
    		</member>
    		<member name="M:Calculator.Calc.InitializeComponent">
    		<summary>Required method for Designer support — do not 
    modify the contents of this method with the code editor.</summary> 
    		</member>
    		<member name="M:Calculator.Calc.Main">
    			<summary>The main entry point for the application.</summary> 
    		</member>
    		<member name="M:Calculator.Calc.btn1_Click(System.Object,System.EventArgs)">
    			<summary>Обработчики для кнопок обращаются к методу 
    chislonaEkrane класса mehanizmcalkulyatora и передают ему одну из постоянных (odin, dva, tri и
    т.д.). Результат, возвращаемый методом, присваивается свойству Text текстового поля txtOutput.</summary> 
    		</member>
    		<member name="M:Calculator.Calc.btn_delenie_Click(System.Object,System.EventArgs)">
    			<summary>Обработчики кнопок действия калькулятора
     передают методу DeystvieCalculatora класса mehanizmcalkulyatora переменную перечисления Deystvie.</summary> 
    		</member>
    		<member name="F:Calculator.mehanizmcalkulyatora.peremennayaMinus">
    			<summary>Объявляем и инициализируем переменную, 
    которая будет использоваться для смены знака при нажатии клавиши (+/–)</summary> 
    		</member>
    		<member name="F:Calculator.mehanizmcalkulyatora.resultatVichisleniy">
    			<summary>Объвляем переменные для работы калькулятора: 
    resultatVichisleniy — переменная для хранения промежуточного результата в механизме 
    калькулятора; resultatOutput — переменная, значение которой будет сниматься с экрана и 
    выводиться на него; tekusheeDeystvieCalculatora — хранение одного из действий калькулятора; 
    pervoeChislo — переменная, в которую будет записываться число на экране до нажатия на одну 
    из четырех кнопок с действием; vtoroeChislo — второе число на экране; dobavlenierazryada — при 
    добавлении следующего разряда эта переменная примет значение true; ChislosTochkoy — при 
    добавлении десятичного разряда (знака точки) эта переменная примет значение true</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.#ctor">
    			<summary>В конструкторе класса mehanizmcalkulyatora 
    инициализируем переменные ChislosTochkoy и dobavlenierazryada — при запуске калькулятора на 
    экране нет ни разрядности, ни десятичной части.</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.chislonaEkrane(System.String)">
    			<summary>В этом методе переменная resultatOutput 
    изменяется — при вводе числа ее значение перезаписывается.</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.DeystvieCalculatora
    (Calculator.mehanizmcalkulyatora.Deystvie)">
    			<summary>Метод, в котором определяется 
    peremenDeystviya, — одно значение перечисления Deystvie, в зависимости от выбора клавиши +, –
    , *, или /</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.ZnakChisla">
    <summary>При нажатии кнопки +/–- число на экране — tekusheeChislo умножается на –1, а 
    затем результат снова присваивается переменной resultatOutput.</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.ZnakTochki">
    			<summary>При нажатии кнопки ( , ) переменная 
    resultatOutput приобретает дробную часть.</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.ZnakRavno">
    			<summary>При нажатии кнопки ZnakRavno обрабатываются 
    значения переменнных pervoeChislo и vtoroeChislo, результат присваивается переменной 
    resultatVichisleniy, которая затем преобразуется в resultatOutput.</summary> 
    		</member>
    		<member name="M:Calculator.mehanizmcalkulyatora.Sbros">
    			<summary>При нажатии кнопки С (сброс) значения 
    переменных обнуляются.</summary> 
    		</member>
    		<member name="T:Calculator.mehanizmcalkulyatora.Deystvie">
    			<summary>Создаем перечисление Deystvie для определения 
    одного из четырех действий калькулятора.</summary> 
    		</member>
    	</members>
    </doc>

    Файл получается довольно длинным, и читать его в таком формате неудобно, хотя и возможно. Однако XML-код можно преобразовать в HTML, написав XSLT-файл. Кроме того, XML-документ можно сохранить в специальной базе данных для документации.

    Документирующие комментарии можно преобразовать в чрезвычайно удобную форму, сгенерировав в среде Visual Studio .NET отчет Code Comment Web Report. Выберите пункт меню Tools/Build Comment Web Pages. Результатом будет набор HTML-файлов, которые можно просмотреть непосредственно в Visual Studio .NET или с помощью браузера, перейдя в сформированную папку CodeCommentReport (рис. 1.48):

    (рис 1.48) Страница отчета

    Для увеличения функциональности создаваемого отчета при XML-документировании кода можно использовать стандартные теги. Подробное обсуждение этих вопросов выходит за рамки этой книги.

    Полный список тегов можно найти в документации, входящей в комплект Visual Studio .NET.

    Создание иконки для приложения

    Когда мы создаем новую форму, в заголовке располагается стандартная иконка —. Наверняка ее вид не может устраивать нас для готового приложения. Можно, конечно, воспользоваться одним из файлов .ico, находящихся на нашем компьютере, или даже иконкой из коллекции, входящей в комплект поставки Microsoft Visual Studio .NET — мы это уже делали, но лучше нарисовать уникальную иконку во встроенном редакторе иконок. Итак, File/New/File (Ctrl+N) и выбираем IconFile:

    (рис 1.49)

    Запускается редактор изображений Microsoft Visual Studio.NET, внешний вид которого имеет много общего с прочими графическими редакторами:

    (рис 1.50)

    Можно определить размер рисунка и его цветовую схему — Image/New Image Type (Ins):

    (рис 1.51)

    Оптимальным типом изображения для иконки нашего приложения будет 32x32, 256 colors.

    После завершения работы над иконкой ее можно сохранить и затем выбрать ее в свойстве Icon формы.

    Аналогично можно создавать изображения курсоров — Ctrl+N и далее Cursor File.

    Вернуться к учебному плану