Совершенствование компонентов
Как вы видели в предыдущем параграфе, на практике нередко нужны поля ввода особого типа, с помощью которых можно вводить целые числа. Стандартный компонент TEdit разрешает вводить любые символы и представляет результат ввода как текстовое свойство Text. Поэтому для того, чтобы получить нужное нам поведение (ввод целых чисел), мы:
• добавили обработчик OnKeyPress, заблокировав ошибочные символы;
• для перевода текстовой строки в число каждый раз использовали функцию StrToInt.
Если такие поля ввода нужны часто и в разных программах, можно избавиться от этих рутинных операций. Для этого создаётся новый компонент, который обладает всеми необходимыми свойствами.
Конечно, можно создавать компонент «с нуля», но так почти никто не делает. Обычно задача сводится к тому, чтобы как-то улучшить существующий стандартный компонент, который уже есть в библиотеке Lazarus. Мы будем совершенствовать компонент TEdit (поле ввода), поэтому, согласно принципам ООП, наш компонент (назовём его TIntEdit) будет наследником класса TEdit, а класс TEdit будет соответственно базовым классом для нового класса TIntEdit:
type TIntEdit=class(TEdit) ... end;
Изменения стандартного класса TEdit сводятся к двум пунктам:
• все некорректные символы (кроме цифр и кода клавиши Backspace) должны блокироваться автоматически, без установки дополнительных обработчиков событий;
• компонент должен уметь сообщать числовое значение; для этого мы добавим к нему свойство Value (в переводе с англ. — значение) целого типа.
Таким образом, описание нового класса выглядит так:
type TIntEdit = class(TEdit) private function GetValue: integer; procedure SetValue(Val: integer); protected procedure KeyPress(var Key: Char); override; public property Value: integer read GetValue write SetValue; end;
Из предыдущего материала (см. § 49) вам должно быть понятно, что для чтения введённого числового значения используется закрытый метод GetValue, а для записи — закрытый метод SetValue. Эти методы могут выглядеть так:
function TIntEdit.GetValue: integer; begin try Result:=StrToInt(Text); except Result:=0; end; end; procedure TIntEdit.SetValue(Val: integer); begin Text:=IntToStr(Val); end;
Для преобразования целых чисел из текстового формата в числовой используется функция StrToInt, а для обратного преобразования — функция IntToStr. В метод GetValue введена защита — в случае ошибки функция возвращает 0.
Для обработки вводимых символов будем использовать метод KeyPress. Этот метод не новый, он есть и у базового класса TEdit. Слово override говорит о том, что класс-наследник переопределяет этот метод базового класса TEdit. Если посмотреть в исходные тексты библиотеки Lazarus, метод KeyPress состоит из одной строчки — он вызывает обработчик события OnKeyPress, установленный пользователем. Мы переопределим этот метод так:
procedure TIntEdit.KeyPress(var Key: Char); begin if not (Key in ['O'..'9', #8]) then Key:=#0; inherited; end;
В первой строке происходит блокировка неверных символов, а во второй с помощью команды inherited вызывается перекрытый метод базового класса. Поэтому если пользователь компонента TIntEdit установит обработчик OnKeyPress, он будет успешно вызван, но уже после того, как введённый символ обработает наша процедура.
Готовый компонент лучше всего поместить в отдельный модуль, назовем его int edit. В среде Lazarus для создания нового модуля нужно выбрать команду меню Файл — Создать модуль. Описание класса размещается в секции interface, а сами методы — в секции implementation. Кроме того, в список используемых модулей (после слова uses) нужно добавить модуль StdCtrls, в котором описан класс TEdit.
Создадим новый проект и сразу добавим в список используемых модулей новый модуль int_edit. Эта программа будет переводить целые числа из десятичной системы в шестнадцатеричную (рис. 7.21).
Рис. 7.21
Поместим на форму метку hexLabel для вывода шестнадцатеричного значения.
Теперь нужно добавить на форму новый компонент класса TIntEdit, но проблема состоит в том, что его нет в палитре компонентов! В этом случае компонент можно добавить при выполнении программы, и все его свойства придётся настраивать вручную, в программе.
Сначала добавим в описание формы переменную типа TIntEdit:
TForml=class(TForm) ... decEdit: TIntEdit; end;
Как вы знаете, это ещё не поле ввода, а ссылка — переменная, в которой можно хранить адрес какого-нибудь поля ввода. Создавать сам объект удобнее всего в обработчике события OnCreate формы (он будет вызван при создании формы в самом начале работы программы):
procedure TForml.FormCreate(Sender: TObject); begin decEdit:=TIntEdit.Create(Self); decEdit.Text:='100'; decEdit.Left:=6; decEdit.Top:=6; decEdit.Width:=115; decEdit.Parent:=Self ; end;
В первой строке создаётся новый компонент, и его адрес записывается в поле decEdit. Слово Self при вызове конструктора Create означает «этот объект». Поскольку метод FormCreate — это метод формы, в данном случае этот объект — сама форма. Такой вызов конструктора говорит о том, что владельцем (англ. owner) нового компонента будет форма, и когда форма будет удалена из памяти, вместе с ней будет уничтожен и компонент decEdit. В следующих строках устанавливаются начальные значения для свойств компонента (Text — содержимое, Left и Тор — координаты левого верхнего угла, Width — ширина).
В последней строчке мы меняем свойство Parent (в переводе с англ. — родитель), записывая в него адрес формы (Self). Это очень важно, потому что «родитель» отвечает за показ всех «дочерних объектов» на экране; если этого не сделать, то поле ввода останется невидимым.
Остаётся определить для нового компонента обработчик события OnChange: при изменении содержимого поля ввода нужно показать соответствующее шестнадцатеричное число с помощью метки hexLabel. Сначала создадим сам обработчик. Вручную добавим в описание формы заголовок процедуры:
procedure decEditChange(Sender: TObject);
а в секцию implementation — её текст:
procedure TForml.decEditChange(Sender: TObject); begin hexLabel.Caption:=IntToHex(decEdit.Value,1); end;
Сначала мы запрашиваем числовое (!) значение у поля ввода, используя новое свойство Value, а затем переводим его в шестнадцатеричную систему счисления с помощью функции IntToHex. Её второй параметр — количество шестнадцатеричных цифр; если этот параметр равен 1, выбирается минимально возможное количество цифр (без лидирующих нулей).
Теперь нужно как-то подключить этот обработчик к вновь созданному компоненту. Для этого нельзя использовать Инспектор объектов, потому что в режиме разработки нашего компонента на форме нет. Но существует другой способ — в программе записать в свойство OnChange (оно наследуется от базового класса TEdit) адрес процедуры обработки события. Это нужно сделать при создании формы, добавив в конец обработчика OnCreate строку
decEdit.OnChange:=0decEditChange;
Символ @ перед именем процедуры обозначает её адрес.
Теперь программа готова и её можно запустить. Фактически мы вручную сделали все операции, которые обычно выполняет среда Lazarus, если компонент добавляется на форму из палитры компонентов. Установить компонент в палитру можно с помощью меню Пакет, но это выходит за рамки нашего курса.
Следующая страница Вопросы и задания