RUS_-_Karejev_-_Razrabotka_Windows-prilozhenij
.pdf
увеличить изображение
Рис. 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 1) редакторов 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 Metod2)( 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");
}
}
}
Листинг 1.1. Полный листинг проекта Delegate
Создайте новое консольное приложение. Назовите его 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.2.
В этом примере мы дважды передаем переменную одному экземпляру делегата (рис. 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.3.
Результатом выполнения этой программы будет вычисление квадрата, куба и четвертой степени значения переменной (рис. 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.4.
Результатом выполнения этой программы будет последовательный вывод самого числа, его квадрата, куба и четвертой степени
(рис. 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 добавляем метод calculatecvadrat 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);
}
}
}
Листинг 1.5.
В языке С# вы уже сталкивались с сокращенной записью операторов, так, выражение 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(Metoddlyadelegata); //вызываем метод для события
btn.MetoddlyaSobitiya();
}
// Создаем метод-обработчик, если среда сгенерировала его сама — добавляем //строку вывода private static void Metodobrabotchik ()
{
Console.WriteLine("Произошло событие");
}
}
}
}
Листинг 1.6.
Сначала мы в строке
delegate void Mydelegate();
объявляем делегат. Параметров у него нет (хотя могут и быть), тип — void.
Затем мы объявляем класс Button, внутри которого объявляем событие Sobitie (имя произвольное):
