Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабы Мартын 1(ComCorbaLab2004).doc
Скачиваний:
32
Добавлен:
10.02.2016
Размер:
1.81 Mб
Скачать

2.1. Проект. Основные принципы

Один из возможных проектов - сконвертировать текстовый симулятор в «нечто» с удобным GUI интерфейсом (этот симулятор используется для изучения архитектуры компьютеров и языка ассемблер). Для такого проекта было бы неплохо иметь индикатор для отображения нулевых или единичных значений, и, хотя удобно было бы воспользоваться стандартным компонентом, ни один из них не имеет таких свойств.

Первое, с чего следует начать при написании компонента - это решить, что именно он должен делать. Компонент, который создается, должен быть одного цвета, когда он включен, и другого, когда выключен. Он также должен иметь свойство On/Off. OK, это звучит очень просто (первое правило - пытаться сохранить проект простым!).

Следующий шаг - решить, на каком компоненте будет основан создаваемый. Здесь стоит взглянуть на иерархию VCL. Ясно, что компонент будет разновидностью TGraphicControl, и уж если надо сохранить компонент простым, почему бы не сделать его простой окружностью вместо сложного битмапа. TShape на первый взгляд, кажется идеальным. Стоит познакомиться с ним поближе (это можно сделать с помощью хелпа или просто, поместив его на форму и внимательно взглянув на его свойства и методы). У него есть не нужные свойства и методовы, но, по крайней мере, в него встроена вся необходимая функциональность.

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

Все, что надо сделать - это сменить форму компонента на ту, которая нужна и менять цвет кисти, когда индикатор включается или выключается.Это все довольно просто, и сейчас, когда это придумано, можно приступать к делу.

2.2. Как создать компонент

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

Шаг 1: Создание тестового приложения:

  • Создание нового приложения (File | New Application).

  • Добавление на форму ближе к низу компонент TButton.

  • Выбор File | Save All и сохранение формы как LEDForm.cpp и проекта как LEDProj.

Шаг 2: Создание компонента:

  • Выбор Component | New, или, в C++Builder v1 File | New и выбор Component в диалоге New File Items.

  • Установка Ancestor type в TShape, имя класса TLED, Palette Page в "Samples". Щелчек "OK" без нажатия "Install" если используется версия 3 или старше (см. рис. 2.1.).

  • Сохранение нового файла компонента. Если используется C++Builder v1 сохранение его под именем LED.cpp (несмотря на то, что по умолчанию unit1.cpp).

Рис. 2.1. Создание компонента

Теперь, когда предварительные действия завершены, можно посмотреть, что же сгенерировал C++ Builder. Файл LED.cpp будет выглядеть примерно так (возможно он будет слегка отличаться - в разных версиях по-разному):

//-----------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "LED.h"

#pragma package(smart_init)

//-----------------------------------------------------------------

//-----------------------------------------------------------------

static inline void ValidCtrCheck(TLED *)

{

new TLED(NULL);

}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner)

{

}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

namespace Led

{

void __fastcall PACKAGE Register()

{

TComponentClass classes[1] = {__classid(TLED)};

RegisterComponents("Samples", classes, 0);

}

}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

Первая функция, ValidCtrCheck, используется для того, чтобы убедиться, что компонент не имеет чистых виртуальных функций (не является абстрактным классом). Не надо о ней беспокоиться.

Следующая функция - конструктор. Он вызывается, когда компонент помещается на форму, все его характеристики, в том числе и видимые в Object Inspector, получают значения по умолчанию именно здесь.

Третья функция регистрирует компонент. Пространства имен позволяют библиотекам (в данном случае компонентам) использовать одни и те же имена без конфликтов (на самом деле тут все немного сложнее). TComponentClass содержит массив имен компонентов, которые надо зарегистрировать (в одной этой функции можно зарегистрировать столько компонентов, сколько необходимо). Подробнее эта функция (т.е. если надо написать свою, вместо той, что подставил Builder) описана в разделе "Writing The Register Function" Component Writers Guide.

Теперь надо взглянуть на .h файл, созданный Билдером. Если этого еще не сделано, следует зайти в редактор и выбрать соответствующую закладку. На экране появится содержимое LED.h (если используется C++Builder v1, там не будет макроса PACKAGE):

//-----------------------------------------------------------------

#ifndef LEDH

#define LEDH

//-----------------------------------------------------------------

#include <SysUtils.hpp>

#include <Controls.hpp>

#include <Classes.hpp>

#include <Forms.hpp>

#include <ExtCtrls.hpp>

//-----------------------------------------------------------------

class PACKAGE TLED : public TShape

{

private:

protected:

public:

__fastcall TLED(TComponent* Owner);

__published:

};

//-----------------------------------------------------------------

#endif

Не очень много, только множество включений файлов, которые пришлось бы делать вручную. Теперь надо кое-что написать самим в дополнение к скелету, соданному Билдером.Далеебудет нужна булева переменная для хранения статуса и специальные переменные для хранения цветов обоих состояний индикатора. Тип bool подойдет для статуса, а для хранения цвета имеется специальный класс (вернее, перечисляемый тип) TColor. Хорошее правило ООП - делать переменные внутри класса скрытыми, чтобы другие программисты имели к ним доступ только через интерфейс класса - это гарантия, что данные не будут испорчены. Поэтому все переменные будут в секции private. Класс будет выглядеть примерно так (пишетсяcolour, как это принято в Великобритании и Канаде):

private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

Префикс F в именах переменных - стандартное соглашение именования членов компонент (позволяет использовать то же имя, только без F, для соответствующего свойства (published property)). Теперь, раз есть переменные, нужны аксессоры (accessors) - функции, осуществляющие доступ, по-английски "access", к данным класса извне, и мутаторы (mutators) - функции, меняющие значения данных класса, по английски - "mutate", (здесь еще добавлены ключевые слова __published и __property). Странным может показаться, что эти функции помещены в ту же секцию, что и данные, и это будет объяснено позже:

private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

void __fastcall SetOnOff(const bool Value);

void __fastcall SetOnColour(const TColor OnColour);

void __fastcall SetOffColour(const TColor OffColour);

Существует стандартное соглашение о том, что имена аксессоров начинаются с "Get" , а имена мутаторов - с "Set". Хотя это может показаться так же неудобным, как начинать имена переменных с F, на самом деле это облегчит жизнь в дальнейшем.

Если еще не использовалсямодификатор __fastcall для функций,надо объяснить, зачем он нужен. Это означает, что при наследовании от классов VCL все функции должны быть __fastcall.

Странныйфакт, что мутаторы объявлены как private. Здесь используется расширение фирмы Борланд в C++ ключевым словом__published. Все свойтва объекта, которыми планируется манипулировать визуально, должны быть объявлены как __published, а аксессоры и мутаторы подключаются к ним с помощью следующего синтаксиса: Если добавить три свойства в секции __published, и класс будет выглядеть примерно так:

__published:

__property bool LEDOn = {read = FOnOff, write = SetOnOff};

__property TColor OnColour = {read = FOnColour, write = SetOnColour};

__property TColor OffColour = {read = FOffColour, write = SetOffColour};

Все объявления свойств начинаются с ключевого слова __property, затем следуют тип переменной (свойства), и объявления членов доступа к свойству. Интересно, что read указывает на переменную, а write на функцию. И read, и write могут указывать и на функцию, и на переменную, но обычно делают так, чтобы write указывал на функцию, в которой есть некоторые проверки правильности значений и т.д. Имя свойства может не иметь ничего общего с именем переменной, которую оно модифицирует (например, переменная FOnOff модифицируется свойством LEDOn). Хотя рекомендуется использовать стандартные соглашения имен - имя свойства должно быть таким же, как и имя связанной с ним переменной, за исключением префикса F в начале. Это все, что нам нужно изменить в файле заголовка класса.

Хотя введение свойств может показаться инородным, может быть, даже кощунственным для старых программистов C++, но если вы хотите полностью использовать новые возможности C++Builder, вы вынуждены будете использовать свойства.