![](/user_photo/2706_HbeT2.jpg)
- •Знакомство с visual basic
- •Практическая работа
- •Свойства, методы и события
- •Практическая работа
- •Переменные
- •Объявление переменных
- •Область видимости переменной
- •Ф орма 1
- •Форма 2
- •Математические операторы
- •Операторы сравнения
- •Логические операторы
- •Практическая работа
- •Обработка ошибок
- •Практическая работа
- •Самостоятельно
- •Условия и циклы
- •Самостоятельно
- •Использование списков и полей со списками
- •Удаление строк из списка
- •Ф орма Код (самостоятельно)
- •Массивы
- •Объявления массивов Объявление массива как одной переменной:
- •Заполнение массива
- •Динамический массив
- •Самостоятельно
- •Работа со строками
- •Определение длины строки
- •Усечение строк
- •Самостоятельно
- •Использование одной кнопки для включения / выключения
- •Конкатенация разнотипных строк
- •Коды символов в формате ascii
- •Окно ввода данных
- •Функция Val() и Slr()
- •Вывод информации на форму
- •Функция Format
- •Работа с графическим окном
- •Элемент управления RichTextBox
- •Объект Printer
- •Дополнительные возможности работы со строками Оператор Like и неопределенный поиск
- •Прочие строковые функции
- •Работа с файлами Чтение текстового файла (1 способ)
- •Запись текста в текстовый файл: Форма
- •Чтение текстового файла (2 способ)
- •Самостоятельно
- •Код (самостоятельно)
- •Простой файловый менеджер
- •'FileCopy не допускает файловые шаблоны, поэтому копирование по полному пути
- •Изменение регистра символов строки
- •Код (проанализировать программу, при необходимости – набрать и проверить работу)
- •Создание меню
- •Клавиши доступа и быстрые клавиши
- •Создание всплывающих меню
- •Разделение строк меню
- •Вырезание, копирование и вставка с помощью объекта Clipboard (буфера обмена)
- •Выделение текста в поле текста
- •Самостоятельно
- •Код (сначала попытайтесь самостоятельно написать код)
- •Использование элемента управления
- •Управляющие элементы Image List и Tool Bar
- •Создание дистрибутивного пакета – подготовка приложения к распространению
- •Время и таймеры
- •Использование одной кнопки для включения /выключения
- •Вычисление разницы между датами
- •Вычисление возраста
- •Код (проанализировать, при необходимости набрать проверить работу)
- •Самостоятельно
- •Код (сначала самостоятельно)
- •Подпрограммы и функции Использование процедур в Visual Basic
- •Самостоятельно
- •Определение точки входа в программу (запуск проекта с нужной формы)
- •Выбор подпрограммы Main() точкой входа проекта
- •' Оповещаем пользователя
- •Тестирование и отладка программ
- •Точка остановки программы
- •Наблюдение за несколькими переменными
- •Полосы прокрутки
- •Самостоятельно
- •Дополнительные компоненты active X
- •Создание дистрибутивного пакета – подготовка приложения к распространению
- •Время и таймеры
- •Использование одной кнопки для включения /выключения
- •Вычисление разницы между датами
- •Вычисление возраста
- •Код (проанализировать, при необходимости набрать проверить работу)
- •Самостоятельно
- •Форма Код (сначала самостоятельно)
- •Подпрограммы и функции Использование процедур в Visual Basic
- •Самостоятельно
- •Определение точки входа в программу (запуск проекта с нужной формы)
- •Выбор подпрограммы Main() точкой входа проекта
- •' Оповещаем пользователя
- •Понятие о базах данных
- •Терминология баз данных
- •Элемент управления данными
- •Свойства элемента управления данными
- •Методы элемента управления данными
- •Свойства набора данных
- •Методы набора записей
- •Практическая работа
- •Связывание элемента управления данными с базой данных
- •Что такое Recordset?
- •Поиск записей
- •Поиск дальше
- •Запросы
- •Компоненты языка sql
- •Самостоятельно
- •Создание отчетов с помощью crystal reports
- •Создание отчета
- •Объекты
- •Практика
- •Движение объектов
- •О связанных элементах управления Последовательность событий при загрузке формы
- •Отношения родитель/потомок между элементами управления данными
- •Практикум –
- •Создание базы данных своими руками
- •Какую технологию выбрать?
- •Элемент управления данными ado
- •Сортировка и объединение данных с помощью элемента управления Hierarchical FlexGrid
- •Проект «Школьный журнал» (пример создания и работы с базой данных)
- •Практическое задание Создать базу данных «Записная книжка»
- •Простейший графический редактор
- •При отпускании кнопки мыши
- •Dim n As Integer ‘счетчик новых записей в массиве
О связанных элементах управления Последовательность событий при загрузке формы
Когда программа начинает работать, загружается форма. Пользователь наблюдает лишь факт появления формы на экране. На самом же деле перед отображением формы возникает целый ряд событий:
Первым происходит событие Form_Initialize.
Затем инициализируется событие Form_Load. По этому событию форма загружается в память и подготавливается к выводу на экран.
Событие Form_Activate происходит сразу после того, как форма отобразится на экране.
Событие Form_Paint вызывается, когда в форме надо что-то нарисовать.
Наборы записей
Выше речь шла о наборе записей, который создается элементом управления данными. Объект Recordset (Набор записей) содержит информацию из таблиц базы, возвращенную в результате выполнения запроса. Набор записей является объектом, поскольку у него есть свойства и методы. В одном из следующих разделов будет рассказано, как этот объект программировать.
Вы задали в качестве значения свойства RecordSource таблицу Publishers. В действительности за этим стоит запрос на языке Structured Query Language (SQL - язык структурированных запросов) на извлечение всех записей из таблицы Publishers. По сути свойство RecordSource - не что иное, как SQL-запрос, определяющий, что необходимо включить в набор записей.
Свойство RecordSource можно установить не только в режиме проектирования, но и из программы. Чтобы сформировать запрос к элементу управления данными, может потребоваться подбор записей из нескольких таблиц. К счастью, SQL позволяет создавать самые сложные запросы.
Набор записей существует в виде временного объекта в оперативной памяти. Запрос извлекает отобранные записи из одной или нескольких таблиц и создает этот временный объект, с которым и работает элемент управления данными. Все наборы записей конструируются из записей (строк) и полей (колонок) таблиц базы данных. Поэтому концептуально набор записей можно представлять себе как таблицу. Эта квазитаблица может отображаться на некоторую реальную таблицу целиком или на часть такой таблицы или на подмножество сразу нескольких таблиц. Набор записей - это в некотором смысле множество обратных ссылок на записи, хранящиеся в одной или нескольких реальных таблицах.
На практике обычно требуется получить одно поле из таблицы, содержащей множество полей, либо несколько полей из одной таблицы и несколько - из другой. Набор записей содержит только те записи, которые были запрошены. Например, таблица Titles содержит 8000 записей, а нужны только книги, названия которых начинаются с «А». Следовательно, набор записей может содержать всего, скажем, 100 записей. Предложение на языке SQL описывает запрос на отбор нужной информации. Этот запрос направляется элементу управления данными, который размещает в памяти построенный набор записей, включающий все указанные в запросе поля.
Когда вы редактируете поле базы данных посредством связанного элемента, набор записей модифицируется, и изменения записываются обратно в базу данных. Для этого элемент управления данными использует Jet, которое и занимается реальным обновлением таблиц и индексов в базе.
В зависимости от поставленных перед программистом задач можно создавать три разных типа наборов записей, а именно:
Table (Таблица) - это отображение реальной таблицы базы данных, с помощью которого можно добавлять, изменять и удалять из нее записи. Если вы работаете с одной таблицей и хотите ее модифицировать, это наиболее эффективный способ организации набора записей. Но, к сожалению, указанный набор только с одной таблицей и может работать. В наборе записей типа «таблица» все изменения записываются непосредственно в таблицу, на базе которой он построен;
Dynaset (Динамический набор) - это результат запроса, в котором могут быть обновляемые записи. С его помощью допускается добавлять, изменять и удалять записи из одной или нескольких таблиц. Динамический набор может содержать поля, выбранные более чем из одной таблицы. При модификации записи изменения сохраняются в той таблице или таблицах, на основе которых набор построен. Этот тип набора записей принимается по умолчанию. Динамический набор - самый мощный и гибкий тип, но, как правило, он уступает табличному набору в скорости обработки данных;
Snapshot (Снимок) - это статическая копия множества записей, которая используется для поиска данных либо генерации графиков или отчетов. Снимок может содержать данные из одной или нескольких таблиц, однако обновление с его помощью запрещено. Если для решения конкретной задачи обновление не нужно, снимок - самый быстрый вариант. На обработку информации, хранящейся в наборе записей этого типа, уходит меньше времени, чем в случае табличного или динамического наборов. Снимок обычно возвращает записи быстрее при использовании ODBC-источников данных.
Важная информация
Если число записей, возвращенных в снимке, велико, его производительность может оказаться ниже, чем для динамического набора, поскольку в отличие от последнего снимок сохраняет в памяти полные копии всех извлеченных записей. Динамический набор, напротив, загружает в память лишь некоторую группу ссылок, а остальные извлекает по мере необходимости. В приложениях с большим числом возвращенных записей можно попробовать открыть как снимок, так и динамический набор, и посмотреть, что работает быстрее. Не забудьте, что снимок позволяет читать, но не обновлять данные.
С элементом управления данными связано понятие текущей записи, то есть записи, доступной в текущий момент работы. В любой момент только одна запись является текущей. Данные именно из этой записи отображаются в связанных элементах управления. При работе с наборами записей всегда надо следить за тем, чтобы текущая запись была действительна. В принципе, можно позиционировать (установить) указатель текущей на удаленную запись или за пределы набора, сделав ее тем самым недействительным. Об этом будет рассказано при рассмотрении свойств BOFAction и EOFAction элемента управления данными.
Массивы элементов управления
В рассмотренном примере классного журнала мы воспользовались массивами текстовых полей и меток для заполнения их в цикле именами полей и значениями из базы данных. Многие программисты, только начинающие работать с Visual Basic, стараются избегать таких массивов, поскольку не понимают, зачем они нужны. Однако использование массивов элементов управления не только упрощает программирование, но и позволяет оптимизировать код. Некоторые предпочитают написать программу, добиться ее работоспособности и только потом заняться оптимизацией. Это абсолютно неправильный подход. Поняв принципы работы Visual Basic, вы сможете сразу создавать уже оптимизированный код. Когда вы слышите, как кто-то говорит, что вернется к оптимизации попозже, будьте уверены: этот человек не очень хорошо знаком с языком. В данной книге постоянно отмечается, когда эффективнее использовать тот или иной способ кодирования из всех возможных. Применение массивов элементов управления - пример именно такой ситуации.
Массив элементов управления «напрашивается», когда в форме есть несколько однотипных элементов. Будет логично, если вы решите создать два массива: один - для полей ввода, другой - для меток. Хранение элементов управления в массиве требует меньше ресурсов, чем размещение их в форме по отдельности. Массивы полезны и в том случае, когда вы хотите, чтобы несколько элементов имели общий код. Например, если десять нолей ввода помещены в массив, по какому бы из них вы ни щелкнули кнопкой мыши, будет выполнен один и тот же код - в этом преимущество массивов элементов управления. Хотя для каждого элемента массива имеется отдельное визуальное представление на экране, процедуры обработки событий являются общими для всех. Чтобы выяснить, какое конкретно поле ввода было выбрано из соответствующего массива, достаточно посмотреть на параметр Index. При добавлении нового поля ввода в массив оно наследует все общие для массива процедуры обработки событий. И, стало быть, код не дублируется, как было бы в случае десяти независимых элементов.
Важная информация
Работая с формой, всегда помните о том, что при ее инициализации должны быть загружены все элементы управления и весь код. Поэтому в форме надо по возможности уменьшать число объектов. Позже, узнав, как выполняется код, обеспечивающий работу элемента управления данными, вы научитесь помещать большую его часть в кодовый модуль. Не забывайте, что чем меньше места в памяти занимает форма, тем быстрее она загружается. Следовательно, тем больше она понравится пользователям.
При создании массива элементов управления Visual Basic создает параметр Index. Вы можете проверить его, чтобы выяснить, к какому из элементов в массиве относится действие, ибо все элементы имеют одну и ту же процедуру обработки событий. Вы лучше поймете, о чем идет речь, когда поработаете с примером, демонстрирующим возможности массива. Пока взгляните, как именно Visual Basic включает параметр Index. При изменении любого поля ввода, входящего в массив, возникает событие Change и срабатывает процедура:
Private Sub Text1_Change(Index As Integer)
End Sub
. Если бы у вас было два отдельных поля ввода, у каждого была бы своя процедура обработки события Change. А так все десять полей пользуются единственным обработчиком, который идентифицирует источник события по переданному индексу.
Еще одно преимущество массивов элементов управления состоит в том, что в них можно добавлять элементы во время выполнения программы.
Не будь у массивов элементов управления, динамическое создание элементов во время выполнения было бы невозможно. Ведь с новым элементом не было бы связано никаких процедур обработки событий. Вот тут-то массивы и приходят на выручку, помогая решить задачу. Новый элемент наследует общие обработчики, уже написанные для массива. Например, если в форме есть несколько полей ввода и каждое из них предназначено для ввода даты, можно поместить их в массив и организовать общую для всех процедуру проверки корректности введенного значения.
Внутреннее устройство элемента управления данными
В предыдущем примере вами задавались свойства Align, DatabaseName и RecordSource элемента управления данными (как вы помните, речь шла о добавлении связанных полей ввода). Для этого использовалось окно свойств Properties, но с равным успехом перечисленные свойства можно установить и программно.
Набор записей, созданный элементом управления данными - это временная структура в памяти. Доступ к содержимому записей можно получить либо с помощью связанных элементов, таких как поля ввода, либо обращаясь напрямую к элементу управления данными. В программе разрешено использовать любое свойство или метод объекта Recordset (Набор записей). В любом случае под началом набора записей понимается его первая запись, а под концом - последняя. Но несмотря на кажущуюся логичность, это не вполне верно. Когда вы достигаете начала набора записей, оказывается, что указатель на начало файла (BOF) установлен перед первой записью. Точно также по достижении последней записи указатель на конец файла (EOF) будет установлен за ней. Как вы, наверное, догадались, это может привести к печальным последствиям, поскольку при достижении начала или конца набора исчезает текущая запись. Связанные элементы управления «очищаются» и пользователь не понимает, что случилось. Если, находясь в начале набора, он попытается переместиться дальше. Visual Basic выдаст сообщение об ошибке. А если это произойдет в конце набора, будет добавлена фантомная запись, и пользователь, вероятно, будет обескуражен, ведь вряд ли он собирался добавить новую запись.
К счастью, у элемента управления данными есть два свойства, позволяющие управлять этой ситуацией. По умолчанию при достижении начала набора указатель будет переустановлен на первую запись, а при достижении конца - на последнюю.
Свойство BOFAction
Свойство BOFAction сообщает элементу управления данными, что делать при достижении начала набора записей.
Положение указателя текущей записи в наборе определяет значения свойств ВОF и EOF. Элемент управления данными обращается к этим свойствам, чтобы понять, есть ли в данный момент текущая запись или пользователь уже вышел за пределы набора.
С помощью свойства BOFAction можно сообщить элементу управления данными, как действовать в подобных случаях. Если оставить значение по умолчанию (0 -Move First), при выполнении метода Move First указатель текущей будет установлен на первую запись, и последующая попытка выполнить метод MovePrevious будет блокирована. Это значение подобно страховочной сетке - пользователю не разрешено выходить за пределы набора.
Другими словами, если любое из свойств BOF или EOF равно True, текущей записи нет. Если сформированный по вашему запросу набор записей оказался пустым (например, вы ищете авторов по имени Зик (Zeke)), оба свойства BOF и EOF будут равны True. У набора записей есть также свойство RecordCount, показывающее, сколько записей в наборе. В случае пустого набора его значение будет равно 0.
Предположим, что вы искали авторов по имени Джек и нашли одну запись. Если набор записей содержит только одну запись, именно она и будет текущей. Как BOF, так и EOF равны False и таковыми останутся до тех пор, пока пользователь не выйдет за пределы набора в результате выполнения метода MovePrevious или MoveNext. После этого текущей записи не станет.
Предположим, что вы удалили единственную запись в наборе. В таком случае и BOF, и EOF останутся равными False, пока вы не попытаетесь переместиться на другую запись.
Пользователь может перейти на последнюю запись в наборе - она станет текущей. Теперь при попытке выполнить MoveNext, текущая запись окажется недействительной, а свойство EOF примет значение True. Аналогично, если пользователь стоит на первой записи и запускает MovePrevious, текущая запись также пропадает, и свойство BOF примет значение True. Так что если необходимо обойти все записи набора, обычно метод MoveNext выполняется в цикле до тех пор, пока свойство 30F не станет равным True. Если после этого вызвать MoveNext еше раз, произойдет ошибка.
Свойство EOFAction
Свойство EOFAction сообщает элементу управления данными, что делать при достижении конца набора записей. Имеется три варианта.
Как и в примере с предыдущим свойством, лучше всего оставить значение по умолчанию (0 - Move Last). В этом случае при достижении конца набора указатель переустанавливается на последнюю запись, и признак конца файла EOF сбрасывается. Второй вариант (1 - EOF) устанавливает признак конца файла EOF при достижении конца набора записей. Последний вариант (2 - Add New) автоматически создает и добавляет новую запись при попытке пользователя переместиться за последнюю запись набора. Происходящее может вызвать недоумение пользователя, если он не знает, что должен ввести новую запись. Такое решение не является интуитивно очевидным. В этом случае все поля формы очищаются, поскольку элемент управления данными создал пустую запись. Если вы не собираетесь создавать программы промышленного уровня, никогда не «разрешайте» пользователю добавлять запись случайно. Гораздо лучше, чтобы пользователь явно попросил добавить запись, щелкнув по кнопке Add New (Добавить новую).
Важная информация
Свойства BOFAction u EOFAction проявляют себя, только если пользователь перемещается по набору записей с помощью навигационных кнопок на элементе управления данными. Указанные свойства игнорируются, если навигация осуществляется из программы, поскольку в таком случае вы выполняете часть функций элемента управления данными. Элемент управления данными не обновляет значения этих свойств, если он сам не используется для навигации.
Свойство Connect
Как правило, для свойства Connect используется принимаемое по умолчанию значение Access. Однако при необходимости подключиться к другому источнику Данных это свойство можно задать явно в окне Properties или из программы. Обратите внимание, что возможные значения свойства Connect представлены в выпадающем списке.
Уже отмечалось, что для ADO-элемента управления структура строки Connect несколько сложнее: в ней указывается, какой драйвер использовать, а также местонахождение и имя базы/источника данных. В случае же стандартного элемента Управления данными достаточно выбрать нужное значение из списка.
Зачем нужен код в обработчике Form_Activate()
При загрузке формы в память последняя переживает ряд событий, которые необходимо понимать каждому программисту (см. «Последовательность событий при загрузке формы» в этой главе).
Прежде всего, происходит событие Initialize, вызывается процедура - обработчик Form_Initialize (). Ее можно использовать для инициализации переменных уровня формы.
Затем срабатывает процедура-обработчик Form_Load (). В ней рисуются визуальные компоненты, такие как поля ввода. Все свойства визуальных элементов устанавливаются также здесь. Если вы попытаетесь обратиться к свойствам элемента управления данными Datal до полной инициализации визуальных компонентов, будет выдано сообщение об ошибке 91 (объектная переменная или переменная блока With не установлена).
Ошибка связана с тем, что еще не все визуальные элементы инициализированы. Некоторые программисты стремятся подавить это сообщение, принудительно высвечивая форму путем обращения к методу Me. Show в обработчике события Load, а уже затем обращаясь к свойствам визуальных элементов. Такой способ допустим, но тем самым программист нарушает естественный «ход событий», заставляя форму появиться до срока. Лучшим местом для доступа к визуальным элементам в процессе загрузки формы является обработчик события Form_Activate (), который срабатывает, когда форма полностью загружена, инициирована и отображена на экране. В этот момент можно быть уверенным, что все визуальные элементы готовы к работе.
Важная информация
Процедура Form_Activate() вызывается всякий раз, когда форма становится активной. Это значит, что если форма вызывается из какой-нибудь другой формы, событие Activate возникает каждый раз, когда пользователь щелкает на форме при переходе от формы к форме. Если необходимо, чтобы некоторый код внутри Form_Activate() исполнялся только один раз, надо воспользоваться статической булевской переменной (Boolean), u присваивать ей значение True во время загрузки формы. Далее при любом возникновении этого же события переменная проверяется и, поскольку она уже установлена, защищенный ей код не выполняется.
Событие Reposition элемента управления данными
Как вы помните, процедура-обработчик Form_Activate () вызывается только один раз при загрузке формы (и также в случае, если форма становится неактивной, а потом снова активной). Тем не менее, обновлять свойство Datal .Caption необходимо при каждом перемещении пользователя на новую запись. И самое подходящее место для этого - обработчик события Datal_Reposition(). Вставьте в него следующий код:
Private Sub Datal_Reposition()
Datal .Caption = Datal. Recordset .AbsolutePosition + 1 & _ " из " & ITotalRecords
End Sub
Вы просто добавляете 1 к значению свойства AbsolutePosition. Связано это с тем, что для первой записи в наборе указанное свойство равно 0, а для последней - числу записей минус 1. Добавляя 1, выделаете нумерацию более привычной для пользователя.
Как уже говорилось, после загрузки набора элементом управления данными первая запись становится текущей. В этот момент происходит событие Reposition. А затем оно возникает, когда пользователь перемещается по набору записей с помощью навигационных кнопок, когда вызывается любой из методов семейства Move (например, MoveNext) или Find (например, FindFirst) и вообще, когда происходит любое действие, изменяющее текущую запись. Это событие возникает уже после того, как запись стала текущей. Если вы хотите произвести какие-то проверки до перехода к новой записи, можете воспользоваться процедурой Datal_Validate (), которая срабатывает до того, как новая запись станет текущей.
Возможно, вы задаете себе вопрос: раз Datal_Reposition () срабатывает при начальной загрузке, зачем же нужен код в обработчике Form_Activate ()? Дело в том, что достаточно только один раз получить общее число записей в наборе и запомнить его в переменной ITotalRecords. Если бы этот код находился в обработчике события Reposition, свойство RecordCount считывалось бы при каждой смене текущей записи.
Создание привлекательного пользовательского интерфейса
Качественная программа должна предоставлять пользователю как можно больше визуальной информации. И это относится не только к таким вещам, как изменение формы курсора на песочные часы во время длительной обработки. Чем больше будет интуитивно понятных визуальных ключей, тем удобнее работать пользователю. Ниже рассказано, как добавить в форму еще один элемент, который вы, возможно, сочтете полезным и для своих программ. Объект Datal.Recordset имеет не только свойство AbsolutePosition, но и свойство PercentPosition (Относительная позиция). Вы будете считывать это свойство и отображать его в элементе управления, который называется Progress Bar (Индикатор прогресса). Код будет выглядеть следующим образом:
Private Sub Datal_Reposition()
With Data1.Recordset
Datal .Caption = Datal. Recordset .AbsolutePosition + 1 & _ " из " & ItotalRecords
ProgressBar1.Value = . PercentPosition
End With
End Sub
Одной из удобных особенностей Access является наличие записей, способных изменять свою длину. Поэтому если в некоторой записи поле Company Name пусто, не надо сожалеть о том, что под него отведено 255 пробелов. Его значение останется равным Null до тех пор, пока не будет введено реальное название. Точно также, если название составляет 10 символов, то и для хранения отводится всего 10 символов. В некоторых более старых базах данных для хранения такого поля было бы зарезервировано 255 символов, даже если значение не задано. Напрасный расход дискового пространства (на тот случай, если в одной из записей некоторое поле окажется гораздо длиннее, чем то же поле во всех остальных записях) всегда очень раздражала проектировщиков баз данных. Перед ними стояла дилемма: стоит ли предусматривать дополнительное место для хранения «необычных» значений? Если так, то для каждого поля надо отвести столько места, сколько для самого длинного. Или «обрезать» длинные значения и смириться с потерей информации?
Access избавила программистов от мучительных размышлений. Здесь нет лишнего расхода дискового пространства, связанного с хранением необычно длинных полей.