Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ООП / Заочники / ООП_ЛабРаб_Методичка

.pdf
Скачиваний:
30
Добавлен:
08.06.2015
Размер:
770.97 Кб
Скачать

Запустите две копии приложения и убедитесь, что оно сопротивляется любым попыткам закрыть его. Заметьте, что вместо обработки сообщения такую проверку можно сделать в обработчике события OnClose формы. Текст процедуры должен быть таким

if (Application.MessageBox('Меня хотят закрыть.Согласны?', 'Караул! Закрывают!', MB_YESNO +MB_ICONEXCLAMATION + MB_DEFBUTTON2) = IDYES) then Abort;

3.7.Добавьте группу радиокнопок «Свернуть», «Развернуть», «Восстановить», «Закрыть» и кнопку «Выполнить». В обработчике щелчка кнопки пошлите сообщение WM_SYSCOMMAND выбранному окну. Параметр wParam должнен принимать значение

SC_MINIMIZE, SC_MAXIMIZE, SC_RESTORE или SC_CLOSE в соответствии с выбранной радиокнопкой. Параметр lParam во всех случаях должен быть равен 0.

3.8.Добавьте в радиогруппу функцию активизации окна (Сообщение WM_ACTIVE с параметром wParam WA_ACTIVE).

3.9.Добавьте в приложение обработчик события WM_GETMINMAXINFO, который ограничивает высоту окна в пределах 100-200 пикселей, ширину - в пределах 150-300 пикселей и задает координаты левого верхнего угла распахнутого окна, равными текущим координатам левого верхнего угла окна.

type

TForm1 = class(TForm)

private procedure WMGetMinMaxInfo(var Info:

TWMGetMinMaxInfo); message WM_GETMINMAXINFO;

end; implementation

procedure TForm1.WMGetMinMaxInfо (var Info:TWMGetMinMaxInfo); begin

with Info.MinMaxInfo^ do begin

ptMinTrackSize.x := 150; ptMaxTrackSize.x := 300; ptMinTrackSize.y := 100; ptMaxTrackSize.y := 200; ptMaxPosition.x:=BoundsRect.Left; ptMaxPosition.y:=BoundsRect.Top;

end;

inherited;

end;

Установите параметры, соответствующие размерам окна вашего приложения.

3.10.Заголовок формы (свойство Caption) сделайте равным «Приложение 1». Добавьте кнопку, при нажатии на которую приложению с именем «Приложение будет отсылаться пользовательское сообщение. Объявите в приложении константу с именем типа пользовательского сообщения:

const WM_MyPost = WM_USER;

а в обработчик события щелчка на кнопке вставьте оператор

SendMessage(FindWindow('TForm1','Приложение 2'), WM_MyPost,2, 5) ;

3.11.Объявите в классе формы обработчик сообщения WM_MyPost: procedure WMMyPost(var a:TMessage) ; message WM_MyPost;

procedure TForm1.WMMyPost(var a:TMessage); begin

ShowMessage (' Получено сообщение '+IntToStr(a.wParam) + ' : ' + IntToStr(a.lParam));

end;

3.12.Создайте новое приложение и скопируйте в него форму из предыдущего приложения. Заголовок формы в новом приложении сделайте равным «Приложение 2».

Во втором приложении адресатом сообщения должно быть приложение с заголовком «Приложение 1».

3.13.Оттранслируйте приложения и запустите оба на выполнение. Убедитесь, что определенные вами сообщения передаются и обрабатываются.

3.14.В приложении с именем «Приложение объявите событие OnMyEvent. Добавьте вызов события в обработчик вашего сообщения. Создайте процедуру обработки события OnMyEvent. В обработчике события OnCreate назначьте обработчик для события OnMyEvent. Проверьте работу с помощью приложения 2.

3.15.Объявите несколько обработчиков вашего события. Можно, например, в разных обработчиках окрашивать в разные цвета какой-либо текст, отображаемый на форме.

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

TRadioGroup.

3.16.Объявите собственный тип события с двумя параметрами. В качестве параметров

при вызове обработчика события из обработчика сообщения передавайте параметры wParam и lParam сообщения. Разработайте процедуру обработки события для проверки правильности обработки сообщения.

4. Содержание отчета

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

-задание;

-листинг программы с подробными комментариями;

-копии экранных форм приложений.

5.Контрольные вопросы

5.1.Что такое событие и обработчик события в языке Object Pascal?

5.2.Что представляет собой сообщение Windows?

5.3.Как выполняется обработка сообщений в приложениях Windows?

5.4.Каким образом одно приложение Windows может послать сообщение другому приложению?

5.5.Как определить собственное сообщение?

5.6.С какой целью регистрируются сообщения?

5.7.На какие диапазоны делятся номера сообщений?

5.8.Как определить дескриптор окна Windows-приложения?

Лабораторная работа № 7

Создание и удаление объектов форм во время выполнения программы

Цель работы: изучение методов динамического создания объектов форм, задания значений их свойствам и обработчикам событий, поиска и удаления объектов во время выполнения приложения.

1. Задание

Разработать приложение, состоящее из приведенной на рис.1 формы и выполняющее следующие функции.

1.1.Загрузка содержимого фрагмента dfm-файла формы из лабораторной работы №1, сохраненного в текстовом формате.

1.2.Создание объектов, перечисленных в этом файле, размещение их на форме и установка значений указанных в файле свойств.

1.3.При создании элементов обеспечить возможность их буксировки в форме с помощью мышки.

1.4.Вывод созданного списка объектов в ListBox.

1.5.Поиск выбранного в списке объекта, установка фокуса на него.

1.6.Удаление из формы выбранного в списке объекта.

Рис.1. Форма приложения

2. Краткая теория

Классами в Object Pascal называются специальные типы, которые содержат поля, методы и свойства. Как и любой другой тип, класс служит лишь образцом для создания

конкретных экземпляров реализации, которые называются объектами. Важным отличием классов от других типов является то, что объекты класса всегда распределяются в куче, поэтому объект-переменная фактически представляет собой лишь указатель на динамическую область памяти. Однако в отличие от других указателей при ссылке на содержимое объекта запрещается использовать символ «^» за именем объекта.

В состав любого класса входят два специальных метода - конструктор и деструктор. У класса TObject эти методы называются Create и Destroy, так же они называются в подавляющем большинстве его потомков. Конструктор распределяет объект в динамической памяти и помещает адрес этой памяти в переменную Self, которая автоматически объявляется в классе. Деструктор удаляет объект из кучи.

Любые поля объекта, а также методы класса, оперирующие с его полями, могут вызываться только после создания объекта с помощью вызова конструктора, т.к.

конструкторы распределяют объект в динамической памяти и делают действительным содержащийся в объекте указатель.

В базовом классе TObject определен метод Free, который сначала проверяет действительность адреса объекта и лишь затем вызывает деструктор Destroy. Обращение к деструктору объекта будет ошибочным, если объект не создан конструктором, поэтому для уничтожения ненужного объекта следует вызывать метод Free.

Пример создания компонента периода выполнения (создание и размещение на форме кнопки):

procedure TForml.ButtonlClick(Sender: TObject); var

NewButton: TButton; begin

NewButton:= TButton.Create(self); NewButton.Name := 'Button2'; NewButton.Left := 100; NewButton.Top :=100; NewButton.Parent := self;

End;

Этот код создает новую кнопку, дает ей имя, устанавливает ее позицию в форме и присваивает свойства Parent (явно) и Owner (неявно). Имя кнопки (свойство Name) должно быть уникальным для данной формы. Свойства Parent и Owner очень похожи.

Каждый элемент управления (не все компоненты, а только производные от TControl) должен иметь родителя, то есть компонент, производный от TWindow. Родитель хранит ссылки на своих потомков в свойстве Controls, являющемся массивом объектов классов, наследников от класса TControl. Свойство оконного объекта ControlCount показывает количество элементов управления, которые размещаются в его окне. Свойство Parent позволяет компоненту, порожденному из TControl, связываться с его окном.

Свойство Owner устанавливается во время вызова конструктора - в данном случае TButton.Create(self). Свойство Owner определяет, какой компонент ответственен за разрушение данного компонента. Владелец хранит ссылки на компоненты, которые он обязан уничтожить, в свойстве Components, являющемся массивом объектов классов, наследников от класса TComponent. Свойство ComponentCount определяет количество таких компонентов в массиве.

В приведенном выше коде форма является и владельцем и родителем элемента управления NewButton. Поэтому окно формы управляет отображением кнопки, и форма уничтожит кнопку, когда приложение уничтожает форму.

При использовании свойств формы Components всегда можно обратиться к каждому компоненту формы. Однако, если нужен доступ к отдельному компоненту, можно воспользоваться методом FindComponent формы владельца вместо сравнения имени каждого компонента с именем искомого компонента. Метод FindComponent класса TComponent по заданному имени компонента находит его в массиве Components и возвращает ссылку на компонент. Если компонент не найден, то возвращается значение nil. Имя компонента (значение его свойства Name) имеет специальную связь с именем соответствующего поля класса формы, ссылающейся на компонент.

Общее правило для новичков: «Никогда не изменяйте значение свойства Name компонента на этапе выполнения, если не вы создали этот компонент при выполнении приложения».

3.Методика выполнения

3.1.Создайте каталог для файлов проекта и скопируйте в него файл Test.txt.

3.2.Создайте проект, содержащий форму с компонентами TListBox, TOpenDialog и TMainMenu (рис.1). Установите для объекта типа TListBox свойство Align в alLeft.

3.3.Включите в MainMenu пункты:

-Загрузить исходный файл (активен всегда);

-Создать компоненты (активен после загрузки; после создания компонентов становится неактивным);

-Найти компонент (активен после создания компонентов);

-Удалить компонент (активен после создания компонентов).

3.4.Используя свойство Filter, установите для объекта типа TOpenDialog в качестве допустимых типов файлов текстовые (*.txt), файлы формы (*.dfm) и все файлы (*.*). Для настройки диалога используйте свойства InitialDir, DefaultExt, Title.

3.5.Создайте процедуру выбора и загрузки файла в ListBox. Использовуйте методы

TListBox.Items.LoadFromFile и компонент TOpenDialog.

Добавьте в форму универсальные обработчики событий onMouseDown, onMouseUp, onMouseMove, которые реализуют буксировку элементов формы:

var

Move : boolean; X0,Y0 : integer;

procedure TForm1.ObjectMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

if (Button<>mbLeft) then exit; X0:=X;

Y0:=Y;

Move:=true;

(Sender as TControl).BringToFront; end;

procedure TForm1.ObjectMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);

begin

if Move then

with (Sender as TControl) do SetBounds(Left+X-X0, Top+Y-Y0,Width,Height); end;

procedure TForm1.ObjectMouseUp(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer); begin

Move:=false;

end;

3.6.Разработайте процедуру создания компонентов, параметры которых заданы содержимым ListBox. Список допустимых типов компонентов ограничьте типами, приведенными в исходном файле. Установите параметры Name, Caption(Text), Left, Top, Height, Width, Align. Для установки значения свойства Caption(Text) в случае представления объекта как компонента типа TControl используйте метод TControl.SetTextBuff(PChar(<текстовая строка>)). Для задания подчиненности используйте свойство Parent. Для каждого созданного компонента задайте обработчики событий, обеспечивающие их буксировку:

OnMouseDown:=ObjectMouseDown;

OnMouseMove:=ObjectMouseMove;

OnMouseUp:=ObjectMouseUp;

3.7.Список созданных компонентов загрузите в ListBox.

3.8.Имя компонента для поиска или удаления выбирайте в списке компонентов ListBox. Для поиска компонента по его имени используйте метод класса TСomponent FindComponent. Установка фокуса выполняйте оператором

(Temp as TWinControl).SetFocus;

где Temp – переменная типа TСomponent, результат работы метода FindComponent.

4.Содержание отчета

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

-задание;

-листинг программы с подробными комментариями;

-копия экранной формы до загрузки файла в ListBox1;

-копия экранной формы после загрузки файла в ListBox1;

-копия экранной формы после создания компонентов;

-на всех копиях экранных форм должны быть указаны идентификаторы всех объектов формы.

5.Контрольные вопросы

5.1.Что такое конструктор и деструктор класса? Как они используются в программах?

5.2.Объясните работу процедур при буксировке объектов формы при помощи мышки.

5.3.Объясните назначение, устройство и основные методы компонента TOpenDialog.

5.4.Как работает метод класса TСomponent FindComponent?

5.5.В чем разница между свойствами компонентов Parent и Owner?

5.6.Удалите из формы объект Panel1. Как изменяется вид формы? Объясните результат работы программы.

5.7.Какова структура dfm-файла в текcтовом виде?

Лабораторная работа № 8

Построение собственных компонентов

Цель работы: изучение принципов и механизмов наследования классов на примере создания собственного компонента Delphi.

1. Задание

Разработать компонент «Светофор», состоящий из 3 расположенных вертикально индикаторов красного, желтого и зеленого цветов. В любой момент времени может быть «включенным» только один из них. Компонент должен иметь свойства, позволяющие устанавливать его размеры (высоту и ширину), режим работы (ручной или автоматический), интервал переключения индикаторов в автоматическом режиме. Компонент должен позволять программировать обработку событий OnClick для каждого из индикаторов разрешать или запрещать переключение светофора. Щелчок левой

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

2. Краткая теория

Рассмотрим процесс разработки и тестирования нового компонента Delphi на примере простого графического компонента «светодиод» и составного компонента «светофор».

Графический компонент «светодиод»

LED - это английское сокращение светоизлучающего диода, твердотельного электронного индикатора, обычно используемого для отображения таких двоичных состояний, как передача/прием модема. Компонент назван TDdhLed с использованием префикса Ddh, чтобы обеспечить уникальность компонента в Deplhi.

Вот объявление этого простого класса компонента. Как видно, объявлены только два свойства цвет Color и состояние Status:

type

TDdhLedStatus = (lsOn, lsOff); TDdhLed = class(TGraphicControl) private

fStatus: TDdhLedStatus; fColor: TColor; protected

procedure SetStatus(Value: TDdhLedStatus); procedure SetColor(Value: TColor);

public

constructor Create(Owner: TComponent); override; procedure Paint; override;

published

property Status: TDdhLedStatus read fStatus write SetStatus default IsOn; property Color: TСolor read fColor write SetColor default clRed; property Width default 20;

property Height default 20; property OnClick; property OnDblClick;

end;

Для свойства Status определен перечислимый тип данных TDdhLedStatus, более понятный и гибкий, чем тип данных Boolean. Обычно, для построения имен

перечислимых значений нужно использовать начальные буквы компонента и имени свойства (ls — для LED Status), например, lsOn.

Исследуя объявления свойств, обратите внимание, что в них включены для директив read частные переменные fStatus и fColor и определены процедуры для соответствующих директив write. Процедуры используются для таких установок значений свойства, чтобы можно было модифицировать интерфейс пользователя при изменении свойства.

Важно называть поля, методы доступа и свойства в соответствии со стандартом, определенным фирмой Borland и описанным в руководстве Component Writer's Guide. Основной руководящий принцип: используйте для свойств содержательные имена, добавляйте f к именам полей, соответствующих свойствам, и называйте методы доступа,

как Get или Set плюс имя свойства (как SetStatus и SetColor).

Стандартной форме соответствуют два метода Set: если новое значение действительно отличается от текущего, измените значение и обновите интерфейс пользователя. Иначе, ничего не делайте. Метод TControl.Invalidate полностью перерисовывает объект на экране (с помощью метода TGraphikControl.Paint).

procedure TDDHLed.SetStatus(Value: TDdhLedStatus); begin

if Value <> fStatus then begin

fStatus := Value; Invalidate; end;

end;

procedure TDDHLed.SetColor(Value: TColor); begin

if Value <> fColor then begin

fColor:= Value; Invalidate; end;

end;

Конструктор Create вызывает унаследованную версию конструктора (важный шаг, который необходимо запомнить), а затем устанавливает значения, определенные как значения свойства по умолчанию:

constructor TDDHLed.Create(Owner: TComponent); begin

inherited Create(Owner);

// Установка значений по умолчанию fColor:= clRed;

fStatus:= lsOn; Width := 20; Height := 20; end;

He забудьте добавить ключевое слово override в объявление конструктора, иначе библиотека визуальных компонент VCL никогда не вызовет вашу версию конструктора!

Так как значения свойств по умолчанию устанавливаются в конструкторе, их нужно отразить при объявлении свойства, добавляя ключевое слово default и определяя значения, используемые в конструкторе. Delphi не использует значение свойства по умолчанию для автоматической установки. За правильную установку значений по

умолчанию отвечает конструктор компонента. Только из-за этого версия конструктора Create объявлена замещаемой.

Метод Paint — немного сложнее методов свойств. Сначала он выводит фоновый круг, используемый как рамка, а затем внутренний круг, если LED включен. Чтобы убедиться, что LED выглядит правильно (они всегда круглые), проверяются свойства ширины и высоты для определения действительного диаметра LED.

procedure TDDHLed.Paint; var

Radius, XCenter, YCenter: Integer; begin

// Получение минимума между шириной и высотой if Height > Width then Radius := Width div 2-2

else Radius := Height div 2-2; // получение центра XCenter := Width div 2;

YCenter := Height div 2;

Canvas.Brush.Color := clDkGray; // цвет границы LED (фиксированный)

// повехность led

Canvas.Ellipse (XCenter - Radius, YCenter - Radius,XCenter + Radius, YCenter + Radius); if fStatus = lsOn then

begin

Canvas.Brush.Color := fColor; Radius := Radius - 3;

Canvas.Ellipse (XCenter - Radius, YCenter - Radius, XCenter + Radius, YCenter + Radius); end;

end;

Перед использованием компонента TDdhLED, его нужно установить в библиотеку визуальных компонентов VCL Delphi. При этом Delphi добавит код компонента в библиотеку и вызовет процедуру Register для каждого компонента, определенного в модуле. Процедура Register делает компонент доступным для среды проектирования Delphi и помещает его в отдельную страницу палитры:

procedure Register; begin

RegisterComponents('DDHB', [TDdhLED]); // Компонент TDdhLEDбудет размещен на

// странице DDHB.

end;

Для проверки работы компонента DdhLed необходимо написать тестовую программу. Например, можно поместить компоненты TCheckBox и TDdhLed в главную форму и подключить один и тот же метод к событию OnClick обоих компонентов. Щелчок мышью на LED или элементе TCheckBox изменяет состояние DdhLed. Поэтому обработчик события должен изменить в текущее состояние DdhLed, используя оператор if. В более сложном варианте можно предусмотреть возможность изменения цвета светодиода и изменение его размеров.

Составной компонент «светофор»

Теперь построим компонент, связывающий вместе три компонента DdhLed и компонент Timer. Его иногда называют составным компонентом.

Отобразим три компонента DdhLed (красный, желтый и зеленый) и позволим в любой момент времени быть «включенным» только одному из них, а затем представим

альтернативный режим, в котором красный DdhLed вспыхивает на интервал, определенный в свойстве.

Вот полное объявление класса:

type

TDdhSemaphore = class(TCustomControl) private

fGreenL, fYellowL, fRedL: TDDHLed; // Три цвета светофора fSemState: TSemState; // состояние светофора

fTimer: TTimer; // задает период мигания fInterval: Integer; // интервал переключения

// события щелчка мышью на цвете

fGreenClick, fRedClick, fYellowClick: TLightClickEvent; procedure TimerOnTimer(Sender: TObject);

procedure TurnOff; procedure StartPulse; procedure StopPulse;

// методы ответа LED на щелчок procedure GreenLedClick(Sender: TObject); procedure RedLedClick(Sender: TObject); procedure YellowLedClick(Sender: TObject); protected

// методы доступа к свойствам procedure SetSemState(Value: TSemState); procedure SetInterval(Value: Integer); public

constructor Create(Owner: TComponent); override; destructor Destroy; override;

procedure Paint; override;

procedure SetBounds (ALeft, ATop,AWidth, AHeight: Integer); override; published

// Новые свойства

property SemState: TSemState read fSemState write SetSemState default scOff; property Interval: Integer read fInterval write SetInterval default 500;

//наследуемые свойства со значениями по умолчанию property Width default 30; property Height default 90;

//заказные события

property GreenClick: TlightClickEvent read fGreenClick write fGreenClick; property RedClick: TlightClickEvent read fRedClick write fRedClick; property YellowClick: TlightClickEvent read fYellowClick write fYellowClick;

end;

Вкачестве базового класса выбран TCustomControl, так как он предоставляет возможности раскрашивания TGraphicControl (имеет метод Paint, который можно переопределить).

Вкомпонент TDdhSemaphore включены три компонента TDdhLed для отображения сигналов светофора. Свойство SemState определяет состояние светофора. Для этого свойства не использовано имя Color, чтобы не запутывать пользователей компонента (обычно, свойство предназначено для другой роли), и чтобы рассмотреть состояния «включено» и «мигает». Свойство SemState основано на перечислении:

type

TSemState = (scRed, scGreen, scYellow, scOff, scPulse);

Метод SetSemState сложнее большинства методов установки свойств и вызывает другие частные методы этого класса (TurnOff, StartPulse и StopPulse). Кроме присваивания