«Если отладка - процесс удаления ошибок, то программирование должно быть процессом их внесения»
58. Разработка активных форм ActiveForm
Что такое активная форма.
Зачем нужны активные формы.
Последовательность создания активной формы.
Для иллюстрации активных форм воспользуемся примером из работы П.Дарахвелидзе, Е.Маркова и О.Котенка "Программирование в Delphi 5". Предположим, что нам требуется в различных приложениях (в том числе и разрабатываемых на других языках) предоставить пользователям возможность удобного выбора параметров пера Pen (компонент TPen). Подчеркнем, что если бы такая возможность требовалась только в одном приложении, то активную форму можно было бы и не разрабатывать, а просто реализовать необходимый код. В приведенном ниже примере будет реализован выбор цвета пера из имеющегося набора, типов и толщин линий.
Этап 1. Создание проекта.
В репозитории Delphi выбираем мастера File | New | ActiveX | ActiveForm. Delphi отобразит такое диалоговое окно:
В этом окне необходимо указать, в простейшем случае, только имя новой активной формы (New ActiveX Name). Другие имена при этом мастер изменит автоматически, что, впрочем, не мешает Вам изменить их и вручную. После указания имени формы окно примет вид:
Последний штрих перед завершением этого этапа – сохранение проекта под заданным именем. Если оставить все имена без изменения, то в результате Delphi создаст такие файлы:
1. PenProj1.dpr – файл проекта (library PenProj1), который после компиляции станет DLL библиотекой с расширением .ocx (OLE Custom eXtension).
2. PenIpml1.pas – файл реализации, который и является основным файлом-носителем специфических методов и свойств активной формы.
3. PenProj1_TLB.pas – как обычно при использовании СОМ объектов, этот файл является описанием библиотеки типов (type library) на паскале.
Этап 2. Разработка формы.
Переходим к форме и размещаем на ней необходимые компоненты, а именно два компонента ComboBox и один UpDown. Разместим их на форме в желаемом виде и обязательно минимизируем размеры формы для того, чтобы в клиентском приложении она не занимала на экране много места. В результате форма примет такой вид:
Этап 3. Реализация свойств и методов компонентов формы. На этом этапе мы должны реализовать те возможности, которые хотим предоставить пользователям нашего комбинированного компонента.
Назначение компонентов формы примем следующим:
· ComboBox1 – для выбора цвета пера;
· ComboBox2 – для выбора типа пера (вида линии);
· UpDown – для выбора толщины линии.
Для того чтобы в списках ComboBox1 и ComboBox2 непосредственно отображался цвет и тип линии соответственно, зададим значения csOwnerDrawFixed (отрисовываемые пользователем) их свойствам Style с помощью инспектора объектов.
Далее добавим в файл PenIpml1.pas четыре обработчика событий (с помощью инспектора объектов), а также массив констант цветов DefColors и глобальную переменную PenWidth. Вот фрагменты файла PenImpl1.pas с изменениями:
unit PenImpl1;
…
type
TPenX = class(TActiveForm, IPenX)
ComboBox1: TComboBox;
ComboBox2: TComboBox;
UpDown: TUpDown;
procedure ActiveFormCreate(Sender: TObject);
procedure ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure ComboBox2DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure UpDownClick(Sender: TObject; Button: TUDBtnType);
…
end;
implementation
uses ComObj, ComServ;
{$R *.DFM}
const DefColors : array[0..15] of TColor = (clBlack, clDkGray,
clGray, clLtGray, clSilver, clBlue, clFuchsia, clGreen,
clLime, clMaroon, clNavy, clOlive, clPurple, clRed,
clTeal, clWhite);
var PenWidthGbl : integer; // толщина пера
…
procedure TPenX.ActiveFormCreate(Sender: TObject);
var i : integer;
begin
for i:=Low(DefColors) to High(DefColors) do
ComboBox1.Items.Add(IntToStr(i));
for i:=0 to 7 do
ComboBox1.Items.Add(IntToStr(i));
end;
procedure TPenX.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
ComboBox1.Canvas.Brush.Color:=DefColors[Index];
ComboBox1.Canvas.FillRect(Rect);
end;
procedure TPenX.ComboBox2DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var HCenter : integer;
begin
HCenter:=Rect.Top + (Rect.Bottom-Rect.Top) shr 1;
with ComboBox2.Canvas do
begin
Brush.Color:=clWhite;
FillRect(Rect);
Pen.Style:=TPenStyle(Index);
Pen.Width:=PenWidthGbl;
MoveTo(Rect.Left,HCenter);
LineTo(Rect.Right,HCenter);
end;
end;
procedure TPenX.UpDownClick(Sender: TObject; Button: TUDBtnType);
begin
PenWidthGbl:=UpDown.Position;
ComboBox2.Repaint;
end;
…
end.
В результате добавления такого кода мы должны получить при нажатии на кнопку списка ComboBox1 палитру из 16 цветов, а при нажатии на кнопку ComboBox2 – палитру из 8 типов линий. Нажатие на кнопки UpDown должно приводить к изменению толщины выбранной линии. Однако, на этом этапе разработки активной формы мы этого увидеть не сможем, так как библиотека не может быть запущена на выполнение без клиентского приложения.
Сейчас можно только откомпилировать проект и устранить возможные ошибки.
Этап 4. Реализация свойств и методов интерфейса активной формы.
Для того чтобы клиентское приложение могло получить доступ к возможностям нашего комбинированного элемента – активной формы – требуется дополнить автоматически сгенерированный мастером Delphi СОМ объект свойствами и методами.
Вызовем редактор библиотеки типов (например, с помощью меню View | Type Library) и добавим к имеющемуся интерфейсу три новых свойства: PenWidth, PenStyle и PenColor. Каждое из свойств можно сделать свойством только для чтения, так как программно мы их изменять не собираемся, но в этом случае свойства не будут отображаться в инспекторе объектов приложения, которое будет использовать наш компонент. Если свойство будет свойством для чтени и записи, то редактор библиотеки типов для каждого свойства создаст два метода: один для записи и второй – для чтения. Для всех методов возвращаемое значение по умолчанию имеет тип long, который мы изменять не будем, так как этот тип нас устраивает. В результате мы увидим окно редактора с таким содержимым:
Теперь нажмем кнопку Refresh Implementation и добавим код в заготовки методов чтения значений свойств (в файле PenImpl1.pas):
function TPenX.Get_PenColor: Integer;
begin
Result:=DefColors[StrToInt(ComboBox1.Text)];
end;
function TPenX.Get_PenStyle: Integer;
begin
Result:=StrToInt(ComboBox2.Text);
end;
function TPenX.Get_PenWidth: Integer;
begin
Result:=UpDown.Position;
end;
После внесения этих изменений можно откомпилировать проект, однако наш комбинированный элемент еще не полностью готов к использованию
После регистрации библиотеки DLL необходимо с помощью меню Component | Install Component добавить новый компонент в палитру компонентов. В окне Unit File Name необходимо указать имя файла с библиотекой типов TLB. Установить компонент можно в новый пакет (страница Into new package) или в существующий (Into existing package). Окно диалога по включению компонента в новый пакет имеет такой вид (задано имя пакета MyPackage):
При установке компонента в существующий пакет будет выведено такое диалоговое окно:
В этом случае надо последовательно выполнить команды "Compile", "Add" и "Install".
В результате выполнения этих действий (по установке пакета) будут получены файлы MyPackage.bpl и MyPackage.dpk, которые в данном примере будут располагаться в том же каталоге, что и файлы проекта. В палитре компонент на вкладке ActiveX появится новый компонент. Этот компонент будет виден во вновь создаваемых проектах благодаря тому, что в каталог Delphi\Projects\Bpl будут записаны файлы MyPackage.bpl и MyPackage.dcp.
В принципе теперь компонент можно включить в клиентское приложение таким же образом, как включаются любые другие компоненты, имеющиеся в палитре компонент. Если мы построим и запустим клиентское приложение, то увидим, что новый компонент реагирует на нажатие пользователем кнопок списков ComboBox и кнопки UpDown. Однако наш компонент имеет один весьма существенный недостаток: клиентское приложение не получает никаких "известий" о том, что пользователь выбрал новый цвет, тип или толщину линии. Для посылки известия клиентскому приложению требуется выполнить следующий – заключительный – этап.
Этап 5. Дополнение активной формы событием.
Событие, точнее, метод, который будет посылать сообщение клиентскому приложению в нужное время, необходимо добавить к интерфейсу IPenXEvents. Для этого вернемся к редактору библиотеки типов и добавим метод OnPenChanged к интерфейсу IPenXEvents. Для этого метода не нужно задавать никаких параметров и для него Delphi не создает заготовки. Опять-таки необходимо обновить реализацию библиотеки типов (Refresh Implementation).
Это еще не все. Далее требуется создать обработчики событий OnChange для каждого из компонентов, входящих в состав нашей активной формы: ComboBox1, ComboBox2 и UpDown. Эти обработчики должны иметь такой код:
Procedure TPenX.ComboBox1Change(Sender: TObject);
Begin
if FEvents<>nil then FEvents.OnPenChanged;
End;
После внесения этих дополнений необходимо вновь откомпилировать проект и зарегистрировать его для того, чтобы изменения в библиотеке типов были внесены в реестр Windows. Кроме того, необходимо вновь установить компонент – файл с библиотекой типов TLB – в палитру компонент Delphi и повторно откомпилировать пакет. После выполнения всех этих действий событие OnPenChanged появится в списке событий нашего компонента при включении его в клиентское приложение.
Использование активной формы в клиентском приложении.
Клиентское приложение строится обычным образом и в него включается компонент PenX из палитры компонент (страница ActiveX по умолчанию). Включенный компонент будет иметь, помимом прочих, свойства PenWidth, PenStyle и PenColor, а также событие OnPenChanged. Теперь достаточно добавить обработчик этого события и можно получать значения свойств PenWidth, PenStyle и PenColor, установленные пользователем.
Для проверки корректности работы разработанной активной формы можно вывести эти значения с использованием какого-нибудь компонента типа TMemo.
«57. Обработка ошибок в Windows»
59. Разработка клиентских приложений для MS Office