Доступ к компонентам формыСистематизированный и переработанный материал. Создавая программу в среде Borland C++ Builder программист помещает на форму множество компонент (различные кнопки, меню,таймеры, контейнеры для графики, списки и т.д). Естественно, возникает вопрос - как программно достучаться до того или иного компонента. Ответ прост - по имени компонента. Но, например, когда на один обработчик событий замкнуто несколько элементов, то вопрос о том как определить какой именно компонент вызвал событие становится совсем не таким простым. И, в тоже время, несколько механизмов Borland C++ Builder позволяют выполнить данную и другие, возникающие в практике задачи. Конструкция TObject *Sender (ссылка на объект вызвавший событие) присутствует практически во всех обработчиках собитий компонент C++ Builder и может адресовать любые объекты. Для того, чтобы воспользоваться таким указателем, необходимо выполнить преобразование типа от абстрактного прородителя компонента (TObject) к конкретному. Преобразования можно выполнить двумя способами - статическим в стиле обычного C++ и динамическим (расширенным ANSI). Доступ через статическое преобразование типовПрямое преобразование типаДанный обработчик прячет кнопку, вызвавшую событие ее нажатия:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TButton *ptButton;
ptButton=(TButton*)Sender;
ptButton->Visible=false;
}
Этот обработчик можно назначить сразу нескольким компонентам, но только TButton. При попытке преобразования к другого типу компонента программа выдаст исключение на преобразование типов. Если же необходимо выполнить преобразование из неизвестного класса, то можно использовать конструкции try...catch, но проще воспользоваться динамическим преобразованием - оператором dynamic_cast. Преобразование к TTreeViewСледующий код делает тоже, что и предыдущий (прячет кнопку):
.....
#include <vcl\comctrls.hpp>
......
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TTreeView * ptTreeViev=(TTreeView*) Sender;
ptTreeViev->Visible=false;
}
Преобразование к классу TTreeView позволяет достаточно просто получить любое свойство объекта. Например, пусть обработчики событий OnClick двух кнопок направлены на один код Button1Click(). Для кнопки 2 установим свойство Cursor в crHandPoint.
.....
#include <vcl\comctrls.hpp>
......
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Clear();
TTreeView * ptTreeViev=(TTreeView*) Sender;
Memo1->Lines->Add(ptTreeViev->Name);
Memo1->Lines->Add(ptTreeViev->Align);
Memo1->Lines->Add(ptTreeViev->Left);
Memo1->Lines->Add(ptTreeViev->Cursor);
............
}
Результат: Button1 0 264 0 //Курсор по умолчанию Для кнопки 2: Button2 0 450 -21 //Курсор в виде ладони Доступ через динамическое преобразование типовДинамическое преобразование типов доступно в программе через конструкцию: dynamic_cast<TButton*> Динамическое преобразование позволяет осуществить попытку преобразования некоторого типа объекта к указанному типу и при удаче возвращается значение отличное от нуля. Так, если обработчики нажатия двух кнопок компонента TButton и одной кнопки компонента TSpeedButton замкнуты на один код, то при нажатии кнопок компонентов TButton они будут скрыты, при нажатии кнопки компонента TSpeedButton ничего не произойдет:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TButton* ptButton=dynamic_cast<TButton*>(Sender);
if(ptButton == NULL) return;
ptButton->Visible=false;
}
Если события нескольких одинаковых компонент используют одни и теже функции, то целесообразно их замкнуть на один обработчик, а различать объекты внутри обработчика можно по свойству Tag, присвоив ему разные значения для компонент. Например, пусть у нескольких кнопок компонент TSpeedButton в соответствии с их номером установлено свойство Tag равное номеру кнопки, тогда:
void __fastcall
TForm1::SpeedButton1Click(TObject *Sender)
{
if(dynamic_cast<TSpeedButton *>(Sender))
{
int i=((TSpeedButton *)(Sender))->Tag;
switch(i)
{
case 0:
.... //Код для кнопки 1
break;
case 1:
.... //Код для кнопки 2
break;
}
.......// Общий код
}
}
Посредством конструкции ((класс компонента *)(Sender))-> также можно получить доступ к любому свойству объекта. Доступ по номеру компонента в иерархии объектовВсе компоненты на форме хранятся также в массиве Components. При переносе компонента на форму ему автоматически присваивается сквозной номер в массиве Components,а число компонентов на форме хранится в свойстве ComponentCount. Используя это, можно осуществить доступ к любому компоненту формы. Так, следующая функция ищет среди всех компонент формы компоненты TLabel и среди них компоненту с требуемым номером. После нахождения требуемой компоненты записывает в нее некоторый текст:
void __fastcall
vNewText(int viNumber,AnsiString vasNewText)
{
AnsiString vasName=AnsiString("Label") + IntToStr(viNumber);
for(int i = 0;i < Form1->ComponentCount; i++)
{
if(Form1->Components[i]->ClassNameIs("TLabel"))
{
if(((TLabel *)Form1->Components[i])->Name == vasName)
{
((TLabel *)Form1->Components[i])->Name == vasNewText;
}
}
}
}
Кроме того можно использовать свойство Tag для определения конкретного компонента. Например, пусть на форме имеется 100 панелей, свойство Tag панели определяет номер панели (1-100). При передачи в функцию числа n - все панели с номером большим n исчезнут.
void __fastcall
TForm1::vVisibleFalse(int viNumber)
{
for(int i = 0;i < ComponentCount; i++)
{
if(Components[i]->ClassNameIs("TPanel") &&
Components[i]->Tag > viNumber)
{
if(Components[i]->Tag <= 100)
((TPanel *)Components[i])->Visible=false;
}
else if(Components[i]->ClassNameIs("TPanel") &&
Components[i]->Tag <= viNumber
&& Components[i]->Tag != 0)
((TPanel *)Components[i])->Visible=true;
}
}
Аналогично свойству Tag можно использовать любое свойство, которое имеет компонент. В следующем коде ( кусочек кода из игры "Bricks" в которой изображение может быть разбито на от 4*4 до 10*10 фрагментов) эта конструкция использована для позиционирования TImage в зависимости от его номера и коэффициэнтов, рассчитанных для определения требуемого положения фрагмента изображения:
for(int i = 0;i < ComponentCount; i++)
{
if(Components[i]->ClassNameIs("TImage") && Components[i]->Tag <= viNumX
&& Components[i]->ClassNameIs("TImage") && Components[i]->Tag >= viNumStartX
&& Components[i]->Tag != 0)
{
((TImage *)Components[i])->Left=Components[i]->Tag*viK_X;
((TImage *)Components[i])->Top=Components[i]->Tag*viK_Y;
}
}//for(int i = 0;i < ComponentCount; i++)
Подчиненность объекта и определение его типаИспользуя функцию ClassType() можно определить класс источника события (в примере для компонента TButton), а далее, применяя функцию ClassParent() можно двигаться вверх по иерархии объектов. Этот код размещен здесь по причине того, что метод ClassName() может быть использован для проверки типа объекта перед его использованием.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TClass tClass;
Memo1->Lines->Clear();
tClass = Sender->ClassType();
int i=0;
while(tClass != NULL)
{
Memo1->Lines->Add(String(i)+" "+tClass->ClassName());
tClass = tClass->ClassParent();
i++;
}
}
Результат: 0 TButton 1 TButtonControl 2 TWinControl 3 TControl 4 TComponent 5 TPersistent 6 TObject Пример использования метода ClassType() для проверки источника вызвавшего данное событие:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TClass tClass;
tClass = Sender->ClassType();
AnsiString vasS=tClass->ClassName();
if(vasS == "TButton")
{
TButton *ptButton;
ptButton=(TButton*)Sender;
ptButton->Visible=false;
}
}
|