Си - Статьи - Перечисление компонентов

Есть несколько вопросов, которые всплывают снова и снова
в ньюсгруппах Borland. Один из них (в различных вариациях) - "Как
мне очистить содержимое всех элементов редактирования на форме?".
Этот вопрос типичен для программистов, перешедших с Visual Basic '
a на C++Builder. Эти программисты привыкли использовать массив компонентов
в Visual Basic 'е и хотят таким же образом работать в C++Builder'е.
Эта статья объяснит, каким образом использовать свойство Components
VCL для перечисления всех компонентов на форме. Я также покажу вам,
как использовать оператор dynamic_cast для определения типа каждого
компонента на форме.


Массив компонентов


У класса TForm есть два свойства, которые могут
быть использованы для перечисления компонентов формы. Свойство ComponentCount
является свойством только для чтения и возвращает количество компонентов,
которыми владеет форма. Это количество включает в себя как компоненты,
помещенные на форму во время разработки, так и компоненты (визуальные
и невизуальные), созданные во время выполнения. Свойство Components
является свойством-массивом. Оно содержит указатель на каждый компонент,
которым владеет форма. Если вы посмотрите на объявление свойства Components,
вы обнаружите, что оно содержит список указателей на TComponent.
TComponent, в конечном счете, является базовым классом для всех
компонентов. В своем коде вы можете привести указатель к любому нужному
вам типу для доступа к свойствам и методам конкретного типа компонента.
Я объясню вам, как это сделать, чуть ниже.


Массив компонентов, представленный свойством Components,
используется внутри VCL. Среди всего прочего, данный массив используется
VCL при удалении всех объектов формы, что обычно происходит при ее удалении.


Перечисление компонентов формы


Компоненты формы могут быть перечислены простым циклом
for. Следующий код перечисляет компоненты, расположенные на форме
и помещает их имена в многострочный элемент редактирования (memo).

Memo1->Lines->Clear();

 

for(int I = 0; i < ComponentCount; i++)

 {

   String S = Components->Name;

   Memo1->Lines->Add(S);

 

 }


Этот код говорит сам за себя. Цикл начинается с нуля и останавливается
при достижении ComponentCount минус единица. Свойство Name
каждого компонента прочитывается и заносится в многострочный элемент
редактирования. Как принято на практике, мы упростим код, сделав его легче
для понимания. В большинстве случаев вы будете писать код, подобный следующему:


for(int i = 0; i < ComponentCount; i++)

 Memo1->Lines->Add(Components->Name);


Помните, что динамически созданные компоненты будут иметь
пустое свойство Name , если вы явно не присвоили свойству значение
при создании компонента. Например:

TLabel* label = new TLabel(this);

label->Top = 20;

label->Left = 20;

label->Parent = this;

label->Caption = "Hello";
 

В этом случае свойству Name не присвоено
 никакого значения, и оно будет содержать пустую
 строку. Вы можете непосредственно использовать
 указатель, предоставленный свойством Components, если вам необходимо получить
 доступ к свойствам компонента, которые находятся в
 классе TComponent. Например, допустим, что вам
 необходимо перечислить только те компоненты,
 свойство Tag которых установлено в единицу. Код, выполняющий данное перечисление, будет
 выглядеть следующим образом:
for(int I = 0; i < ComponentCount; i++)
 if(Components->Tag == 1)
 Memo1->Lines->Add(Components->Name);
В этом коде только те компоненты, значение свойства
Tag которых установлено в единицу, добавляются в многострочный элемент
редактирования. Если вы хотите получить доступ к свойствам конкретного
типа компонента (например, TEdit), вам необходимо привести к нему
указатель на TComponent. Сейчас мы поговорим об этом.

 

Приведение указателя на TComponent


В реальном приложении, вам, вероятнее всего, будет необходимо
привести указатель из массива Components , чтобы получить доступ
к индивидуальным свойствам конкретного типа компонента. Давайте предположим,
что вам, например, необходимо очистить текст во всех элементах редактирования
на форме. В данной ситуации вам необходимо привести указатель на TComponent
к указателю на TEdit. Это можно сделать двумя способами:
правильным и неправильным.


Неправильный способ


Неправильным способом является использование приведение
в стиле С. Этот код иллюстрирует неправильный метод приведения указателя.

for(int i = 0; i < ComponentCount; i++)

 ((TEdit*)Components)->Text = "";


Этот код будет работать в том, и только в том случае, если
все компоненты на форме являются экземплярами класса TEdit. Если
компонент не является экземпляром класса TEdit, то он будет перечислен,
но почти всегда будет сгенерировано "access violation" при попытке
обращения к свойству Text. Учитывая это, вы никогда не должны
использовать приведение указателя в стиле С в этом случае. (В действительности,
я считаю, что вы никогда не должны использовать приведение указателя в
стиле С в программе на С++).


Правильный способ


Правильным способом приведения указателей является использование
оператора dynamic_cast . Оператор dynamic_cast вернет
указатель на объект, если приведение может быть выполнено, или нуль, если
приведение не может быть выполнено. Базовой идеей при перечислении компонентов
на форме является приведение каждого компонента к тому типу, который вам
нужен, и последующий анализ, закончилось ли приведение успехом или неудачей.
В случае неудачи компонент не принадлежит тому типу, который вам нужен.
В случае успешного приведения вы затем можете использовать указатель,
возвращенный оператором dynamic_cast , для выполнения каких-либо
действий.


Очистка текста во всех элементах редактирования

Теперь давайте посмотрим, как dynamic_cast может
быть использован для очистки текста во всех элементах редактирования на
форме. Вот необходимый код:


for(int i = 0; i < ComponentCount; i++)

 {

  TEdit* edit;

  edit = dynamic_cast(Components);

 

  if(edit)

  edit->Text = "";

 }


Каждый компонент в массиве Components приводится
к TEdit. Если приведение завершается неудачей, то цикл продолжается
без дальнейшей обработки. Если приведение завершается успешно, тогда мы
можем использовать указатель, возвращенный dynamic_cast ,
для установки значения свойства Text в пустую строку. Давайте
посмотрим на другой пример. Этот код делает жирным шрифт всех меток на
форме:

for(int i = 0; i < ComponentCount; i++)

 {

  TLabel* label = dynamic_cast(Components); 

   if (label)

        label->Font->Style = TFontStyles() << fsBold;

 }


Этот код также иллюстрирует правильный способ установки
стиля шрифта.


Количество вызовов dynamic_cast, которые вы можете
помещать внутрь цикла, не ограничено. Приведенный ниже код, например,
объединяет два вызова оператора dynamic_cast:

for(int i = 0; i < ComponentCount; i++)

 {

  TEdit* edit = dynamic_cast(Components);

   if(edit)

    edit->Text = "";

    TLabel* label = dynamic_cast(Components);

   

   if(label)

    label->Font->Style = TFontStyles() << fsBold;

 }


Если найден элемент редактирования, TEdit, то очищается
его содержимое. А если найдена метка, TLabel, то изменяется стиль
ее текста. Все остальные компоненты на форме игнорируются.


Заключение


Перечисление компонентов на форме является тривиальной задачей,
как только вы поняли принцип работы со свойством Components. Использование
dynamic_cast для приведения указателя из массива указателей,
представленного свойством Components, дает вам полный контроль
над всеми компонентами формы.

Автор: Kent Reisdorph. Перевод: Т. Сорока. По материалам: www.bcbdev.ru.
Copyright © 2006-09.