Доступ к компонентам формыСистематизированный и переработанный материал. Создавая программу в среде 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; } } |