От Delphi 4 к Delphi 5 часть 14
От Delphi 4 к Delphi 5 Палитра компонентов.
Страница QREPORT.
Компоненты страницы QReport обеспечивают простой способ создания отчетов по результатам выборки данных из базы данных (рисунок 1).
QuickRep (отчет) позволяет превращать форму, на которую он помещен, в отчет (в документ, подготовленный к печати).
QRSubDetail (главный — детальный), в отличие от компонента QRGroup, не требует включения компонента Query для создания отчета.
QRStringsBand (строковая полоса) отличается от QRBand встроенным списком строк Items, который обрабатывается с помощью размещаемого на полосе компонента QRExpr. Полоса повторяется в отчете столько раз, сколько строк содержит список.
QRBand (полоса отчета) формирует в отчете полосу того или иного назначения: полосу — заголовок, полосу промежуточных данных, полосу с итоговыми данными и т.д.
QRChildBand (вспомогательная полоса) предназначен для размещения других компонентов отчета.
QRGroup (группа отчета) используется совместно с компонентом Query для создания отчета типа "отчет главный — детальный".
QRLabel (текст) обеспечивает вывод в отчет статического текста, например, заголовков колонок.
QRDBText (текстовое поле) используется для вывода в отчет текстовой информа-ции из базы данных.
QRExpr (вычисляемое поле) обеспечивает типичные для отчета вычисления: суммирование данных, вычисление среднего значения, поиск максимума и т.д.
QRSysData (системная величина) используется для вывода разнообразной системной информации: текущей даты, времени, номера печатаемой страницы, общего количества записей.
QRMemo (многострочный текст) обеспечивает вывод в отчет многострочного текстового поля.
QRRichText (многострочный форматированный текст) в отличие от QRMemo, использует текст в формате RTF.
QRDBRichText представляет собой многострочное форматированное текстовое поле.
QRShape (фигура), с помощью этого компонента можно поместить в отчет простейшую фигуру — прямоугольник, окружность, линию и т.д.
QRImage (рисунок) служит для размещения в отчете произвольной графической информации.
QRDBImage (поле данных, содержащее рисунок), в отличие от QRImage, графические данные для этого компонента берутся из базы данных.
QRCompositeReport (составной отчет) облегчает создание отчетов, манипулирующих выборками данных из множества таблиц.
QRPreview (просмотр) обеспечивает просмотр внешнего вида отчета с возможностью последующей печати этого документа.
QRChart (диаграмма) служит для размещения в отчете диаграмм, внедряемых из базы данных.
QRExprMemo (многострочное вычисляемое поле) содержит поля, вычисляемые по данным, внедренным из других полей набора данных.
QRTextFilter предназначен для экспорта отчета при его печати и в формат текстовых строк.
QRCVFilter предназначен для экспорта отчета при его печати в формат CSV.
QRHTMLFilter предназначен для экспорта отчета при его печати в формат HTML.
Изменения в тексте модуля .PAS и .DFM в процессе разработки приложения
Весь исходный текст, т.е. программный код, записан в одном файле .pas. Он развивается и растет, когда вы помещаете новые компоненты на форму и формируете отклики на некоторые события. Свойства внедряемых компонентов сохраняются вместе со свойствами формы в файле .dfm.
Среда Delphi все ваши действия отображает в том или ином коде. Нет ничего скрытого и недоступного. Вы получаете программный код, и хотя некоторые его фрагменты могут быть довольно сложными, вы можете редактировать все, по крайней мере, пока не станете крупным специалистом по программированию в Delphi. Давайте посмотрим, что генерирует Delphi в ходе выполнения операций во время разработки приложения и как каждое ваше действие отражается в файле .pas и .dfm.
1. Запустите Delphi.
2. Сохраните файл модуля под именем Primer_pas. Вы увидите изменение имени модуля, вместо принимаемого по умолчанию unit Unit1; на unit Primer_;. Имя модуля должно соответствовать имени файла.
3. Как только вы начинаете добавлять в форму компоненты, описание класса формы в исходном тексте изменяется. Например, поместите на форму компонент Button — часть текста, описывающая новый тип данных, становится такой (новая строка показана полужирным шрифтом):
Итак, имеется файл .dfm, описывающий форму, ее свойства, компоненты формы и свойства компонентов. Это двоичный файл, который нельзя прочитать в обычном редакторе. Однако если загрузить этот файл в редакторе кода Delphi, он преобразуется в текстовое описание, которое вы можете просмотреть на этапе разработки приложения. Для этого необходимо щелкнуть правой кнопкой мыши на форме, из всплывшего меню выбрать команду View as Text. Эта команда закроет форму и откроет dfm-файл (рисунок 2). Для возвращения к форме необходимо щелкнуть правой кнопкой мыши в редакторе кода и из всплывшего меню необходимо выбрать команду View as Form.
В Delphi 5 эта возможность сохранена. Но вам предоставляется способ выбрать, в каком виде вы предпочтете сохранять файл формы: текстовом или двоичном. Если вы щелкнете на форме правой кнопкой мыши, то увидите во всплывшем меню индикатор Text DFM. По умолчанию он включен, и файлы форм сохраняются в текстовом виде. Если выключить этот индикатор, то они будут храниться в двоичном виде.
Чтобы понять содержимое файла .dfm, рассмотрим приведенную ниже последовательность операторов, где представлено текстовое описание формы приложения Primer. Вы можете сравнить этот текст с фрагментами программного кода, приведенными выше.
Компонент CheckBox.
Этот компонент представляет собой независимый переключатель, который используется для того, чтобы указать свое решение типа Да/Нет или Да/Нет/Не знаю. Данное решение отражается в свойстве State компонента, доступном как для чтения, так и для записи. В составе диалогового окна может быть несколько компонентов CheckBox. Состояние любого из них не зависит от состояния остальных, поэтому такие переключатели называются независимыми.
Свойство Alignment определяет положение текста: taLeftJustify — текст с левой стороны от компонента; taRightJustify — текст размещается с правой стороны от компонента.
Свойство AllowGrayed компонента разрешает или запрещает использование состояния cbGrayed (Не знаю).
Свойство Checked компонента содержит выбор типа Да/Нет. Состояния cbUnchecked и cbGrayed отражаются в это время как False.
Свойство State определяет состояние компонента CheckBox как cbUnchecked — нет, cbChecked — да, cbGrayed — не знаю.
Управляющий элемент контрольного индикатора позволяет пользователю выбрать состояние On (включено, отмечено) или Off (выключено, не отмечено) в качестве индивидуального варианта выбора или установки. Когда выбор произведен, в индикаторе имеется метка, она появляется внутри него в виде крестика или галочки. Когда нет выбора, индикатор пуст.
Компонент RadioButton
В отличие от компонента CheckBox, компоненты RadioButton представляют собой зависимые переключатели, предназначенные для выбора одного из нескольких взаимоисключающих решений. На форму или желательно на компонент-контейнер помещается, по меньшей мере, два таких компонента. Они могут находиться в одном из двух состояний, определяемом свойством Checked. Если в одном компоненте это свойство принимает значение True, то во всех других компонентах, расположенных в том же контейнере, свойства Checked принимают значение False.
Управляющий элемент радиокнопка, также известный под названием кнопки выбора опции, отображает единственный вариант выбора из набора взаимоисключающих опций. В любой группе радиокнопок пользователь в данный момент может выбрать одну и только одну кнопку. То есть когда несколько радиокнопок помещены в один и тот же контейнер, типа окна группы, они становятся взаимоисключающими.
Если радиокнопка выбрана вами, то она отображается в виде круга с закрашенной серединой. Невыбранная радиокнопка представляет собой пустой круг. Для изменения ее визуального состояния необходимо присвоить значение True или False свойству Checked.
Вы можете использовать группы радиокнопок, чтобы разрешить пользователю выбирать одно значение из ограниченного набора вариантов, обычно не больше, чем четырех или пяти. Если количество вариантов выбора превышает это число, то вам, возможно, стоит рассмотреть альтернативные способы, которые менее требовательны к размеру области экрана. Например, это компоненты типа окон списков или комбинированных окон.
Компонент RadioGroup.
Он представляет собой просто совокупность из нескольких радиокнопок, размещенных внутри группового окна. Радиогруппа имеет свойство Items, в результате чего требуется только ввод заголовков радиокнопок во время разработки, а их размещение и выравнивание определяется автоматически.
Рассмотрим пример применения радиокнопок.
Форма для этого примера содержит компонент RadioGroup с несколькими радиокнопками, с помощью которых можно выбрать одну из пяти допустимых констант функции MessageBeep.
Программа позволяет воспроизводить звук, соответствующий текущему выбору, когда вы производите щелчок по кнопке с заголовком "Выдача сигнала". Метод обработки события OnClick сначала определяет константу, которая соответствует одной из выбранных радиокнопок, с использованием оператора case, и затем с помощью этой же константы воспроизводит звук. Если на компьютере не установлен подходящий звуковой драйвер, то вы услышите только стандартные сигналы от выбранного элемента.
1. Запустите Delphi.
2. Сохраните файл модуля под именем Signal_pas, а файл проекта — под именем Signal dpr.
3. Поместите на форму компонент RadioGroup со страницы Standard палитры компонентов. Выделите компонент RadioGroup1, перейдите на страницу Properties инспектора объекта. Найдите строку Items, щелкните дважды левой кнопкой мыши справа от слова (TStrings). Откроется редактор String list editor, который позволяет в строки вводить заголовки кнопок. Введите следующие строки: "mb_IconAsterisk", "mb_IconExclamation", "mb_IconHand", "mb_IconQuestion", "mb_OK" (рисунок 3), что будет соответствовать появлению радиокнопок и заголовков на панели RadioGroup1.
4. Поместите кнопку Button1 на форму и, используя событие OnClick для кнопки, напишите следующий код:
Результат работы программы показан на рисунке 4.
Использование компонентов CheckBox можно показать на примере изменения цвета формы путем нажатия левой кнопки мыши над одним из компонентов CheckBox и перетягиванием мышью на форму, не отпуская ее левой кнопки. Для данного примера используем три компонента CheckBox, у которых свойство DragMode в инспекторе объекта на странице Properties установлено в dmAutomatic. Программный код имеет следующий вид.
Литература:
1. Марко Канту. Delphi 2 для Windows 95/NT. Москва. ООО "Малип". 1997 г.
2. Джон Матчо. Дэвид Р. Фолкнер. Delphi. Москва. БИНОМ. 1995 г.
3. Эндрю Возневич. Delphi. Освой самостоятельно. Москва. Восточная книжная компания. 1996 г.
4. В.В.Фаронов. Delphi 5. Учебный курс. Москва. Издательство Нолидж. 1998 г.
5. А. Я. Архангельский. Программирование в Delphi 5. Москва. ЗАО "Издательство Бином". 2000 г.
Владимир Скуратов
(c) компьютерная газета
Страница QREPORT.
Компоненты страницы QReport обеспечивают простой способ создания отчетов по результатам выборки данных из базы данных (рисунок 1).
QuickRep (отчет) позволяет превращать форму, на которую он помещен, в отчет (в документ, подготовленный к печати).
QRSubDetail (главный — детальный), в отличие от компонента QRGroup, не требует включения компонента Query для создания отчета.
QRStringsBand (строковая полоса) отличается от QRBand встроенным списком строк Items, который обрабатывается с помощью размещаемого на полосе компонента QRExpr. Полоса повторяется в отчете столько раз, сколько строк содержит список.
QRBand (полоса отчета) формирует в отчете полосу того или иного назначения: полосу — заголовок, полосу промежуточных данных, полосу с итоговыми данными и т.д.
QRChildBand (вспомогательная полоса) предназначен для размещения других компонентов отчета.
QRGroup (группа отчета) используется совместно с компонентом Query для создания отчета типа "отчет главный — детальный".
QRLabel (текст) обеспечивает вывод в отчет статического текста, например, заголовков колонок.
QRDBText (текстовое поле) используется для вывода в отчет текстовой информа-ции из базы данных.
QRExpr (вычисляемое поле) обеспечивает типичные для отчета вычисления: суммирование данных, вычисление среднего значения, поиск максимума и т.д.
QRSysData (системная величина) используется для вывода разнообразной системной информации: текущей даты, времени, номера печатаемой страницы, общего количества записей.
QRMemo (многострочный текст) обеспечивает вывод в отчет многострочного текстового поля.
QRRichText (многострочный форматированный текст) в отличие от QRMemo, использует текст в формате RTF.
QRDBRichText представляет собой многострочное форматированное текстовое поле.
QRShape (фигура), с помощью этого компонента можно поместить в отчет простейшую фигуру — прямоугольник, окружность, линию и т.д.
QRImage (рисунок) служит для размещения в отчете произвольной графической информации.
QRDBImage (поле данных, содержащее рисунок), в отличие от QRImage, графические данные для этого компонента берутся из базы данных.
QRCompositeReport (составной отчет) облегчает создание отчетов, манипулирующих выборками данных из множества таблиц.
QRPreview (просмотр) обеспечивает просмотр внешнего вида отчета с возможностью последующей печати этого документа.
QRChart (диаграмма) служит для размещения в отчете диаграмм, внедряемых из базы данных.
QRExprMemo (многострочное вычисляемое поле) содержит поля, вычисляемые по данным, внедренным из других полей набора данных.
QRTextFilter предназначен для экспорта отчета при его печати и в формат текстовых строк.
QRCVFilter предназначен для экспорта отчета при его печати в формат CSV.
QRHTMLFilter предназначен для экспорта отчета при его печати в формат HTML.
Изменения в тексте модуля .PAS и .DFM в процессе разработки приложения
Весь исходный текст, т.е. программный код, записан в одном файле .pas. Он развивается и растет, когда вы помещаете новые компоненты на форму и формируете отклики на некоторые события. Свойства внедряемых компонентов сохраняются вместе со свойствами формы в файле .dfm.
Среда Delphi все ваши действия отображает в том или ином коде. Нет ничего скрытого и недоступного. Вы получаете программный код, и хотя некоторые его фрагменты могут быть довольно сложными, вы можете редактировать все, по крайней мере, пока не станете крупным специалистом по программированию в Delphi. Давайте посмотрим, что генерирует Delphi в ходе выполнения операций во время разработки приложения и как каждое ваше действие отражается в файле .pas и .dfm.
1. Запустите Delphi.
2. Сохраните файл модуля под именем Primer_pas. Вы увидите изменение имени модуля, вместо принимаемого по умолчанию unit Unit1; на unit Primer_;. Имя модуля должно соответствовать имени файла.
3. Как только вы начинаете добавлять в форму компоненты, описание класса формы в исходном тексте изменяется. Например, поместите на форму компонент Button — часть текста, описывающая новый тип данных, становится такой (новая строка показана полужирным шрифтом):
type TForm1 = class(TForm) Button1: TButton; private { Private declarations } public { Public declarations } end; Далее произведите изменение значения свойства Name кнопки с помощью инспектора о бъекта на PrimerButton, код снова изменится: type TForm1 = class(TForm) PrimerButton: TButton;.4. Существенное изменение кода происходит после добавления обработчиков событий. Для каждого определяемого вами обработчика события добавляется строка в описании типа данных для формы, а также появляется пустое тело процедуры обработки события в разделе реализации (implementation). Добавьте событие OnClick для формы и для кнопки. Полный программный текст показан ниже:
unit Primer_; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) PrimerButton: TButton; procedure PrimerButtonClick(Sen der: TObject); procedure FormClick(Sender: TObject); private { Private declarations } public { Publi c declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.PrimerButtonClic k(Sender: TObject); begin MessageDlg ('Вы произвели щелчок по кнопке', mtInformation, [mbOk], 0); en d; procedure TForm1.FormClick (Sender: TObject); begin MessageDlg ('Вы произвели щелчок по форме', m tInformation, [mbOk], 0); end; end.Описание формы содержит файл .dfm. Он хранит свойства формы, свойства компонентов, расположенных на ней, включая описание их месторасположения на ее поверхности, а также другую информацию о состоянии формы. В общем случае исходный текст описывает действия, производимые с формой и ее компонентами. Перечисленные свойства определяют состояние формы и ее компонентов.
Итак, имеется файл .dfm, описывающий форму, ее свойства, компоненты формы и свойства компонентов. Это двоичный файл, который нельзя прочитать в обычном редакторе. Однако если загрузить этот файл в редакторе кода Delphi, он преобразуется в текстовое описание, которое вы можете просмотреть на этапе разработки приложения. Для этого необходимо щелкнуть правой кнопкой мыши на форме, из всплывшего меню выбрать команду View as Text. Эта команда закроет форму и откроет dfm-файл (рисунок 2). Для возвращения к форме необходимо щелкнуть правой кнопкой мыши в редакторе кода и из всплывшего меню необходимо выбрать команду View as Form.
В Delphi 5 эта возможность сохранена. Но вам предоставляется способ выбрать, в каком виде вы предпочтете сохранять файл формы: текстовом или двоичном. Если вы щелкнете на форме правой кнопкой мыши, то увидите во всплывшем меню индикатор Text DFM. По умолчанию он включен, и файлы форм сохраняются в текстовом виде. Если выключить этот индикатор, то они будут храниться в двоичном виде.
Чтобы понять содержимое файла .dfm, рассмотрим приведенную ниже последовательность операторов, где представлено текстовое описание формы приложения Primer. Вы можете сравнить этот текст с фрагментами программного кода, приведенными выше.
object Form1: TForm1 Left = 207 Top = 111 Width = 544 Height = 375 Caption = 'Form1' Color = c lBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnClick = FormClick PixelsPerInch = 96 TextHeight = 13 object PrimerButton: TButton Left = 112 Top = 112 Width = 75 Height = 25 Caption = 'Пример' Fo nt.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -13 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] ParentFont = False TabOrder = 0 OnClick = PrimerButtonClick end endВ данном программном коде вы можете заметить, что в текстовом описании имеется два объекта, на разных уровнях. Как можно понять из отступов текста, объект Form1 содержит объект PrimerButton. Каждый объект имеет ряд свойств и некоторые методы, связанные с событиями OnClick. Программисту, опытному в Delphi, приходится работать над этим текстом по ряду причин. Для больших проектов текстовое описание формы является мощным средством документирования, важной формой резервного копирования и хорошим инструментом для поддержки версий.
Компонент CheckBox.
Этот компонент представляет собой независимый переключатель, который используется для того, чтобы указать свое решение типа Да/Нет или Да/Нет/Не знаю. Данное решение отражается в свойстве State компонента, доступном как для чтения, так и для записи. В составе диалогового окна может быть несколько компонентов CheckBox. Состояние любого из них не зависит от состояния остальных, поэтому такие переключатели называются независимыми.
Свойство Alignment определяет положение текста: taLeftJustify — текст с левой стороны от компонента; taRightJustify — текст размещается с правой стороны от компонента.
Свойство AllowGrayed компонента разрешает или запрещает использование состояния cbGrayed (Не знаю).
Свойство Checked компонента содержит выбор типа Да/Нет. Состояния cbUnchecked и cbGrayed отражаются в это время как False.
Свойство State определяет состояние компонента CheckBox как cbUnchecked — нет, cbChecked — да, cbGrayed — не знаю.
Управляющий элемент контрольного индикатора позволяет пользователю выбрать состояние On (включено, отмечено) или Off (выключено, не отмечено) в качестве индивидуального варианта выбора или установки. Когда выбор произведен, в индикаторе имеется метка, она появляется внутри него в виде крестика или галочки. Когда нет выбора, индикатор пуст.
Компонент RadioButton
В отличие от компонента CheckBox, компоненты RadioButton представляют собой зависимые переключатели, предназначенные для выбора одного из нескольких взаимоисключающих решений. На форму или желательно на компонент-контейнер помещается, по меньшей мере, два таких компонента. Они могут находиться в одном из двух состояний, определяемом свойством Checked. Если в одном компоненте это свойство принимает значение True, то во всех других компонентах, расположенных в том же контейнере, свойства Checked принимают значение False.
Управляющий элемент радиокнопка, также известный под названием кнопки выбора опции, отображает единственный вариант выбора из набора взаимоисключающих опций. В любой группе радиокнопок пользователь в данный момент может выбрать одну и только одну кнопку. То есть когда несколько радиокнопок помещены в один и тот же контейнер, типа окна группы, они становятся взаимоисключающими.
Если радиокнопка выбрана вами, то она отображается в виде круга с закрашенной серединой. Невыбранная радиокнопка представляет собой пустой круг. Для изменения ее визуального состояния необходимо присвоить значение True или False свойству Checked.
Вы можете использовать группы радиокнопок, чтобы разрешить пользователю выбирать одно значение из ограниченного набора вариантов, обычно не больше, чем четырех или пяти. Если количество вариантов выбора превышает это число, то вам, возможно, стоит рассмотреть альтернативные способы, которые менее требовательны к размеру области экрана. Например, это компоненты типа окон списков или комбинированных окон.
Компонент RadioGroup.
Он представляет собой просто совокупность из нескольких радиокнопок, размещенных внутри группового окна. Радиогруппа имеет свойство Items, в результате чего требуется только ввод заголовков радиокнопок во время разработки, а их размещение и выравнивание определяется автоматически.
Рассмотрим пример применения радиокнопок.
Форма для этого примера содержит компонент RadioGroup с несколькими радиокнопками, с помощью которых можно выбрать одну из пяти допустимых констант функции MessageBeep.
Программа позволяет воспроизводить звук, соответствующий текущему выбору, когда вы производите щелчок по кнопке с заголовком "Выдача сигнала". Метод обработки события OnClick сначала определяет константу, которая соответствует одной из выбранных радиокнопок, с использованием оператора case, и затем с помощью этой же константы воспроизводит звук. Если на компьютере не установлен подходящий звуковой драйвер, то вы услышите только стандартные сигналы от выбранного элемента.
1. Запустите Delphi.
2. Сохраните файл модуля под именем Signal_pas, а файл проекта — под именем Signal dpr.
3. Поместите на форму компонент RadioGroup со страницы Standard палитры компонентов. Выделите компонент RadioGroup1, перейдите на страницу Properties инспектора объекта. Найдите строку Items, щелкните дважды левой кнопкой мыши справа от слова (TStrings). Откроется редактор String list editor, который позволяет в строки вводить заголовки кнопок. Введите следующие строки: "mb_IconAsterisk", "mb_IconExclamation", "mb_IconHand", "mb_IconQuestion", "mb_OK" (рисунок 3), что будет соответствовать появлению радиокнопок и заголовков на панели RadioGroup1.
4. Поместите кнопку Button1 на форму и, используя событие OnClick для кнопки, напишите следующий код:
procedure TForm1.Button1Click (Sender: TObject); var BeepConstant:Cardinal; begin case RadioGr oup1.ItemIndex of 0: BeepConstant :=mb_IconAsterisk; 1: BeepConstant :=mb_IconExclamation; 2: BeepCo nstant := mb_IconHand; 3: BeepConstant := mb_IconQuestion; 4: BeepConstant := mb_OK else BeepConstan t :=0; end; MessageBeep(BeepConstant); end; end.
Результат работы программы показан на рисунке 4.
Использование компонентов CheckBox можно показать на примере изменения цвета формы путем нажатия левой кнопки мыши над одним из компонентов CheckBox и перетягиванием мышью на форму, не отпуская ее левой кнопки. Для данного примера используем три компонента CheckBox, у которых свойство DragMode в инспекторе объекта на странице Properties установлено в dmAutomatic. Программный код имеет следующий вид.
procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var A ccept: Boolean); begin if CheckBox1.Dragging then Color:=clAqua; if CheckBox2.Dragging then Color:=c lYellow; if CheckBox3.Dragging then Color:=clLime; end;Результат работы программы показан на рисунке 5.
Литература:
1. Марко Канту. Delphi 2 для Windows 95/NT. Москва. ООО "Малип". 1997 г.
2. Джон Матчо. Дэвид Р. Фолкнер. Delphi. Москва. БИНОМ. 1995 г.
3. Эндрю Возневич. Delphi. Освой самостоятельно. Москва. Восточная книжная компания. 1996 г.
4. В.В.Фаронов. Delphi 5. Учебный курс. Москва. Издательство Нолидж. 1998 г.
5. А. Я. Архангельский. Программирование в Delphi 5. Москва. ЗАО "Издательство Бином". 2000 г.
Владимир Скуратов
(c) компьютерная газета
Компьютерная газета. Статья была опубликована в номере 21 за 2001 год в рубрике программирование :: delphi