Отправляет email-рассылки с помощью сервиса Sendsay

Borland C++ Builder - всякая всячина

  Все выпуски  

Borland C++ Builder всякая всячина (Z-координата в семействе Controls)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!

Вот, нашел еще одну интересную темку для разговора. Как известно, компоненты могут быть расположены на форме с частичным перекрытием друг друга. То есть, одни элементы могут быть выдвинуты на передний план, другие - отодвинуты на задний. В конструкторе форм это реализуется через меню Edit (или контекстное меню элемента) Bring to Front и Send to Back. Во время выполнения программы это реализуется через соответствующие функции TControl::BringToFront и SendToBack. Возникает вопрос: а где (каким параметром) определяется такой порядок следования элементов относительно "вертикальной" оси экрана.

Поэкспериментировав немного, я обнаружил, что при изменении z-порядка элементов в конструкторе форм, они меняют свой порядок в списке элементов формы Controls. Для того, чтобы проиллюстрировать это на примере, возьмем пример, описанный мной в предыдущем выпуске рассылки. Он подходит нам по той причине, что позволяет перемещать элементы по форме, что позволит лучше понять принцип наложения элементов управления друг на друга.

Добавьте на форму еще три элемента: список ZListBox (TListBox), в котором мы будем отображать z-порядок элементов, и две кнопки: UpButton и DownButton, с помощью которых мы будем менять порядок наложения элементов друг на друга. Кроме того, постарайтесь, чтобы на форме оказались как минимум пара компонентов, порожденных от TGraphicControl (TLabel, TImage) и пара компонент, порожденных от TWinControl (например, TEdit и TPanel). В описании главной формы в секции private рядом с объявлением вспомогательной переменной TPoint StartPos создайте объявление функции void __fastcall RefreshList(AnsiString AName);, а в основном модуле нарисуйте ее код:

//---------------------------------------------------------------------------
void __fastcall TForm1::RefreshList(AnsiString AName)
{
    ZListBox->Items->Clear();
    int i= 0;
    while (i<Form1->ControlCount)
    {
      int Idx= ZListBox->Items->Add(Form1->Controls[i]->Name);
      if (ZListBox->Items->Strings[i]==AName) ZListBox->ItemIndex= Idx;
      i++;
    }
}
//---------------------------------------------------------------------------

Смысл этого кода такой: он получает в качестве параметра имя элемента, который предполагается сделать текущим, очищает список, затем в цикле перебирает все элементы управления формы (семейство Controls), и последовательно добавляет их имена в список, причем, если имя текущего элемента совпадает с полученным параметром AName, то он устанавливает на него указатель текущего элемента списка TListBox::ItemIndex, полученный от функции TListBox::Items::Add.
Кроме этого вам придется немного "модернизировать" метод Form1::MoveStartDrag:

//---------------------------------------------------------------------------
void __fastcall TForm1::MoveStartDrag(TObject *Sender, TDragObject *&DragObject)
{
    if (dynamic_cast<TControl *>(Sender))
    {
      TControl *Src= dynamic_cast<TControl *>(Sender);
      DragObject= new TDragControlObject(Src);
      StartPos= Src->ScreenToClient(Mouse->CursorPos);
      RefreshList(Src->Name);
    }
}
//---------------------------------------------------------------------------

Вызов метода RefreshList добавлен в этот код для того, чтобы при попытке переноса элемента в другое место, он автоматически выделялся бы в списке элементов формы.

Для кнопок UpButton и DownButton создайте следующие функции-обработчики события OnClick:

//---------------------------------------------------------------------------

void __fastcall TForm1::UpButtonClick(TObject *Sender)
{
    if (ZListBox->ItemIndex!=-1)
    {
      AnsiString AName= ZListBox->Items->Strings[ZListBox->ItemIndex];
      int i= 0;
      while (i<Form1->ControlCount)
      {
        if (Form1->Controls[i]->Name==AName) break;
        else i++;
      }
      if (i<Form1->ControlCount)
      {
        Form1->Controls[i]->BringToFront();
        RefreshList(AName);
      }
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::DownButtonClick(TObject *Sender)
{
    if (ZListBox->ItemIndex!=-1)
    {
      AnsiString AName= ZListBox->Items->Strings[ZListBox->ItemIndex];
      int i= 0;
      while (i<Form1->ControlCount)
      {
        if (Form1->Controls[i]->Name==AName) break;
        else i++;
      }
      if (i<Form1->ControlCount)
      {
        Form1->Controls[i]->SendToBack();
        RefreshList(AName);
      }
    }
}
//---------------------------------------------------------------------------

Нажатие на одну из этих кнопок приводит к выполнению следующей последовательности команд: во-первых, с помощью свойства TListBox::ItemIndex проверяется, выделен ли какой-нибудь элемент в списке, имя этого элемента сохраняется в переменной AName. Затем в цикле перебираются все компоненты формы, и, если будет найден компонент с именем, совпадающим с хранящимся в AName, он с помощью функции TControl::SendToBack() (или TControl::BringToFront()), передвигается на передний или на задний планы. И, наконец, для обновления списка вызывается метод RefreshList() с именем передвинутого компонента.

Желательно также создать обработчик для свойства OnActivate формы, в котором вызвать метод RefreshList(""); для того, чтобы при появлении формы на экране в списке уже находились имена всех элементов формы.

Попробуйте запустить этот пример и поэкспериментировать с перемещением элементов по форме и их перекрытием относительно друг друга. Обратите внимание, что элемент, например, TLablel не может быть вынесен выше элемента, скажем, TEdit, и наоборот. Это связано с тем, что в секции private класса TWinControl все дочерние элементы хранятся в двух списках: порожденные от TWinControl - в списке FWinControls а все остальные - в списке FControls. И менять свое местоположение могут только в пределах этих списков. Кроме того, метод TWinControl::FindChildControl() производит поиск только в вышеупомянутом списке FWinControls, поэтому нам пришлось в методах UpButtonClick и DownButtonClick задействовать полный перебор в цикле всех элементов формы.

Итак, резюме. Я отдаю себе отчет, что тема сегодняшнего выпуска не несет особой практической пользы, и разобрал эту проблему только для того, чтобы самому лучше понять механизм работы VCL, поскольку знание механизма работы библиотеки может избавить от многих "непоняток" при разработке своих компонент. Вообще, почаще заглядывайте в исходники VCL, содержащие порой шокирующую информацию, коренным образом меняющую представление о ее внутренней структуре.



С уважением, Васильев Евгений...

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное