Элементы управления. Кнопки и надписи
Лабораторная работа 5-6
Тема: Программирование для Windows. Приложения Windows Forms. Графика.
Теоретические сведения
- Делегаты.
- События.
- Windows Forms.
- Элементы управления. Кнопки и надписи.
- Практический пример. Отображение координат мыши при ее перемещении.
- Прелюдия к практическому примеру.
- Практический пример. Игра "Пятнашки".
Делегаты.
Делегат, какое знакомое слово .... Вспоминается СССР: “Делегаты съезда ….” В C# к счастью под понятием делегата не маскируется тот человек, которого послал народ на очередной пленум. Итак, что же это? Делегат – это так называемый “безопасный указатель на функцию”. Однако, в отличие от обычных указателей на функцию в С++, делегаты C# могут вызывать более одной функции (при совместном комбинировании двух делегатов результатом будет делегат, который вызывает их обоих). В чем состоит “безопасность” ? В С++ указатель на функцию – это фактически просто адрес, делегат же позволяет проверять количество передаваемых параметров, возвращаемое значение и т.д..Как вы уже привыкли многие конструкции C# - это классы, так и делегат - это класс отнаследованный от базового класса System.MulticastDelegate.Делегат можно объявлять как в классе, так и просто в пространстве имен.Рассмотрим синтаксис объявления делегата:
спецификатор_доступа delegate тип_возвр_значения_для_метода Имя_Делегата(список_параметров_метода)
Небольшие комментарии к приведенному выше: 1)спецификатор_доступа - например, public и т.д. 2)delegate говорит о том что, объявляется делегат 3)тип_возвр_значения_для_метода, список_параметров_метода - определяет, на что может указывать делегат. Например:
public delegate void SetFire()
Читается это так – SetFire – это делегат на метод возвращаюший void, и не принимающий параметров.На первый взгляд всё прозрачно, однако что же мы сделали на самом деле? SetFire – это класс, который автоматически отнаследовался от System.MulticastDelegate и получил поля и методы базового класса , т.е. вот что было на самом деле:
public class SetFire:System.MulticastDelegate
Делегат может указывать как на обычный метод так и на статический.Настало волшебное время - мы применим полученную о делегатах информацию во благо обществу, т.е. рассмотрим практический пример, с использованием Делегатов:
using System;
using System.Collections;
class Human{
// Делегат на функцию, которая ничего не возвращает и принимает объект типа h
public delegate void HumanDelegate(Human h);
// Перечисление
public enum Sex{Male,Female};
private Sex p;
private string name;
private string surname;
private int age;
// конструктор
public Human(){
name = surname = "Нет Данных";
age = 0;
p = Sex.Male;
}
// конструктор с параметрами
public Human(string name,string surname,int age,Sex p){
this.name = name;
this.surname = surname;
this.age = age;
this.p = p;
}
// Задание имени, возврат его
public string Name{
get{
return name;
}
set{
name = value;
}
}
// Задание фамилии, возврат её
public string Surname{
get{
return name;
}
set{
name = value;
}
}
// Задание возраста, возврат его
public int Age{
get{
return age;
}
set{
age = value;
}
}
// Задание пола,возврат его
public Sex RealSex{
get{
return p;
}
set{
p = value;
}
}
}
//Класс, содержащий людей
class Firm{
ArrayList people = new ArrayList();
public Firm(){
// Добавляем в список 3 - х людей
people.Add(new Human());
people.Add(new Human("Вася","Иванов",80,Human.Sex.Male));
people.Add(new Human("Катерина","Маркова",25,Human.Sex.Female));
}
// Метод, принимающий делегат, четко указывается название класса,
//где содержится делегат, а также название делегата
public void AnalyzePeople(Human.HumanDelegate ptr){
Console.WriteLine("Будем выполнять действия над человеком !!!");
// Вызываются методы, на которые указывает делегат
foreach(Human obj in people)
ptr(obj);
}
}
class Sample
{
// Проверка пола
static void AnalyzeSex(Human h){
if(h.RealSex==Human.Sex.Male){
Console.WriteLine("Мужчина");
}
else{
Console.WriteLine("Женщина");
}
}
// Проверка по возрасту
static void AnalyzeAge(Human h){
if(h.Age>65){
Console.WriteLine("Больше 65 лет");
}
else{
Console.WriteLine("Меньше или равно 65 лет");
}
}
static void Main()
{
Console.WriteLine("Пример работы Делегата");
Firm firm = new Firm();
// В этой строке происходит использование делегатов. Создаётся объект // делегата используя
// ключевое слово new.
// Сейчас делегат указывает на метод AnalyzeSex
firm.AnalyzePeople(new Human.HumanDelegate(AnalyzeSex));
// Сейчас делегат указывает на метод AnalyzeAge
firm.AnalyzePeople(new Human.HumanDelegate(AnalyzeAge));
Console.Read();
}
}
Вывод:
Пример работы ДелегатаБудем выполнять действия над человеком !!!МужчинаМужчинаЖенщинаБудем выполнять действия над человеком !!!Меньше или равно 65 летБольше 65 летМеньше или равно 65 летОбратите внимание на то, что у нас сейчас делегат, вложенный в класс и поэтому для доступа к нему мы используем имя класса:
firm.AnalyzePeople(new Human.HumanDelegate(AnalyzeAge));Как уже было сказано выше делегат это класс отнаследованный от System.MulticastDelegate. Рассмотрим 2 полезных метода этого базового класса. Первый метод Combine – этот статический метод используется для создания делегата, указывающего на несколько разных функций (также можно пользоваться перегруженным оператором +), второй Remove - этот статический метод удаляет делегат из списка указателей на функции. Если делегат указывает на несколько методов, то тогда при вызове делегата будут вызываться методы,на которые указывает делегат. Например(те же самые классы другой Main):
static void Main(){ Console.WriteLine("Пример работы Делегата"); Firm firm = new Firm(); // Создание делегатов Human.HumanDelegate sex = new Human.HumanDelegate(AnalyzeSex); Human.HumanDelegate age = new Human.HumanDelegate(AnalyzeAge); // Многоадресный Делегат (формируем его через +) // Произойдет вызов методов AnalyzeSex и AnalyzeAge firm.AnalyzePeople(sex+age); Console.WriteLine("\n\n"); // Многоадресный Делегат (формируем его через +) // Произойдет вызов методов AnalyzeSex и AnalyzeAge firm.AnalyzePeople(age+sex); // Или так тоже Многоадресный Делегат Console.WriteLine("\n\n"); // Многоадресный Делегат (формируем его через Combine) // Произойдет вызов методов AnalyzeSex и AnalyzeAge firm.AnalyzePeople((Human.HumanDelegate)Delegate.Combine(sex,age)); // Или так тоже можно MulticastDelegate del = age+sex; firm.AnalyzePeople((Human.HumanDelegate)del); // Удаляем один делегат Delegate onlysex = MulticastDelegate.Remove(del,age); Console.WriteLine("\n\n************************************\n\n"); // Уже не многоадресный делегат firm.AnalyzePeople((Human.HumanDelegate)onlysex); Console.Read();}Сейчас мы через делегат вызывали статические методы класса, но это можно делать и с обычными. Для этого надо чуть-чуть видоизменить создание делегата. Например:
Firm firm = new Firm();// Test - это какой-то класс в нем есть методы AnalyzeSex,// AnalyzeAge Test test = new Test();//firm.AnalyzePeople(new Human.HumanDelegate(test.AnalyzeSex));firm.AnalyzePeople(new Human.HumanDelegate(test.AnalyzeAge));События.
Понятие события вам уже известно из курса программирования под Windows. В контексте C# событие - это способ, с помощью которого один класс оповещает другой (другие) класс о чем-то произошедшем. Иногда говоря, что механизм событий использует идеологию “публикация/подписка”. Какой-то класс публикует свои события, а другие классы подписываются на те события, которые им интересны. Кроме этой модели события, безусловно, используются при программировании на C# с использованием Windows Forms. Для работы с событием нужно выполнить следующие действия:1) Создать делегат, который будет использоваться для вызова нужного метода при срабатывании события 2) Определить само событие при помощи ключевого слова event. Слова словами давайте рассмотрим пример на события:
using System;using System.Collections;class Human{ // Делегат для реакции на событие public delegate void HumanHandler(string info); // События, которые могут возникать public static event HumanHandler Run; public static event HumanHandler Stop; // Методы из которых могут генерироваться события public void MayBeStart(){ Random rand = new Random(); if(rand.Next(0,2)==0) Run("Беги, Лола, Беги");// Генерируем событие и передаём ему параметры!!! } public void MayBeStop(){ Random rand = new Random(); if(rand.Next(0,2)==0) Stop("Стоп,Машина");// Генерируем событие и передаём ему параметры!!! } public enum Sex{Male,Female}; private Sex p; private string name; private string surname; private int age; public Human(){ name = surname = "Нет Данных"; age = 0; p = Sex.Male; } public Human(string name,string surname,int age,Sex p){ this.name = name; this.surname = surname; this.age = age; this.p = p; } public string Name{ get{ return name; } set{ name = value; } } public string Surname{ get{ return name; } set{ name = value; } } public int Age{ get{ return age; } set{ age = value; } } public Sex RealSex{ get{ return p; } set{ p = value; } }}// Класс с Main, и обработчикамиclass Sample{ // Обработчики событий // Обработчик Run public static void OnRun(string msg){ Console.WriteLine("В OnRun:"+msg); } // Обработчик Stop public static void OnStop(string msg){ Console.WriteLine("В OnStop:"+msg); } // Обработчик Run public static void OnRun2(string msg){ Console.WriteLine("В OnRun2:"+msg); } public static void Main(){ Human obj = new Human(); // Добавление к событиям обработчиков используя делегаты Human.Run += new Human.HumanHandler(OnRun); Human.Stop += new Human.HumanHandler(OnStop); Console.WriteLine("\n*****************************************\n"); for(int i = 0;i<5;i++) obj.MayBeStart(); Console.WriteLine("\n*****************************************\n"); for(int i = 0;i<5;i++) obj.MayBeStop(); // Добавляем ещё один обработчик для Run Human.Run += new Human.HumanHandler(OnRun2); Console.WriteLine("\n*****************************************\n"); for(int i = 0;i<5;i++) obj.MayBeStart(); // Удаляем обработчик события Run под названием OnRun2 Human.Run -= new Human.HumanHandler(OnRun2); Console.Read(); }}Обратите внимание, как переплетаются между собой события и делегаты в нашем примере. Например, мы указываем какие методы будут, в ответ, на приход события вызываться при помощи делегата (то есть, указываем названия обработчиков). В терминологии С# указание обработчика для события – это подключение приёмника для прослушивания события. Вот так гордо называются строки приведенные ниже:
// Добавление к событиям обработчиков используя делегатыHuman.Run += new Human.HumanHandler(OnRun);Human.Stop += new Human.HumanHandler(OnStop);Отсоединение обработчика называется отключением от прослушивания. Например, этот процесс указывается ниже ещё раз:
// Удаляем обработчик события Run под названием OnRun2Human.Run -= new Human.HumanHandler(OnRun2);Обработчики событий можно также поместить в отдельный класс. Например:
using System;using System.Collections;class Human{ // Делегат public delegate void HumanHandler(string info); // События public static event HumanHandler Run; public static event HumanHandler Stop; // Генерация событий public void MayBeStart(){ Random rand = new Random(); if(rand.Next(0,2)==0) Run("Беги, Лола, Беги"); } public void MayBeStop(){ Random rand = new Random(); if(rand.Next(0,2)==0) Stop("Стоп,Машина"); } public enum Sex{Male,Female}; private Sex p; private string name; private string surname; private int age; public Human(){ name = surname = "Нет Данных"; age = 0; p = Sex.Male; } public Human(string name,string surname,int age,Sex p){ this.name = name; this.surname = surname; this.age = age; this.p = p; } public string Name{ get{ return name; } set{ name = value; } } public string Surname{ get{ return name; } set{ name = value; } } public int Age{ get{ return age; } set{ age = value; } } public Sex RealSex{ get{ return p; } set{ p = value; } }}//=======================================================// Класс для обработчиков событий//=======================================================class HumanEventSink{ // Обработчики событий public void OnRun(string msg){ Console.WriteLine("В HumanEventSink OnRun:"+msg); } public void OnStop(string msg){ Console.WriteLine("В HumanEventSink OnStop:"+msg); } public void OnRun2(string msg){ Console.WriteLine("В HumanEventSink OnRun2:"+msg); } }//=======================================================class Sample{ public static void Main(){ HumanEventSink envs = new HumanEventSink(); Human obj = new Human(); //======================================================= Human.Run += new Human.HumanHandler(envs.OnRun); Human.Stop += new Human.HumanHandler(envs.OnStop); //======================================================= Console.WriteLine("\n*****************************************\n"); for(int i = 0;i<5;i++) obj.MayBeStart(); Console.WriteLine("\n*****************************************\n"); for(int i = 0;i<5;i++) obj.MayBeStop(); //======================================================= Human.Run += new Human.HumanHandler(envs.OnRun2); Console.WriteLine("\n*****************************************\n"); for(int i = 0;i<5;i++) obj.MayBeStart(); //======================================================= Human.Run -= new Human.HumanHandler(envs.OnRun2); Console.Read(); }}Windows Forms.
Итак, мы подошли к волнующему и ответственному моменту - программированию Windows приложений. Перед тем как написать хотя бы одну строку кода определимся, как мы будем создавать Windows приложение. Тип проекта у нас останется Empty Project.После создания пустого каркаса зайдите в опции проекта (Project->имя_проекта properties).Должна быть выбрана вкладкаGeneral. В пункте Output Type надо поставить Windows Application(это означает, что приложение является Windows – приложением).Это и демонстрируется на рисунке.
Также нужно подключить 2 библиотеки динамической линковки(System.dll,System.Windows.Forms.dll). Для этого нужно выбрать Project->Add Refernce. И через кнопку select при активной закладки .NET выбрать указанные dll. Это показывается на рисунке ниже:
Выполнив всё это вы готовы к написанию программы. Теперь пришло время подковаться теоретически для взятия новых высот.
<тема>Пространство имен System.Windows.Forms
Это пространство предназначено для создания Windows - приложений с графическим интерфейсом. В этом пространстве есть много нужных и полезных классов.Первая программа:
using System.Windows.Forms;
using System;
class Sample
{
public static void Main()
{
// Создание формы
Form frm = new Form();
// Задание заголовка
frm.Text = "Tell me a secret";
Application.Run(frm);
}
}
Данная программа отображает на экран форму ( аналог диалога в терминологии Visual C++). Данная программа отображает на экран форму (аналог диалога в терминологии Visual C++).Form – это класс, который находится в пространстве System.Windows.Forms отвечает за создание форм. Вот его иерархия наследования:
Object
MarshalByRefObject
Component
Control
ScrollableControl
ContainerControl
Form
Естественно у этого класса есть свойства и методы, например в нашем примере с помощью свойства Text мы изменили заголовок окна. Application – это класс, который отвечает за запуск, остановку приложения и т.п.. Метод Run() – это статический метод класса Application.Он запускает стандартный цикл работы с сообщениями для текущего потока. Форма, чья ссылка была передана в метод Run становится видимой и главной формой в приложении. Вообще в приложении может быть много форм, но главная будет только одна и когда она закрывается, все подчиненные формы закрываются тоже. Примеры работы с Windows.Forms смотрите в следующих разделах урока.
Элементы управления. Кнопки и надписи.
Итак, мы снова встречаемся с элементами управления. На этот раз вас ждет много приятных сюрпризов, связанных с огромным количеством визуальных свойств, присущих элементам управления Windows Forms. Начнем с того, что практически все элементы управления являются наследниками класса Contro, который является базовым для компонент с визуальным отображением.
Наследники класса Control:
System.Object System.MarshalByRefObject System.ComponentModel.Component System.Windows.Forms.Control System.ComponentModel.Design.ByteViewer System.Windows.Forms.AxHost System.Windows.Forms.ButtonBase System.Windows.Forms.DataGrid System.Windows.Forms.DateTimePicker System.Windows.Forms.GroupBox System.Windows.Forms.Label System.Windows.Forms.ListControl System.Windows.Forms.ListView System.Windows.Forms.MonthCalendar System.Windows.Forms.PictureBox System.Windows.Forms.PrintPreviewControl System.Windows.Forms.ProgressBar System.Windows.Forms.ScrollableControl System.Windows.Forms.ScrollBar System.Windows.Forms.Splitter System.Windows.Forms.StatusBar System.Windows.Forms.TabControl System.Windows.Forms.TextBoxBase System.Windows.Forms.ToolBar System.Windows.Forms.TrackBar System.Windows.Forms.TreeViewВ этом разделе мы представим несколько общих свойств и методов, присущих всем элементам управления, а также свойства и методы, которые специфичны кнопкам и надписям.