Классы, содержащие несколько объектов
Рассмотрим пример создания сложного пользовательского класса, содержащего не один объект, а несколько. Предположим, что разработчику понадобилось расположить на экранных формах цифровые таймеры, отсчитывающие время с указанной точность и предполагающие возможность перезапуска в произвольный момент. Объект должен предусматривать так же возможность задания стартового значения времени и шаг временных интервалов.
Задача достаточно простая, решается использованием на форме объектов типа надписи и таймера. Однако из условия видно, что количество таймеров величина неопределенная, их может быть произвольно число от нуля и до пределов фантазии разработчика. Поэтому целесообразно создать один раз пользовательский класс, объединяющий в себе надпись и таймер, а затем просто тиражировать его необходимое количество раз на экранных формах.
Вначале создайте новый пользовательский класс, выбрав в качестве базового класса Container (рис. 11). (Контейнер – это объект, содержащий в себе некоторые подобъекты). Обратите внимание, на то, что в одной библиотеке имена классов повторяться не могут. Если необходимо создать класс, имя которого уже используется, его необходимо сохранять в отдельную библиотеку.
Рис. 11. Создание нового класса на базе контейнера
В появившемся окне редактирования класса (рис. 12) разработчик может расположить произвольные объекты внутри области контейнера. Допускается произвольно менять визуальную область контейнера, для этого достаточно захватить его край мышкой и просто растянуть до требуемой величины. Рекомендуется в начале процесса редактирования увеличивать размер контейнера для удобного расположения входящих в него элементов, а в конце работы – сжать визуальную область до необходимого размера. Помните, на экранной форме контейнер будет занимать точно такой размер, какой определен при редактировании класса.
Рис. 12. Окно редактирования класса типа Контейнер
При помощи панели Form Controls расположите внутри контейнера объекты Label и Timer, как показано на рис. 13. К сожалению, границы надписи в режиме редактирования контейнера заметить практически невозможно, видны только точки изменения ее размеров, когда надпись выделена. Поэтому не следует присваивать свойству Caption надписи пустое значение, иначе ее можно визуально просто потерять, хотя в окне свойств выбрать сам объект Label можно.
Рис. 13. Контейнер с размещенными объектами Label и Timer
Затем добавьте при помощи команды Class | New property два новых свойства к классу с именами st_val и st_ep, которые будут содержать в себе стартовое значение счетчика и его шаг соответственно. Важно задавать стартовые значения новым свойствам, которые определяют их типы по умолчанию. В нашем случае обязательно присвойте стартовые значения «0.0»
В методе Init создаваемого класса необходимо прописать присвоение стартовых параметров самого таймера:
*задание интервала таймера в миллисекундах
this.timer1.Interval=1000*this.st_ep
*присвоение надписи начального значения счетчика
*всего будет выведено 10 знаков, после запятой - 1
this.label1.Caption=STR(this.st_val,10,1)
*обновление контейнера на форме
this.Refresh()
Сам объект таймер в системе VFP работает так же, как и в остальных языках программирования, т.е. таймер отсчитывает указанное количество миллисекунд от нулевого значения, а затем вызывает собственное событие Timer. После этого начальное значение таймера сбрасывается и отсчет начинается заново. Для управления отображением счетчика следует в окне свойств, при помощи ниспадающего списка выбрать объект Timer (или просто выделить его мышкой в окне редактирования класса) и во вкладке Methods выбрать событие Timer, после чего, двойным щелчком мыши вызвать окно редактирования программного кода. В нем следует прописать поведение таймера при достижении им порогового значения:
*увеличиваем свойство класса, отвечающее за стартовое значение
*на величину, указанную в свойстве шага счетчика
this.Parent.st_val = this.Parent.st_val+this.Parent.st_ep
*выводим значение в надписи
this.Parent.label1.Caption=STR(this.Parent.st_val,10,1)
*обновляем контейнер
this.Parent.Refresh()
В данном программном коде, кроме указателя на текущий объект this, используется указатель на объект более верхнего уровня, являющийся родительским по отношению к текущему объекту – Parent. В нашем случае родительским объектом в иерархии является контейнер, а надпись и таймер – его дочерние подобъекты. Поэтому, находясь в объекте Timer, обращение к контейнеру будет выглядеть как this.Parent. А обращение к надписи из таймера – this.Parent.label1.
В принципе, работа над счетчиком почти завершена. При запуске устанавливаются стартовые значения, в процессе работы по указанному таймингу значения изменяются. Однако, задача содержала в себе еще и требование на возможность сброса значения счетчика и установки нового шага.
Для этого следует добавить новый метод в созданный класс при помощи команды Class | New method. Добавьте новый метод с именем Reset. Далее следует описать действия класса при вызове этого метода. Дважды щелкните в окне свойств на имени созданного метода и пропишите следующий программный код:
*переустанавливаем шаг таймера
this.timer1.Interval=this.st_ep *1000
По окончанию редактирования нового класса не забудьте сжать видимую область контейнера до размеров надписи! Не пугайтесь, все элементы не вошедшие в видимую область все равно будут работать, а при последующем редактировании класса к ним можно легко добраться просто увеличив размер контейнера.
Убедитесь в полноте и правильности выполненных действий и закройте окно редактирования класса.
Затем создайте новую экранную форму и поместите на нее два объекта созданного класса. Для этого либо просто перетащите дважды класс на форму, либо подключите собственную библиотеку в окне Form Controls (см. рис. 4, см. рис. 5). Кроме того, разместите на этой же форме кнопку, которая должна будет отвечать за рестарт одного из счетчиков. В методе Click кнопки пропишите следующий программный код:
*установка нового стартового значения для первого счетчика
thisform.my1.st_val =0
*установка нового шага для первого счетчика
thisform.my1.st_ep = 0.5
*сброс первого счетчика
thisform.my1.reset()
Запустите форму на исполнение. Оба счетчика должны синхронно работать. После того, как значение счетчиков станут больше 10, нажмите кнопку сброса счетчика (Рестарт). Обратите внимание на поведение первого счетчика. Если все сделано правильно, то его значения должны начать отсчитываться с нуля с шагом 0.5 секунд (рис. 14).
Рис. 14. Форма с запущенными счетчиками
Примечания:
1. Во вкладке Methods окна свойств отображаются как методы объекта, так и его события. Основное отличие в том, что методы необходимо явно вызывать, а события вызываются сами при некоторых внешних условиях.
Задания
1. Создайте пользовательский класс выключателя, меняющего цвет надписи на синий в выключенном состоянии и желтый – во включенном.
2. Создайте пользовательский класс, выводящий на форме текущую дату и время с точностью до секунд. Обновление значений должно происходить ежесекундно. Функция, возвращающая текущую дату и время в VFP, это DATETIME(). При создании класса рекомендуется использовать в качестве базового – контейнер.
Лабораторная работа № 12