Головна |
Якщо поруч з компонентом TListAdd помістити компонент TPanel і порівняти показуване в інспектора об'єктів, то можна відзначити, що для панелі експонується досить велика кількість властивостей і подій, а для TListAdd - тільки кілька якостей. Тим часом клас TCustomPanel є предком обох компонентів. Для того щоб зрозуміти причину, відкриємо модуль ExtCtrls.pas і розглянемо різницю між класами TCustomPanel і TPanel. Можна відзначити, що всі методи і змінні, які забезпечують функціональність панелі, визначені на рівні класу TCustomPanel. У ньому ж визначено і властивості, які потім відображаються в інспектора об'єктів для TPanel, тільки ці властивості визначені в секції Protected. Реалізація ж класу TPanel надзвичайно проста: як предка визначається TCustomPanel, і властивості цього класу редекларіруются, але вже в секції published. Стає зрозуміло, що необхідно зробити в класі TListAdd для появи в інспектора об'єктів властивостей і методів класу TcustomPanel, а саме редекларіровать властивості. У секції published класу TListAdd запишемо:
property Align; property OnMouseDown;При редеклараціі властивості не потрібно вказувати його тип і посилатися на змінні або методи читання або запису властивості. Після компіляції компонента через редактор пакетів в інспектора об'єктів можна спостерігати появу властивості Align і події OnMouseDown. Таким чином, для нащадків TCustom ...- класів програміст має можливість вибирати, які властивості і події слід відображати в інспектора об'єктів, а які ні. Саме з цієї причини TCustom ...- класи рекомендується використовувати в якості предків для створення компонентів.
Тепер розглянемо, як можна ввести нову властивість (то, що ми робили вище -редекларація вже наявних властивостей). Як потрібної якості для відображення в інспектора об'єктів можна використовувати текст на кнопці: нехай програміст, який користується компонентом TListAdd, самостійно змінює текст на етапі розробки. Спроба ввести нову властивість (назвемо його BtCaption) за допомогою оголошення:
property BtCaption: string read FButton.Caption write FButton.Caption;призводить до помилки при спробі компіляції компонента. Тому визначимо заголовки двох методів в секції private:
function GetBtCaption: string; procedure SetBtCaption (const Value: string);У секції published оголосимо властивість BtCaption:
property BtCaption: string read GetBtCaption write SetBtCaption;І нарешті, реалізуємо два оголошених методу в секції реалізації:
function TListAdd.GetBtCaption: string; begin Result: = FButton.Caption; end; procedure TListAdd.SetBtCaption (const Value: string); begin FButton.Caption: = Value; end;Після компіляції компонента за допомогою редактора пакетів в інспектора об'єктів з'являється нова властивість. Зміна значення цієї властивості відбивається прямо на етапі розробки.
Тепер визначимо нову подію. У цьому завданню було б розумним створити подія, що дозволяє програмісту, що використовує такий компонент, аналізувати текст перед занесенням вмісту редактора в список і дозволити або заборонити додавання тексту в список. Отже, цей метод зобов'язаний як параметр містити поточне значення тексту в редакторі і залежати від логічної змінної, якій програміст може привласнити значення True або False. Крім того, будь-який обробник події в компоненті зобов'язаний залежати від параметра Sender, в якому викликає його компонент передає посилання на самого себе. Це необхідно робити тому, що в середовищі розробки Delphi один і той же обробник події може викликатися з декількох різних компонентів і програміст повинен мати можливість проаналізувати, який саме компонент викликав обробник. Отже, після слова type в секції interface перед визначенням TListAdd визначаємо новий тип методу:
type
TFilterEvent = procedure (Sender: TObject; const EditText: string; var CanAdd: boolean) of object;
Далі визначаємо змінну цього типу в секції private:
FOnFilter: TFilterEvent;
І в секції published визначаємо властивість даного типу:
Відкрити в окремому вікні Копіювати Друк?
property OnFilter: TFilterEvent read FOnFilter write FOnFilter;
При визначенні нового властивості посилаємося на змінну FOnFilter, а не на методи - вони тут не потрібні. Тепер, якщо скомпілювати компонент за допомогою редактора пакетів, можна виявити появу в інспектора об'єктів події OnFilter. Однак якщо ми призначимо йому обробник і запустимо проект на виконання, то він може не викликатися. Це відбувається тому, що ми ніде його не спричинили в нашому компоненті. Відповідне місце для виклику події OnFilter - обробник події OnClick для FButton, який вже реалізований. Тому ми змінимо код реалізації раніше визначеного методу BtClick:
1. procedure TListAdd.BtClick (Sender: TObject);
2. var
3. CanAdd: boolean;
4. begin
5. if length (FEdit.Text)> 0 then begin
6. CanAdd: = True;
7. if Assigned (FOnFilter) then FOnFilter (Self, FEdit.Text, CanAdd);
8. if CanAdd then begin
9. FListBox.Items.Add (FEdit.Text);
10. FEdit.Text: = '';
11. FEdit.SetFocus;
12. end else beep;
13. end;
14. end;
Отже, в наведеному вище фрагменті коду визначається логічна змінна CanAdd. При написанні коду слід враховувати, що програміст може не зробити обробник події OnFilter. Тому встановлюємо значення змінної CanAdd за замовчуванням рівним True - все рядки додавати в список. Далі, перед викликом FonFilter, слід перевірити, а чи зробив програміст обробник події. Це досягається викликом методу Assigned, який повертає логічне значення. Для покажчика виклик методу Assigned еквівалентний перевірці P nil. Для методу об'єкта ми не можемо використовувати перевірку FOnFilter nil, так як метод об'єкта характеризується двома адресами і така перевірка не буде дозволена компілятором. Але виклик методу Assigned прекрасно перевіряє, чи був зроблений обробник події. Вищенаведений код - абсолютно стандартний спосіб виклику обробника подій з компонента.
Залишилося протестувати обробник події. Помістимо два компонента TListAdd на форму, для одного дозволимо додавання тільки цілих чисел, а для іншого - тільки слів, що починаються з прописних англійських букв. Відповідно код для обробників подій OnFilter буде виглядати наступним чином:
1. procedure TForm1.ListAdd1Filter (Sender: TObject; const EditText: String;
2. var CanAdd: Boolean);
3. var
4. I, N: integer;
5. begin
6. Val (EditText, N, I);
7. CanAdd: = I = 0;
8. end;
9.
10. procedure TForm1.ListAdd2Filter (Sender: TObject; const EditText: String;
11. var CanAdd: Boolean);
12. begin
13. CanAdd: = False;
14. if length (EditText)> 0 then CanAdd: = (EditText [1]> = 'A') and (EditText [1] <= 'Z');
15. end;
Код простий для розуміння, єдиним його нюансом є перевірка того, що текст являє собою не порожній рядок, перед перевіркою першої літери тексту в обробнику події ListAdd2Filter. Проведення такої перевірки обов'язково: рядки в Object Pascal - це об'єкти, і порожній рядку відповідає nil-покажчик. При спробі перевірити першу букву порожнього рядка додаток спробує дереференсіровать nil, що призведе до виникнення виключення. В даному випадку це не страшно: перед викликом обробника подій FOnFilter з компонента TListAdd перевіряється рядок на ненульову довжину. Однак для компонентів, вихідний текст яких вам недоступний, така перевірка є обов'язковою!
Створення складного компонента | Приховування властивостей в інспекторові об'єктів
Тема 4.11 Програмування динамічно підключаються бібліотек | Аргументи на користь використання DLL | Основи розробки DLL | Експорт функцій з DLL | Використання DLLProc | Завантаження DLL | Виклик процедур і функцій, завантажених з DLL. | пакети компонентів | шаблони компонентів | Створення найпростішого компонента |