Событие Paint и его обработчик
Если прикладное окно программы примера 9.6.1 свернуть, а потом развернуть или же изменить его размер, то представленная в нём информация исчезнет. Дело в том, что операционная система Windows не заботится о восстановлении содержимого окна при его разрушении и возлагает его восстановление на само Windows-приложение, то есть на программиста.
При всяком разрушении окна операционная системаWindows посылает приложению событие Paint, и приложение реагирует на него предопределённой функцией-обработчиком OnPaint() в языках C# и C++/CLI или функцией-обработчиком paint() в языке Java(J#), пустых по умолчанию, то есть ничего не отображающих в окне. Переопределив функцию OnPaint() или paint(), приложение будет рисовать в области клиента окна что-то в соответствии с её реализацией. В описание переопределённой функции на языке C# и C++/CLI надлежит поместить ключевое слово override. Функции Paint() и paint() относятся к виртуальным функциям, о которых подробно рассказано в разделе 13.1.
Перерисовку окна может осуществить и программист, вызвав специальную функцию Invalidate() в языках C# и C++/CLI или функцию repaint() в языке Java(J#). Вызов этих функций генерирует событие Paint.
Итак, чтобы заставить перерисовываться область клиента окна, необходимо только переопределить функцию перерисовки, имеющую формат:
C++/CLI.
protected: virtual void OnPaint (PaintEventArgs ^e) override;
C#.
protected override void OnPaint (PaintEventArgs e);
Функция реагирует на событие, определённое как
C++/CLI.
public: event PaintEventHandler ^Paint;
C#.
public event PaintEventHandler Paint;
Событие Paint использует делегат:
C++/CLI.
public: delegate void PaintEventHandler (Object ^sender, PaintEventArgs ^e);
C#.
public delegate void PaintEventHandler (object sender, PaintEventArgs e);
Для языка Java(J#) функция перерисовки имеет формат:
Java(J#).
public void paint (Graphics g);
[C++]
Пример 9.7.1 иллюстрирует использование событий, возникающих при нажатии на клавишу мыши и при перерисовке области клиента прикладного окна.
Пример 9.7.1. Программа рисования ломаной линии.
Приложение создаёт главное окно и ожидает нажатия левой клавиши мыши. При нажатии на клавишу рисуется линия из текущей точки в точку носика указателя мыши. После этого текущая точка переносится в точку носика указателя мыши, и приложение снова ждёт нажатия левой клавиши мыши. Прикладное окно с нарисованной ломаной линией изображено на рис. 10.7.1.
Рис.9.7.1. Прикладное окно программы рисования ломаной линии
Ниже приводится программа:
///////////////
// C#
using System;
using System.Drawing;
using System.Windows.Forms;
class CLine : Form // Класс прикладного окна
{
Point [ ] points; // Массив точек носика курсора мыши
int len; // Текущая длина массива points
public CLine( ) // Конструктор
{
// Привязать к событию MouseDown функцию OurMouseDown
MouseDown += new MouseEventHandler (OurMouseDown);
Text= "Line"; // Заголовок прикладного окна
points= new Point [20]; // Создать массив points в куче
len= 0; // Инициализировать len
}
// Перерисовать область клиента окна
protected override void OnPaint (PaintEventArgs arg)
{
base.OnPaint (arg);
for (int i=0; i < len-1; i++) // Перебрать массив точек
arg.Graphics.DrawLine // Нарисовать отрезок линии
(new Pen (Color.Blue), points[i], points[i+1]);
}
// Обработать событие MouseDown мыши
void OurMouseDown (object sender, MouseEventArgs arg)
{
// Реагировать только на левую клавишу мыши
if (arg.Button == MouseButtons.Right) return;
if (len < 20) // Если есть место в массиве points, то
{ // поместить в массив точку
points[len].X= arg.X; // Координата X носика курсора мыши
points[len].Y= arg.Y; // Координата Y носика курсора мыши
len++;
}
Invalidate ( ); // Перерисовать
}
static void Main ( ) // Главная функция
{
Application.Run (new CLine ( )); // Выполнить приложение
}
}
///////////////
// C++/CLI
#include "stdafx.h"
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;
ref class CLine : Form // Класс прикладного окна
{
array<Point^> ^points; // Массив точек носика курсора мыши
int len; // Текущая длина массива points
public:
CLine( ) // Конструктор
{
// Привязать к событию MouseDown функцию OurMouseDown
MouseDown += gcnew MouseEventHandler (this,
&CLine::OurMouseDown);
Text= "Line"; // Заголовок прикладного окна
points= gcnew array<Point^> (20); // Создать массив points в куче
len= 0; // Инициализировать len
}
protected:
// Перерисовать область клиента окна
virtual void OnPaint (PaintEventArgs ^arg) override
{
Form::OnPaint (arg);
for (int i=0; i < len-1; i++) // Перебрать массив точек
arg->Graphics->DrawLine // Нарисовать отрезок линии
(gcnew Pen (Color::Blue), *points[i], *points[i+1]);
}
// Обработать событие MouseDown мыши
void OurMouseDown (Object ^sender, MouseEventArgs ^arg)
{
if (len < 20) // Если есть место в массиве points, то
{
points[len]=gcnew Point (arg->X, arg->Y); // поместить в массив
len++;
}
Invalidate ( ); // Перерисовать
}
};
void main ( )
{
Application::Run (gcnew CLine ( ));
}
///////////////
// Javaи J#
import java.awt.*;
import java.awt.event.*;
class CLine extends Frame // Класс прикладного окна
{
Point [ ] points; // Массив точек носика курсора мыши
int len; // Текущая длина массива points
public CLine( ) // Конструктор
{
setTitle ("Line"); // Установить заголовок прикладного окна
setSize (400, 200); // Установить размер окна
//this.setBackground (Color.lightGray);
points= new Point [20]; // Создать массив points точек в куче
len= 0; // Инициализировать len
// Реализовать обработчик события мыши, применив
// внутренний анонимный класс
this.addMouseListener (new MouseAdapter ( )
{
public void mousePressed (MouseEvent mE)
{
// Реагировать только на левую клавишу мыши
if((mE.getModifiers()&Event.META_MASK)>0) return;
if (len < 20) // Если есть место в массиве points
{
points[len]= mE.getPoint (); // Координаты
// носика курсора мыши len++;
}
repaint ( );
}
});
this.addWindowListener (new OurWindowAdapter());
}
// Перерисовать область клиента окна
public void paint (Graphics g)
{
g.setColor (Color.blue);
for (int i=0; i < points.length-1; i++) // Перебрать массив точек
{
g.drawLine( // Нарисовать отрезок линии
points[i].x, points[i].y ,points[i+1].x, points[i+1].y);
}
}
public static void main ( ) // Главная функция
{
CLine line= new CLine ( );
line.show ( );
}
}
// Класс адаптера окна
class OurWindowAdapter extends WindowAdapter
{
public void windowClosing (WindowEvent wE)
{
System.exit (0);
}
}
C#. Класс CLine прикладного окна, порождённый из базового класса Form, содержит закрытый массив точек points, соответствующих координатам носика мыши нажатых клавиш, и текущую длину len этого массива.
При создании объекта-окна конструктор инициализирует данные класса CLine, создав массив points из 20 точек в куче и присвоив его текущей длине len значение 0. Кроме этого конструктор подписывает обработчик OurMouseDown() на событие MouseDown клавиши мыши и посредством свойства Text формы определяет заголовок “Line” прикладного окна.
Рассмотрим работу приложения. После запуска приложения появилось прикладное окно. При создании окна конструктор класса Cline, создав массив из 20 точек, присвоит длине массива len начальное значение и подпишет обработчик OurMouseDown() на событие MouseDown. При появлении окна сгенерируется событие Paint и, как следствие, выполнится наш переопределённый обработчик OnPaint(), который в виду нулевого значения переменной len не рисует ломаную линию в области клиента. Таким образом, при появлении прикладного окна его область клиента пуста. Приложение ждёт события MouseDown от нажатия левой клавиши мыши и события от закрытия прикладного окна. При нажатии левой клавиши мыши выполнится обработчик OurMouseDown(). Обработчик OurMouseDown(), воспользовавшись параметром arg типа MouseEventArgs, извлекает значения координат носика курсора нажатой клавиши мыши и присваивает их текущему элементу массива points точек. Затем продвигается индекс len массива. Если индекс len выходит за границы массива, то пополнения массива новыми точками не будет. Перед завершением обработчик OurMouseDown() вызывает функцию Invalidate(), которая генерирует событие Paint, обеспечивая тем самым выполнение функции OnPaint(). Ломаная линия перерисовывается в окне.
Функция OnPaint() использует графический объект класса Graphics, извлекая его из структуры PaintEventArgs с помощью свойства Graphics и обратившись к функции DrawLine() рисования линии. В качестве аргумента функция DrawLine() использует объект-перо синего цвета.
Ломаная линия рисуется циклически, при этом в каждом цикле рисуется только один отрезок линии синего цвета.
C++/CLI. Программа на языке C++/CLI схожа с программой на языке C#, поскольку применяются те же классы библиотеки .NET Framework. Обращает на себя внимание иное описание и использование массива точек. В управляемом классе CLine применён управляемый массив points, который описан с применением слова array и является массивом дескрипторов на объекты точек, размещенных в управляемой куче. При нажатии на клавишу мыши обработчик присваивает очередному дескриптору массива ссылку на созданный в куче объект точки, содержащей координаты носика мыши. А функция paint() перерисовки использует через дескриптор эти объекты при рисовании ломаной линии.
Обратите внимание, что заголовок функции OnPaint() языка C++/CLI отличается от заголовка функции OnPaint() языка C#: используется ключевые слово virtual, а слово override стоит в конце заголовка.
Java. Так же, как и в программе на C#, программа на языке Java использует графический класс Graphics в предопределённой функции перерисовки, которая называется paint(). Функция рисования линии drawLine() не содержит объект пера, как в соответствующей функции DrawLine() библиотеки .NET Framework. Цвет пера устанавливается с помощью свойства setColor(). Отлично от программы языка C# реализуется и обработка события мыши, в соответствии с одним из вариантов, изложенных в разделе 10.5. Вместо функции Invalidate() языка C# применена функция repaint(). Эти функции инициируют появление события Paint. Использованию различных классов позволило их импортирование с помощью
import java.awt.*;
import java.awt.event.*;
Управляющие элементы
C# и C++/CLI.Управляющие элементы составляютоснову при формировании пользовательского интерфейса приложения. Библиотека NET. Framework и пакеты языка Java включают десятки классов, используемых для создания объектов управления, а из них - элементов управления (controls). Элементы управления, такие как нажимаемые или селективные кнопки, панели списков, панели редактирования и другие, широко используются разработчиками оконных приложений. Они значительно облегчают интерфейс между пользователем и приложением.
В библиотеке NET. Framework все классы управляющих элементов порождаются из общего базового класса Control и поэтому позволяют однообразное их использование. Имеется специальный класс ControlCollection, обеспечивающий объединение управляющих элементов в одной коллекции.
Для связи управляющих элементов с конкретной коллекцией (т.е. объектом класса ControlCollection) используется свойство Controls управляющих элементов, наследуемое из класса Control. Получив посредством свойства Controls ссылку на коллекцию, можно воспользоваться его функциями (функциями класса ControlCollection), чтобы конкретный управляющий элемент добавить (Add) или удалить (Remove) из коллекции, или удалить все элементы (Clear) из коллекции, или же выявить (Contains) наличие указанного элемента в коллекции.
Из множества классов управляющих элементов библиотеки NET. Framework мы рассмотрим классы Button и TextBox.
Из класса Button создаётся так называемая нажимаемая кнопка.
Из класса TextBox создаётся панель редактирования, которая представляет собой простейший редактор с возможностью ввода и редактирования текста.
Java. В языке Java классы управляющих интерфейсных элементов порождаются из класса Component, содержащего их общие свойства и функции. Кнопка и панель редактирования создаются из классов Button и TextField. Эти и другие управляющие элементы добавляются в контейнер окна посредством функции add().
Рис.9.8.1 и 9.8.2 иллюстрируют наследование базовых классов классами Button, TextBox и TextField.
Рис. 9.8.1. Наследование базовых классов классами TextBox и Button в C# и C++/CLI
Рис. 9.8.2. Наследование базовых классов классами TextField и Button в Java
Позднее будут рассмотрены ещё классы UserControl и Panel, используемые для получения интересного интерфейсного элемента, представляющего в прикладном окне полотно-окно с богатой функциональностью.
Класс Button
C# и C++/CLI.Объект класса Button представляет нажимаемую кнопку. Объект создаётся конструктором:
Button ( );
В классе Control библиотеки .NET Framework среди более 50 событий объявлено событие Click:
C#.
public event EventHandler Click;
C++.NET.
public: event EventHandler^ Click;
На это событие мы будем подписывать обработчик, реагирующий на нажатие кнопки.
При применении класса Button, кроме его собственных свойств, можно воспользоваться наследуемыми свойствами класса Control. Наследуемое свойство Text , например, позволяет поместить текст в кнопку, а свойства Size и Location - определить размеры кнопки и координаты её верхнего левого угла.
Обратите внимание, что делегатом события кнопки является делегат EventHandler, определённый в библиотеке .NET Framework для различных событий. Об этом делегате, рекомендованном фирмой Microsoft для упорядочивания применения событий, упоминалось в разделе 7.5, а в примере 7.5.6 иллюстрировалось его применение.
Java. Добавление к кнопке предопределённого обработчика actionPerformed() класса ActionListener осуществляет функция addActionListener() класса Button. К кнопке можно применить множество свойств, наследуемых из класса Component, например, setLocation() и getLocation() при размещении кнопки и setSize() для установки размера.
Классы TextBox и TextField
C# и C++/CLI.Объектом класса TextBox представляется поле ввода информации, являющееся простейшим редактором текста. Объект создаётся конструктором:
TextBox ( );
При применении класса TextBox кроме его собственных свойств можно воспользоваться наследуемыми свойствами Text для помещения и извлечения текста, BackColor для установки и получения цвета фона, Multiline для указания многострочности, TextLength для получения длины текста и так далее. А свойства Size и Location, как и в случае кнопки, позволяют определить размеры панели редактирования и координаты её верхнего левого угла.
Java. Для размещения редактора класса TextField в области клиента окна применяются свойства setLocation() и getLocation(), для установки размера – setSize(). Имеется ещё много свойств, наследуемых из класса Component.
Пример 9.8.1. Программа с управляющими элементами на языке C#.
Приложение имеет прикладное окно с кнопкой и редактором текста. В окне рисуется российский трехцветный флаг. Цвета флага – белый, синий и красный – применяются и при рисовании строки, введенной в редактор. Строка рисуется при нажатии на клавишу мыши, начиная с носика курсора мыши. Нажатие на кнопку меняет белый цвет строки циклически на синий, красный, а затем на белый цвет..
Прикладное окно изображено на рис. 9.8.3.
///////////////
// C#
using System;
using System.Drawing;
using System.Windows.Forms;
class CTextBox_Button : Form // Класс прикладного окна
{
TextBox ourTextBox; // Редактор
Button ourButton; // Кнопка
Point point; // Начальные координаты строки
Color [] colors; // Массив цветов флага
int n; // Индекс массива цветов colors
public CTextBox_Button( ) // Конструктор
{
Text= "Russian flag"; // Установить заголовок прикладного окна
ourTextBox= new TextBox ( ); // Создать редактор
ourTextBox.Location= new Point (10, 120); // Установить позицию
ourTextBox.Size= new Size (150, 20); // Установить размер
ourTextBox.Text= "Russia"; // Установить текст в редакторе
Controls.Add (ourTextBox); // Добавить редактор в форму
ourButton= new Button ( ); // Создать кнопку
ourButton.Location= new Point (170, 10); // Установить позицию
ourButton.Size= new Size (40, 20); // Установить размер
ourButton.Text= "OK"; // Установить текст в кнопке
Controls.Add (ourButton); // Добавить кнопку к форме
// Подписать обработчик на событие Click кнопки
ourButton.Click += new EventHandler (OurButtonClick);
// Подписать обработчик на событие MouseDown мыши
MouseDown += new MouseEventHandler (OurMouseDown);
n= 0;
point= new Point (100, 100);
colors= new Color [3]; // Создать массив цветов
colors [0]= Color.FromArgb (255, 255, 255); // Белый цвет
colors [1]= Color.FromArgb (0, 0, 255); // Синий цвет
colors [2]= Color.FromArgb (255, 0, 0); // Красный цвет
}
// Перерисовать область клиента окна
protected override void OnPaint (PaintEventArgs arg)
{
base.OnPaint (arg);
Rectangle rect= new Rectangle (10, 10, 130, 30);
for(int i= 0; i < 3; i++) // Нарисовать флаг
{
arg.Graphics.FillRectangle // Нарисовать прямоугольник
(new SolidBrush (colors [i]), rect);
rect.Offset (0, 30); // Переместить прямоугольник вниз
}
// Нарисовать строку от носика курсора мыши
arg.Graphics.DrawString (ourTextBox.Text,
new Font ("Arial", 16), new SolidBrush (colors [n]), point);
}
// Обработать событие MouseDown мыши
void OurMouseDown (object sender, MouseEventArgs arg)
{
point.X= arg.X; // Сохранить координату X носика курсора мыши
point.Y= arg.Y; // Сохранить координату Y носика курсора мыши
Invalidate ( ); // Перерисовать область клиента окна
}
// Обработать событие Click кнопки
void OurButtonClick (object sender, EventArgs arg)
{
n++; // К следующему цвету флага
if (n >= 3) n= 0;
Invalidate ( ); // Перерисовать область клиента окна
}
static void Main ( ) // Главная функция
{
Application.Run (new CTextBox_Button ( )); // Выполнить
// приложение
}
}
C#. Выполнение приложения начинает функция Main(), которая вызывает функцию Run(), передав ей в качестве аргумента объект класса CTextBox_Button прикладного окна. Прикладное окно с изображенным флагом, кнопкой и редактором появляется на экране.
При создании объекта класса CTextBox_Button выполняется конструктор CTextBox_Button().
Вначале конструктор, используя свойство Text объекта окна, присваивает заголовку окна название Russian flag. Затем создаются редактор текста и кнопка. Свойства Location и Size редактора и кнопки размещают их в области клиента окна и устанавливают размеры. Функция Add() включает объекты редактора и кнопки в коллекцию прикладного окна. Если не сделать этого, то окно не будет считать редактор и кнопку своими объектами и не отобразит их в окне.
Теперь осуществляется подписка обработчика OurMouseDown() на событие MouseDown мыши: создаётся объект делегата EventHandler и этот объект добавляется к объекту события MouseDown. Затем на событие Click кнопки подписывается её обработчик OurButtonClick().
Перед завершением конструктор создаёт объект point точки для фиксации координат носика мыши, необходимых при рисовании строки функцией OnPaint().
Наконец, создается и инициализируется массив colors цветов.
Обработчик OurMouseDown() события MouseDown мыши прост. При каждом нажатии на клавишу мыши точке point присваиваются координаты носика мыши и вызывается функция Invalidate(), которая инициирует выполнение функции перерисовки OnPaint().
Функция OnPaint() вначале изображает флаг, трижды рисуя со смещением прямоугольник rect с требуемыми цветами. Используемая при этом функция FillRectangle() закрашивает прямоугольник кистью нужного цвета. Затем функция DrawString() рисует строку, извлечённую из редактора с помощью свойства Text.
Обработчик OurButtonClick() кнопки меняет цвет рисуемой строки, изменив индекс n элемента массива colors цветов и вызвав функцию Invalidate(). Функция Invalidate() инициирует перерисовку окна с объектом кисти очередного цвета. Этот объект кисти класса SolidBrush применяет функция DrawString() рисования строки.
Рис. 9.8.3. Прикладное окно приложения примера 9.8.1 с управляющими элементами
Пример 9.8.2. Программа с управляющими элементами на языке Java.
///////////////
// Javaи J#
import java.awt.*;
import java.awt.event.*;
class CTextBox_Button extends Frame // Класс прикладного окна
{
TextField ourTextBox; // Редактор
Button ourButton; // Кнопка
Point point; // Начальные координаты строки
Color [] colors; // Массив цветов флага
int n; // Индекс массива цветов colors
public CTextBox_Button ( ) // Конструктор
{
setTitle ("Russian flag"); // Установить заголовок прикладного окна
setSize (400, 200);
this.setBackground (Color.lightGray);
setLayout (new FlowLayout ( ));
ourTextBox= new TextField ( ); // Создать редактор
ourTextBox.setSize (150, 20); // Установить размер
ourTextBox.setText ("Russia"); // Установить текст в редакторе
add (ourTextBox); // Добавить редактор в форму
ourButton= new Button ("OK"); // Создать кнопку
add (ourButton); // Добавить кнопку к форме
// Подписать обработчик на событие кнопки
ourButton.addActionListener (new ActionListener ( )
{
public void actionPerformed (ActionEvent aE)
{
n++; // К следующему цвету флага
if (n >= 3) n= 0;
repaint ( );
}
});
// Подписать обработчик на событие при нажатии на мышь
this.addMouseListener (new MouseAdapter ( )
{
public void mousePressed (MouseEvent mE)
{
System.out.println ("Mouse: x= " + mE.getX ( )
+ " y= " + mE.getY ( ));
point.x= mE.getX ( );
point.y= mE.getY ( );
repaint ( );
}
});
// Примененить внутренний анонимный класс для закрытия окна
this.addWindowListener (new WindowAdapter ( )
{
public void windowClosing (WindowEvent wE)
{
System.exit (0);
}
});
n= 0;
point= new Point ( );
point.x= 100; point.y= 100;
colors= new Color [3]; // Создать массив цветов
colors [0]= Color.white; // Серый цвет
colors [1]= Color.blue; // Синий цвет
colors [2]= Color.red; // Красный цвет
}
// Перерисовать область клиента окна
public void paint (Graphics g)
{
Rectangle rect= new Rectangle (10, 40, 130, 30);
for (int i= 0; i < 3; i++) // Нарисовать флаг
{
g.setColor (colors[i]);
g.fillRect // Нарисовать прямоугольник
(rect.x, rect.y, rect.width, rect.height);
rect.translate (0, 30); // Переместить прямоугольник вниз
}
// Нарисовать строку от носика курсора мыши
g.setColor (colors[n]);
g.drawString (ourTextBox.getText ( ), point.x, point.y);
}
public static void main ( ) // Главная функция
{
CTextBox_Button cT= new CTextBox_Button ( );
cT.show ( );
}
}
Для упрощения размещения управляющих элементов в прикладном окне на языке Java применяются объекты так называемых менеджеров компоновки классов FlowLayout, BorderLayout, CardLayout и GridLayout. Каждый менеджер компоновки располагает управляющие элементы, добавленные в контейнер окна с помощью функции add(), в прикладном окне в соответствии с принятыми в нём правилами размещения. Вызов в конструкторе функции
setLayout (new FlowLayout ( ));
устанавливает менеджер FlowLayout компоновки, который размещает элементы “потоком” от центра, начиная с верхней части окна, что иллюстрирует рис. 9.8.4.
Но можно разместить управляющие элементы, применив свойство setLocation(), в любом месте окна, отказавшись от менеджеров компоновки. Для этого в конструкторе вызывается
setLayout (null);
Рис. 9.8.4.Прикладное окно приложения примера 10.8.2 с управляющими элементами на языке Java
Пример 9.8.3. Программа с управляющими элементами на языке C++/CLI.
///////////////
// C++/CLI
#include "stdafx.h"
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Drawing;
ref class CTextBox_Button: public Form // Класс прикладного окна
{
TextBox ^pTextBox; // Дескриптор редактора
Button ^pButton; // Дескриптор кнопки
Point point; // Начальные координаты строки
array<Color ^> ^colors; // Массив цветов
int n; // Индекс цвета
public:
CTextBox_Button ( ) // Конструктор
{
// Подписать обработчик на событие MouseDown мыши
MouseDown += gcnew MouseEventHandler (this,
&CTextBox_Button::OurMouseDown);
Text= "Russian flag"; // Установить заголовок окна
n= 0;
BackColor= Color::Gold;
colors= gcnew array<Color^> (3); // Создать массив структур
colors [0]= Color::White;
colors [1]= Color::Blue;
colors [2]= Color::Red;
pTextBox= gcnew TextBox ( ); // Создать объект редактора
pTextBox -> Location= Drawing::Point (10,120); // Разместить
pTextBox-> Size= Drawing::Size (150, 20); // Размер
pTextBox -> Text= "Russia"; // Поместить текст в редактор
Controls -> Add (pTextBox); // Добавить редактор к форме
pButton= gcnew Button; // Создать объект кнопки
pButton -> Location= Point (170,10); // Разместить
pButton -> Size= Drawing::Size (40, 20); // Размер
pButton -> Text= "OK"; // Поместить текст в кнопку
Controls -> Add (pButton); // Добавить кнопку к форме
// Подписать обработчик на событие Click кнопки
pButton -> Click += gcnew EventHandler (this,
&CTextBox_Button::OurButtonClick);
}
protected:
virtual void OnPaint (PaintEventArgs ^ arg) override // Перерисовать область
{
Form::OnPaint (arg);
Rectangle rect (10, 10, 130, 30); // Прямоугольник третьей части флага
for(int i=0; i < 3; i++) // Нарисовать флаг
{
arg->Graphics->FillRectangle(gcnew SolidBrush (*colors[i]),
rect.X, rect.Y, rect.Width, rect.Height);
rect.Offset (0, 30); // Сместить прямоугольник rect вниз
}
// Написать строку от координат носика курсора мыши
arg -> Graphics -> DrawString (pTextBox ->Text,
gcnew Drawing::Font ("Arial", 16),
gcnew SolidBrush (*colors [n]), point);
}
void OurMouseDown (Object ^pSender, MouseEventArgs ^arg)
{
point.X= arg -> X; // Сохранить координату X носика курсора мыши
point.Y= arg -> Y; // Сохранить координату Y курсора мыши
Invalidate ( ); // Перерисовать область клиента окна
}
// Обработать щелчок на кнопке
void OurButtonClick (Object ^sender, EventArgs ^e)
{
n++; // К следующему цвету массива цветов colors
if (n >= 3) n= 0;
Invalidate ( ); // Перерисовать область клиента окна
}
};
void main ( )
{
Application::Run (gcnew CTextBox_Button ( )); // Выполнить
}
C++/CLI. В отличии от программы на языке Java в программе на языке C++/CLI появилась глобальная главная функция main(), операторы #using добавляют требуемые библиотеки. Трансформировалась грамматика – появилось слово ref перед управляемым классом, операторы new заменены на gcnew, появились дескрипторы и стрелки. Иначе получены объекты делегатов событий, аргументы конструкторов которых содержат ссылку this на объект-окно, содержащий обработчик, и ссылку на сам обработчик. В заголовке функции OnPaint() появились ключевые слова virtual и override.
Пример 9.8.3 программы на языке C++/CLI иллюстрирует некоторые особенности этого языка, которые удивят программистов, знающих предшествующую версию C++.NET. Они, наверняка, обратят внимание на создание и использование массива цветов, присвоение значений свойствам кнопки и редактора, а также на неожиданное появление в заголовке функции OnPaint() после слова OnPaint ключевого слова override, что совершенно не согласуется с принятыми описаниями функций в предшествующем языке С++.NET.
Рис. 9.8.5.Прикладное окно приложения примера 10.8.3 с управляющими элементами на языке C++/CLI
Классы UserControl и Panel
Объекты класса UserControl языка C# и объекты класса Panel языка Java своеобразны, поскольку представляют соответствующие управляющие элементы в окне в виде прямоугольных областей, каждая из которых обладает функциональностью окна. То есть в этой прямоугольной области можно выводить графическую информацию, применять свои потоки и события. Так в прикладном окне могут появиться прямоугольные области, самостоятельно функционирующие. Эти области (точнее, эти управляющие элементы) размещаются в окне так же, как и любые другие управляющие элементы – кнопки, редакторы и т.д. Обычно на базе классов UserControl и Panel создаются порождённые классы, в которых, воспользовавшись свойствами управляющего элемента, легко получаются координаты его левой верхней точки, размер элемента и другие свойства, а также многие функции базовых классов UserControl и Panel, включая функции OnPaint() или paint() и Invalidate() или repaint(). Такое необычное применение в порождённом классе свойств, установленных вне управляющего элемента, иллюстрируют программы примеров 9.8.4, 9.8.5 и 9.8.6.
C# и C++/CLI.Объектом класса UserControl представляется поле, обладающее свойствами и функциональностью окна. Объект создаётся конструктором:
UserControl ( );
При применении класса UserControl можно воспользоваться свойствами BackColor, ClientSize, Controls, Height, Location, Size, Visible, Width и другими, наследуемыми из класса Control, событиями Click, MouseDown, MouseUp и множеством других, наследуемых из того же класса Control. Также наследуются известные нам функции OnPaint() и Invalidate(), и масса других. Как видим, управляющий элемент типа UserControl может существенно обогатить интерфейс пользователя с программой.
Java. Также как и элемент типа UserControl, управляющий элемент типа Panel содержит сотни свойств и функций, наследуемых из базовых классов. Выделим, например, такие свойства, как getGraphics(), getSize(), getLocation(), и функции paint() и addMouseListener().
Сравните диаграммы классов рисунков 9.3.1.1 и 9.8.6. Сравнение показывает, что функциональность управляющего элемента типа UserControl и окна типа Form схожи. Что же касается управляющего элемента типа Panel и окна типа Frame, то здесь имеется отличие, как явствует из рисунков 9.3.1.2 и 9.8.7.
Рис. 9.8.6.Наследование базовых классов классом UserControl в C# и C++/CLI
Рис. 9.8.7.Наследование базовых классов классом Panel в Java
Пример 9.8.4. Программа с управляющим элементом типа UserControl на языке C# с рисованием в потоке.
Приложение имеет прикладное окно с элементом типа UserControl. В области клиента этого управляющего элемента рабочий поток рисует эллипсы со случайными цветами и размерами, сменяющие друг друга (см. рис.9.8.8).
Рис. 9.8.8.Прикладное окно программы примера 10.8.4
///////////////
// C#
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
// Класс управляющего элемента типа UserControl
class User: UserControl
{
Thread t; // Ссылка на поток
bool life; // Признак жизни потока
Graphics g; // Ссылка на графический объект
Random rand; // Ссылка на случайное число
// Конструктор
public User ( )
{
g= this.CreateGraphics (); // Создать граф.объект
rand= new Random (); // Создать случайное число
// Создать и запустить поток рисования
life= true; // Пусть поток живёт
t= new Thread (new ThreadStart (F)); // Создать объект
t.Start ( ); // Запустить поток
}
// Выполнить рабочий поток
private void F ( )
{
while(life)
{
// Нарисовать случайный эллипс в области упр.элемента
g.FillEllipse (new SolidBrush (Color.FromArgb (rand.Next(255),
rand.Next(255), rand.Next(255))),
rand.Next (this.Width),
rand.Next (this.Height),
rand.Next (this.Width<this.Height?this.Width:this.Height),
rand.Next (this.Width<this.Height?this.Width:this.Height));
// Поспать
Thread.Sleep (50);
}
}
}
// Класс прикладного окна
class W: Form
{
User u; // Ссылка на объект управляющего элемента
//Конструктор
public W ( )
{
u= new User( ); // Создать объект управляющего элемента
u.Location= new Point (50, 40);// Разместить,
u.Size =new Size (140, 130); // установить размер
u.BackColor=Color.Coral; // и фон управляющего элемента
Controls.Add (u); // Включить управляющий элемент в
// коллекцию
}
// Выполнить основной поток
static void Main ( )
{
Application.Run (new W());
}
}
Обсуждение.
C#.
Читатель.
Изменится ли функционирование программы, если воспользоваться функцией перерисовки OnPaint()?
Автор.
В нижеследующей программе в области клиента управляющего элемента типа UserControl рабочий поток совместно с функцией OnPaint() рисует эллипсы со случайными цветами и размерами. Поскольку при рисовании каждого эллипса область клиента управляющего элемента перерисовывается заново, в отличие от примера 9.8.4, в котором текущий эллипс рисуется на предыдущих эллипсах, в нижеследующем примере 9.8.5 каждый рисуемый эллипс сменяет предыдущий.
//Пример 9.8.5. Обсуждение. C# программа с управляющим элементом типа UserControl
// и функцией OnPaint().
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
// Класс управляющего элемента типа UserControl
class User: UserControl
{
Thread t; // Ссылка на поток
bool life; // Признак жизни потока
Graphics g; // Ссылка на графический объект
Random rand; // Ссылка на случайное число
// Конструктор
public User ( )
{
g= this.CreateGraphics (); // Создать граф.объект
rand= new Random (); // Создать случайное число
// Создать и запустить поток рисования
life= true; // Пусть поток живёт
t= new Thread (new ThreadStart(F)); //Создать объект потока
t.Start ( ); // Запустить поток
}
// Выполнить рабочий поток
private void F ( )
{
while (life)
{
// Перерисовать область клиента управляющего элемента
Invalidate ( );
// Поспать
Thread.Sleep (50);
}
}
protected override void OnPaint (PaintEventArgs e)
{
base.OnPaint (e);
// Нарисовать случайный эллипс в области упр.элемента
e.Graphics.FillEllipse (new SolidBrush(Color.FromArgb(rand.Next(255),
rand.Next (255),rand.Next(255))),
rand.Next (this.Width),
rand.Next (this.Height),
rand.Next (this.Width<this.Height?this.Width:this.Height),
rand.Next (this.Width<this.Height?this.Width:this.Height));
}
}
// Класс прикладного окна
class W: Form
{
User u; // Ссылка на объект управляющего элемента
//Конструктор
public W ( )
{
u= new User ( ); // Создать объект управляющего элемента
u.Location= new Point (50, 40); // Разместить,
u.Size =new Size (140, 130); // установить размер
u.BackColor=Color.LightGray; // и фон управляющего элемента
Controls.Add (u); // Включить управляющий элемент в
// коллекцию окна
}
// Выполнить основной поток
static void Main ( )
{
Application.Run (new W());
}
}
Пример 9.8.6. Программа с управляющим элементом типа Panel на языке Java с применением потока и функции paint()
Как и в программе примера 9.8.5 в данной программе рисование случайных эллипсов в пределах управляющего элемента типа Panel осуществляется функцией paint(), вызываемой посредством функции repaint() из потока. В области клиента управляющего элемента рисуются эллипсы со случайными координатами, размерами и цветами.
///////////////
// Java и J#
import java.awt.*;
import java.awt.event.*;
import java.util.*;
// Класс управляющего элемента типа UserControl
class User extends Panel implements Runnable
{
Thread t; // Ссылка на поток
boolean life; // Признак жизни потока
Graphics g; // Ссылка на графический объект
Random rand; // Ссылка на случайное число
// Конструктор
public User ( )
{
show();
this.setVisible (true); // Показать управляющий элемент
g= this.getGraphics (); // Создать графический объект
rand= new Random (); // Создать случайное число
// Создать и запустить поток рисования
life= true; // Пусть поток живёт
t= new Thread (this); // Создать объект потока
t.start ( ); // Запустить поток
}
// Завершить поток
public void Finish ( )
{
life= false;
try
{
t.join ( );
}
catch (InterruptedException e) { }
}
// Выполнить рабочий поток
public void run ( )
{
while (life)
{
System.out.println ("-----I'm working");
repaint ( );
// Поспать
try
{
Thread.sleep (50);
}
catch (InterruptedException e) { }
}
}
public void paint (Graphics g)
{
g.setColor (new Color (rand.nextInt(255),
rand.nextInt(255), rand.nextInt(255)));
// Нарисовать случайный эллипс в области упр.элемента
g.fillOval (
rand.nextInt (this.getSize().width),
rand.nextInt (this.getSize().height),
rand.nextInt (this.getSize().width<this.getSize().height?
this.getSize().width:this.getSize().height),
rand.nextInt (this.getSize().width<this.getSize().height?
this.getSize().width:this.getSize().height));
}
}
// Класс прикладного окна
class W extends Frame
{
User u; // Ссылка на объект управляющего элемента
//Конструктор
public W ( )
{
setLayout(null);
setSize (340, 230); // Установить размер
u= new User( ); // Создать объект управл. элемента
u.setLocation(50, 40); // Разместить,
u.setSize (140, 130); // установить размер
u.setBackground (Color.lightGray); // и фон управляющего
// элемента
add(u); // Включить управляющий элемент в
// коллекцию
// Применить внутренний анонимный класс
// для закрытия окна
this.addWindowListener (new WindowAdapter ( )
{
public void windowClosing (WindowEvent wE)
{
System.exit (0);
}
});
show();
}
// Выполнить основной поток
static public void main ( )
{
W w= new W ();
}
}
Дочерние окна
При необходимости можно создать дополнительно к прикладному окну (основной форме или фрейму) другие окна, называемые дочерними окнами (дочерними формами или фреймами). На