Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Теллес М. - Borland C++ Builder. Библиотека программиста - 1998

.pdf
Скачиваний:
764
Добавлен:
13.08.2013
Размер:
4.35 Mб
Скачать

Borland C++ Builder (+CD). Библиотека программиста 61

«спрятанными» (невидимыми) компонентами на форме. Так называемые динамические управляющие элементы вещь, которую достаточно просто сделать в дизайнере форм. Просто добавляете на форму компонент в том месте, где вы хотите, чтобы он позже появился, а потом устанавливаете его свойство Visible («видимый») в false (ложь). Когда вам нужно, чтобы компонент появился на форме, устанавливаете свойство Visible в true (истина) и он становится видимым и доступным пользователю. Однако это не то, что нужно некоторым программистам. Хотя в девяти случаях из десяти этого достаточно, иногда все же вам действительно нужно создавать для пользователя компонент динамически во время работы приложения. В этои примере мы предоставляем возможность создавать компонент не одного, а трех различных типов «на ходу». В добавление к этому вы увидите, как разобраться с обработкой сообщений для динамически созданных компонентов.

Замечание

Вы найдете полный исходный код для программы «Динамические компоненты» на сопроводительном компакт-диске в каталоге Chapter4\DynControl1.

На рис. 4.1 представлена форма, с которой мы будем работать в этом приложении. Создайте простую форму и притащите на нее три переключателя (компонент TRadioButton) и одно поле ввода (компонент TEdit). Разместите их примерно так, как на рисунке.

Рис. 4.1. Форма приложения «Динамические компоненты»

Во-первых, нам нужно изменить заголовочный файл для формы, чтобы он содержал описания компонентов, которые мы будем создавать. В данном примере мы будем создавать компоненты трех различных типов. Нам нужны описания для статического текста (метки), поля ввода и кнопки. Вот изменения, которые нужны для данного примера (выделены подсветкой):

#ifndef Unit1H #define Unit1H

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

#include <vcl\Classes.hpp> #include <vcl\Controls.hpp> #include <vcl\StdCtrls.hpp> #include <vcl\Forms.hpp>

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

class TForm1 : public TForm

Borland C++ Builder (+CD). Библиотека программиста 62

{

__published: // IDE-managed components TRadioButton *RadioButton1; TRadioButton *RadioButton2;

TEdit *Edit1;

TRadioButton *RadioButton3;

void __fastcall RadioButton1Click(TObject *Sender); void __fastcall RadioButton2Click(TObject *Sender); void __fastcall RadioButton3Click(TObject *Sender);

private: // User declarations TEdit *FpEdit;

TLabel *FpLabel;

TButton *FpButton;

void RemoveExistingFields(void);

void __fastcall OnButtonClick( TObject *Sender ); public: // User declarations

__fastcall TForm1(TComponent *Owner);

}; //--------------------------------------------------------

extern TForm1 *Form1; //--------------------------------------------------------

#endif

Три указателя на компоненты будут использованы для создания компонентов «на ходу». Метод RemoveExistingFields нам нужен, чтобы избавиться от предыдущего созданного компонента и убрать его с экрана. И, наконец, метод OnButtonClick будет использован для динамической обработки события нажатия на кнопку.

Во-первых, нам надо инициализировать все указатели на компоненты, чтобы знать, какой из них активен, а какие нет. Добавьте следующий код в конструктор класса:

__fastcall TForm1::TForm1(TComponent *Owner) : TForm(Owner)

{

FpEdit = NULL;

FpLabel = NULL;

FpEdit = NULL;

}

Здесь мы просто устанавливаем указатели в NULL (нуль). Это некорректное значение указателя означает, что компонент не создан. Это важно для нас, так как мы планируем создавать компоненты в одном и том же месте формы. Если не следить за тем, какой из них активен, то может случиться, что они все будут одновременно присутствовать и будут друг друга перекрывать, что приведет к каше на экране.

Создаем поле ввода

Первый из компонентов, которые мы хотим динамически создать это поле ввода. Когда пользователь выбирает кнопкой мыши переключатель Создать новое поле ввода, то на форме появится динамически созданное поле ввода. Создайте обработчик события OnClick (выбор, нажатие) для этого переключателя и добавьте в него следующий код. Когда вы закончите набирать этот код (или просматривать код на компакт-диске), мы обсудим, что это все означает:

Borland C++ Builder (+CD). Библиотека программиста 63

void __fastcall TForm1::RadioButton1Click(TObject *Sender)

{

RemoveExistingFields();

FpEdit = new TEdit(this);

FpEdit->Parent = this;

FpEdit->Left = RadioButton1->Left;

FpEdit->Width = 200;

FpEdit->Height = 20;

FpEdit->Top =

RadioButton3->Top + RadioButton3->Height + 20;

FpEdit->Visible = true;

}

Предыдущий пример, во-первых, удаляет все существующие компоненты, вызывая метод RemoveExistingFields. Мы рассмотрим его чуть позже. Во-вторых, создает новый компонент TEdit (поле ввода) через оператор new. Компоненты VCL могут быть созданы только через оператор new. Вы не можете просто определить новый компонент VCL, например так:

TEdit mEdit(this); // Это не сработает!

Это ограничение наложено компанией Borland на VCL; оно необходимо, так как библиотека VCL была написана на Pascal и поэтому, чтобы все работало как надо, нужны определенные закулисные манипуляции.

Как только компонент создан через оператор new (при этом владелец (owner) компонента передается как параметр конструктору), следующим важным шагом является установка свойства Parent (родитель) компонента. Свойство Parent существенно для определения, где компонент должен находиться. Если это свойство не установить, то компонент никогда не появится на экране, так как его свойства задаются относительно родителя. Если вы динамически создали компонент и он не появляется, то первым делом проверьте установку свойства Parent перед тем, как искать где-либо еще.

Когда свойство Parent установлено, следующие четыре строки кода просто позиционируют поле ввода на форме. В данном случае мы хотим, чтобы поле ввода появилось снизу от последнего переключателя (radio button), так что мы устанавливаем позиционные свойства поля ввода через координаты RadioButton3 (третьего переключателя), и все становится хорошо.

Последний шаг установка свойства Visible (видимый) в логическое значение true (истина), чтобы компонент был виден пользователю. Строго говоря, это необязательно, так как почти для всех компонентов значение свойства Visible по умолчанию есть true. Однако стоит привыкать делать именно так, потому что трудно строить догадки о том, как все это будет работать в будущем.

Чтобы наша программа скомпилировалась, нам надо добавить метод RemoveExistingFields. Вот нужный код:

void TForm1::RemoveExistingFields(void)

{

if ( FpEdit )

{

delete FpEdit; FpEdit = NULL;

Borland C++ Builder (+CD). Библиотека программиста 64

}

if ( FpLabel )

{

delete FpLabel; FpLabel = NULL;

}

if ( FpButton )

{

delete FpButton; FpButton = NULL;

}

}

Как видите, все что мы тут делаем, это удаляем те компоненты, указатели на которые не равны NULL, и ставим их в NULL. Вот почему важно было инициализировать значения указателей в конструкторе формы. Таким образом мы определяем один-единственный компонент и потом удаляем его с формы.

Теперь вы уже сделали достаточно, чтобы скомпилировать и собрать программу. Вам нужно временно закомментировать метод OnButtonClick в заголовочном файле, а потом выбрать команду Project ä Make (или нажать Ctrl+F9) для запуска компиляции и сборки проекта. Запустите полученную программу и щелкните мышью на первом переключателе (создание поля ввода). Вы увидите форму, изображенную на рис. 4.2.

Рис. 4.2. Форма с новым динамическим полем ввода

Добавление статического текста

Создание поля статического текста (метки) очень похоже на создание поля ввода, за исключением одной небольшой детали. Метка, в конце концов, должна содержать какой-нибудь текст. Здесь мы будем использовать тот текст, который пользователь введет в поле ввода, расположенное справа от переключателя Создать новую метку. Этот текст будет использован для свойства Caption поля статического текста (метки).

Borland C++ Builder (+CD). Библиотека программиста 65

Создайте обработчик события OnClick переключателя Создать новую метку и добавьте в него следующий код:

void __fastcall TForm1::RadioButton2Click(TObject *Sender)

{

RemoveExistingFields();

FpLabel = new TLabel(this); FpLabel->Parent = this; FpLabel->Left = RadioButton1->Left; FpLabel->Width = 200; FpLabel->Height = 20; FpLabel->Top =

RadioButton3->Top + RadioButton3->Height + 20; FpLabel->Visible = true;

FpLabel->Caption = Edit1->Text;

}

Как видите, код этого обработчика почти в точности повторяет код для создания компонента поля ввода. Все компоненты создаются в основном одинаковым образом, отличия есть только в установке индивидуальных свойств. В нашем случае свойство Caption (заголовок, текст) метки получит свое значение из поля ввода (рядом с переключателем). Чтобы проверить это, скомпилируйте и запустите программу. Введите какой-нибудь текст в поле ввода, например, Это динамическая метка. Установив переключатель Создать новую метку, вы получите результат, представленный на рис. 4.3, с новым полем статического текста, расположенным там, где недавно было поле ввода. Метка будет содержать текст «Это динамическая метка» (или другой, введенный вами в поле ввода).

Рис. 4.3. Форма с новой динамической меткой

Добавление кнопки

Последний штрих в этом примере добавление на форму новой кнопки. Создание кнопки не отличается от создания метки или поля ввода. Но нам хотелось бы знать о том, что пользователь нажимает кнопку. Для этого нам нужно уметь создавать обработчик события нажатия на кнопку и ассоциировать этот обработчик с событием OnClick.

Borland C++ Builder (+CD). Библиотека программиста 66

Object Inspector, конечно, не работает для динамически созданных компонентов. Как же мы тогда ассоциируем обработчик с кнопкой? Ну, если вы помните пример Scribble3 из главы 1, мы можем модифицировать обработчик для компонента во время работы приложения. Если можно модифицировать, почему бы не добавить новый обработчик? Противопоказаний к этому нет.

Создайте обработчик события OnClick для переключателя Создать новую кнопку и добавьте в обработчик следующий код :

void __fastcall TForm1::RadioButton3Click(TObject *Sender)

{

RemoveExistingFields();

FpButton = new TButton(this); FpButton->Parent = this; FpButton->Left = RadioButton1->Left; FpButton->Width = 200; FpButton->Height = 20; FpButton->Top =

RadioButton3->Top + RadioButton3->Height + 20; FpButton->Visible = true;

FpButton->Caption = "Кнопка 1"; FpButton->OnClick = OnButtonClick;

}

Опять же, почти никакой разницы между случаем кнопки и предыдущими случаями. Мы устанавливаем свойства (включая важнейшее свойство Parent) таким же образом, как и для поля ввода и метки. Для кнопок нам нужно свойство Caption: это текст, который появляется на кнопке. В этом случае мы просто присваиваем этому свойству строку «Кнопка 1».

Подсвеченная строка кода важна для кнопки. Здесь мы присваиваем наш обработчик события для кнопки событию OnClick объекта-кнопки. Единственное требование к обработчику чтобы он был нужной сигнатуры (в смысле аргументов и значения, возвращаемого функцией) для события данного типа. Для события OnClick метод должен принимать один аргумент (Sender, то есть отправитель сообщения, типа TObject) и не возвращать никакого значения (то есть возвращать «пустое значение», void). Так что мы реализуем простой обработчик, вводя следующий код в исходный файл для формы:

void __fastcall TForm1::OnButtonClick( TObject *Sender )

{

MessageBox(NULL, "Вы нажали на кнопку!", "Информация", MB_OK);

}

Заметьте, что сигнатура этого обработчика события подходит под сигнатуру, требуемую для обработчика события нажатия на кнопку. Заметьте также, что у обработчика указан модификатор __fastcall. Этот модификатор требуется указывать во всех переопределениях (overrides) методов VCL. По определению обработчик события является переопределением поведения компонента и поэтому требует директивы __fastcall.

Не забудьте раскомментировать строку из заголовочного файла с прототипом нашего метода OnButtonClick, а потом скомпилируйте и запустите программу. Когда вы запустите программу и установите переключатель Создать новую кнопку, то длинная тощая кнопка появится на форме. Нажав на нее, вы получите окно с сообщением, как на рис. 4.4.

Borland C++ Builder (+CD). Библиотека программиста 67

Рис. 4.4. Форма с результатом вызова динамического обработчика

Ну вот, теперь вы знаете все, что нужно для создания простого динамического управляющего элемента, присваивания его свойствам значений и обработки его событий. Не правда ли, это было не так уж и трудно?

Динамические компоненты, часть вторая

Одна из более разумных причин для динамического создания управляющих элементов отображение полоски прогресса (progress bar). Этот управляющий элемент вам не всегда необходим, но приятно было бы иметь его под рукой, когда происходит какой-либо длительный процесс.

В этом примере мы покажем, как создавать полоску прогресса динамически. Этот управляющий элемент будет не просто создан во время выполнения программы, а еще и создан как дочерний по отношению к другому управляющему элементу на форме панели состояния (status bar). Такой процесс применяется во многих программах, например Microsoft Word и Internet Explorer. Полоска прогресса появляется внизу, на панели состояния во время операций, которые занимают некоторое время, например загрузка и сохранение файлов или получение информации с удаленного сервера.

Теперь ваши приложения также могут использовать такую возможность благодаря мощи и гибкости VCL, встроенной в CBuilder.

Замечание

Для программы «Динамические компоненты исходный код находится на сопроводительном компакт-диске в каталоге Chapter4\DynControl2.

На рис. 4.5 показана форма, с которой мы будем работать в этом приложении. Создайте простую форму и перетащите на нее три кнопки и панель состояния (Status Bar). Убедитесь, что свойство alignment (выравнивание) у панели состояния установлено в alBottom. Это не отразится на нашем примере, но панель состояния принято располагать вдоль нижнего края формы.

Borland C++ Builder (+CD). Библиотека программиста 68

Рис. 4.5. Форма приложения «Динамические компоненты

Три кнопки на форме стадии жизни полоски прогресса. Поскольку мы не можем ни изменять свойства, ни удалять полоску прогресса, пока она не создана, сделайте вторую и третью кнопки недоступными, установив их свойства Enabled (доступны) в false (ложь). Когда придет время, мы их сделаем доступными.

Первый шаг процедуры добавить объявление компонента- полоски прогресса в заголовочный файл формы. Добавьте следующую строчку в заголовочный файл Unit1.h для проекта:

private: // User declarations

TProgressBar *FpProgress;

Следующий шаг, как всегда, заключается в установке указателя в NULL в конструкторе. Модифицируйте конструктор в исходном файле Unit1.cpp следующим образом:

__fastcall TForm1::TForm1(TComponent *Owner) : TForm(Owner)

{

FpProgress = NULL;

}

Создание полоски прогресса

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

void __fastcall TForm1::Button1Click(TObject *Sender)

{

FpProgress = new TProgressBar(StatusBar1); FpProgress->Parent = StatusBar1; FpProgress->Top = 4;

FpProgress->Height = STatusBar1->Height — 6; FpProgress->Left = 2;

Borland C++ Builder (+CD). Библиотека программиста 69

FpProgress->Width = 200;

FpProgress->Enabled = true;

// Устанавливаем диапазон полоски прогресса

FpProgress->Min = 0; FpProgress->Max = 100; FpProgress->Step = 1;

// и делаем доступными две других кнопки

Button1->Enabled = false; Button2->Enabled = true; Button3->Enabled = true;

}

Как видите, код очень похож на создание компонента в предыдущем примере. Сначала создается компонент, потом устанавливается свойство Parent (родитель). Здесь мы используем панель состояния (а не форму) качестве родителя полоски прогресса. Запомните, что все позиционные свойства нового компонента устанавливаются относительно родительского компонента, а не компонента, в котором он создан. Хотя форма и отвечает за создание полоски прогресса (потому что создание происходит в обработчике, принадлежащем форме), но теперь за сам управляющий элемент отвечает панель состояния. Возвращаясь к нашей дискуссии о важных классах, массив Components панели состояния теперь будет содержать указатель на компонент полоску состояния, в то время как массив Components формы содержать его не будет. Он будет содержать указатель на саму панель состояния, также как и на три кнопки на форме.

Мы располагаем полоску прогресса на панели состояния, используя для установки свойств Height (высота) и Top (верх) полоски прогресса координаты панели состояния. Вероятно, вам стоит немного поиграть с этим, чтобы добиться расположения, которое вам больше нравится. Индивидуальные вкусы различаются, и не существует общих рецептов на этот случай.

Как только мы расположили полоску прогресса, мы устанавливаем некоторые его важные свойства. В нашем случае вам нужно установить три важных свойства. Min (минимум) — наименьшее возможное значение счетчика прогресса. Max (максимум), не удивительно, есть наибольшее возможное значение счетчика прогресса. Step (шаг) — это значение, на которое будет увеличиваться счетчик при каждом шаге. Полоска прогресса будет отражать значение от 0 до 100 (процентов), которое занимает счетчик в диапазоне Min-Max.

Позаботившись о полоске прогресса, мы управляем доступностью кнопок в зависимости от их текущего состояния. Не стоит создавать второй управляющий элемент полоску прогресса в том же месте, что и первый, так что делаем кнопку Создать недоступной. Теперь счетчик полоски можно изменять, а саму полоску прогресса удалять, так что мы делаем доступными вторую и третью кнопки.

Увеличение счетчика прогресса

Первым делом мы добавим в код возможность увеличивать счетчик прогресса. Это эмуляция ситуации, когда вы в своей программе увеличиваете его параллельно с основной задачей (сохранение, загрузка файлов). Создайте обработчик для нажатия на кнопку Button2 (Увеличение счетчика прогресса) на форме. Добавьте в обработчик следующий код:

void __fastcall TForm1::Button2Click(TObject *Sender)

{

if ( FpProgress )

Borland C++ Builder (+CD). Библиотека программиста 70

FpProgress->StepIt();

}

Этот обработчик проверяет, существует ли компонент полоска прогресса (проверка разумности действия) и, если он существует, вызывает его метод StepIt.

Метод StepIt увеличит счетчик прогресса на величину, указанную в свойстве Step. Для проверки добавьте этот код, скомпилируйте и запустите приложение. Нажмите на кнопку создания, чтобы создать новую полосу прогресса на панели состояния. Вы увидите этот управляющий элемент в левой части панели состояния. Нажмите несколько раз на кнопку увеличения счетчика, и вы увидите что-либо, похожее на рис. 4.6.

Рис. 4.6. «Динамические компоненты с увеличенным счетчиком прогресса

Удаление полоски прогресса

Последний шаг удалить полоску прогресса с панели состояния. Это эмулирует ваши действия, когда основная операция завершена. Заметьте, что как только полоска прогресса удалена, процесс можно повторить целиком по новой.

Добавьте следующий код в обработчик нажатия на третью кнопку (Удалить полосу прогресса):

void __fastcall TForm1::Button3Click(TObject *Sender)

{

// Удалить полоску прогресса, если она есть

if ( FpProgress ) delete FpProgress; FpProgress = NULL;

//Сделать соотв. кнопки недоступными

Button2->Enabled = false; Button3->Enabled = false;

//Позволить создавать новую полоску

Button1->Enabled = true;

Соседние файлы в предмете Программирование на C++