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

2 Курс Информатика VBA(ЗО) / Книги / В.Д.Хорев - Самоучитель программирования на VBA в Microsoft Office

.pdf
Скачиваний:
2702
Добавлен:
31.05.2015
Размер:
21.66 Mб
Скачать

Автоматизация ввода данных 153

Метод OpenCurrentDatabase объекта Application открывает базу данных, находящуюся в указанном файле, и делает ее текущей:

MyAC.OpenCurrentDatabase "C:\Мои документы\MyOffice.mdb"

Далее будет присвоено значение True свойству Visible приложения MS Access, заключенного в переменной MyAc. Вообще говоря, этого можно не делать — окно приложения в таком случае не появится на экране, и все необходимые действия будут выполнены “за кадром”.

MyAC.Visible = True

Далее, по аналогии с другими приложениями Office, следовало бы обратиться к семейству таблиц текущей базы данных, чтобы открыть таблицу “Операции”, но не тут то было, в Access все устроено по-другому. Прямого доступа к объектам базы данных при помощи объектов MS Access вообще не существует. Такой доступ осуществляется при помощи специальных объектов, которые будут рассмотрены немного позднее. Чтобы совершить задуманное, придется “подавать команды” непосредственно приложению Access, которое выполнит необходимые операции точно таким же образом, как если бы им управлял пользователь. Более того, придется иметь дело не с таблицей “Операции”, как таковой, а с формой “Все операции”, которая служит для ввода данных в упомянутую таблицу.

Использование объекта DoCmd для открытия формы

Дело в том, что для подачи команд приложению Access из кода VBA служит специальный объект DoCmd, чьи методы почти в точности соответствуют набору макрокоманд Access. Пользуясь методами DoCmd и обращаясь к объектам приложения Access, можно добавить запись только через посредство формы. Вот как выглядит команда “Открыть форму”:

MyAC.DoCmd.OpenForm "Все операции"

Создание новой записи и переход на нее

Далее следует команда “Перейти на запись”:

MyAC.DoCmd.GoToRecord acDataForm, "Все операции", acNewRec

Параметр acDataForm указывает, что речь идет о форме, затем указано имя формы, а по-

следний параметр задает направление перехода. Константа acNewRec означает “создать новую запись и перейти на нее”. Если бы потребовалось перейти на последнюю запись, то следовало бы использовать константу acLast. Константа acNext соответствует следующей записи, а константа acGoTo — переходу на запись с заданным номером (последний при этом должен быть указан при помощи еще одного параметра).

Наконец, достигнута требуемая точка — новая запись в таблице “Операции” создана и является текущей в открытой форме “Все операции”. Чтобы ввести данные в поля формы (а тем самым, через посредство формы, и в поля новой записи в таблице), необходимо обратиться к объектам

семейства Forms, принадлежащего объектуприложению Access. Синтаксис обращения к объектам Access характеризуется определенными особенностями. Если речь идет о форме, имя которой состоит из одного слова, то присваивание значения некоторому свойству некоторого поля этой формы должно выглядеть следующим образом:

Приложение.Forms!ИмяФормы!ИмяПоля.Свойство = Значение

Если же имя формы (то же самое относится к имени поля) состоит из нескольких слов, то такое имя необходимо заключить в квадратные скобки:

Приложение.Forms![Имя Формы]![Имя Поля].Свойство = Значение

154 Глава 6. Access: автоматизация офисной базы данных

Таким образом, оператор для загрузки значения в поле Äàòà формы “Все операции” будет выглядеть следующим образом:

MyAC.Forms![Все операции]!Дата = Date

Функция Date возвращает текущую системную дату.

см. также в приложении раздел “Системная дата и системное время”.

Аналогично, значения из соответствующих ячеек рабочего листа загружаются в остальные поля формы:

MyAC.Forms![Все операции]!СуммаОперации = _

ActiveSheet.Range("F15").Value MyAC.Forms![Все операции]!КодОперации = _

ActiveSheet.Range("E6").Value MyAC.Forms![Все операции]!КодКлиента = _

ActiveSheet.Range("C6").Value

После этого приложение Access закрывает текущую базу данных вызовом метода CloseCurrentDatabase, и завершает свою работу вызовом метода Quit:

MyAC.CloseCurrentDatabase

MyAC.Quit

Остается лишь освободить память, занятую объектом MyAC:

Set MyAC = Nothing

Автоматизация работы с имеющимися данными

Второй аспект работы с любой базой данных заключается в использовании накопленных данных. Здесь тоже может найтись место для программ VBA, позволяющих автоматизировать те или иные операции.

Для того чтобы применять VBA в работе с базой данных Access, необходимо где-то расположить соответствующий программный код. Кроме этого, необходимо позаботиться о способе запуска этого программного кода.

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

Создание программного модуля в составе базы данных

Создать программный модуль в составе базы данных несложно — для этого следует переключиться на вкладку Модули и щелкнуть на кнопке Создать. В результате откроется окно редактора Visual Basic, где необходимо ввести какой-нибудь код. Допустим, планируется создать функцию Анализ_баланса, которая будет размещена в этом модуле. Следует выбрать в меню Вставка (Insert) команду Процедура (Procedure), после чего откроется диалоговое окно Вставка процедуры (Add Procedure). В этом окне следует ввести имя функции, установить переключатель типа процедуры в позицию функция (Function), как показано на рис. 6.3, и щелкнуть на кнопке OK.

Автоматизация работы с имеющимися данными 155

Рис. 6.3. Добавление функции в программный модуль

В окно программного модуля будет вставлена заготовка требуемой функции. После этого редактор Visual Basic можно закрыть щелчком на кнопке закрытия окна в правом верхнем углу или командой Файл | Выход (File | Close and Return). Подтверждение на сохранение и присвоение имени созданному модулю может быть запрошено Access, как при закрытии окна модуля, так и при закрытии базы данных. Чтобы затем открыть модуль и ввести код функции или процедуры, достаточно будет выполнить двойной щелчок мышью на значке модуля.

Для запуска созданной функции воспользуемся механизмом макросов Access. Переключитесь на вкладку Макросы и щелкните на кнопке Создать. В окне конструктора выберите в первой строке макрокоманду ЗапускПрограммы, как показано на рис. 6.4.

Рис. 6.4. Макрокоманды Access в окне конструктора

Затем, в нижней области окна конструктора, выберите аргументы макрокоманды: в строке Имя функции щелкните на кнопке построителя (кнопка с тремя точками). В результате откроется окно построителя выражений (рис. 6.5), где последовательно следует выполнить двойные щелчки на значке Функции, на имени базы данных (MyOffice), на имени модуля (Module1), и, наконец, на имени функции (Анализ_Баланса). Затем следует щелкнуть на кнопке Вставить и закрыть окно построителя щелчком на кнопке OK.

156 Глава 6. Access: автоматизация офисной базы данных

Рис. 6.5. Окно построителя: в качестве аргумента макрокоманды “ЗапускПрограммы“ выбрана функция Анализ_Баланса из программного модуля Module1

После этого окно конструктора можно закрыть, сохранив созданный макрос и присвоив ему имя — пусть он так и называется — Анализ баланса.

Ссылки на библиотеки объектов: DAO или ADO?

Теперь для запуска функции Анализ_Баланса достаточно будет открыть вкладку Макросы и выбрать двойным щелчком макрос Анализ баланса. Можно приступать к разработке собственно кода функции.

Но прежде чем вводить код, необходимо позаботиться о ссылках на объектные библиотеки. Такие ссылки уже задавались в предыдущих главах неоднократно, но в Access это сделать сложнее. Во-первых, зачем вообще здесь ссылки — ведь нет обращения к объектам другого приложения? Второй вопрос, — что может быть сложного в том, чтобы поставить флажок напротив имени какой-нибудь библиотеки?

Дело в том, что объекты Access используются для доступа только к тому, что имеет отношение к самой рабочей среде Access. Таким способом можно “добраться” до данных, используя специальный объект DoCmd, однако подобный доступ связан с серьезными ограничениями. Для полного и прямого доступа к данным Access из программного кода VBA используют библиотеки объектов доступа к данным.

см. также в этой главе раздел “Использование объекта DoCmd для открытия формы”.

Проблема заключается в том, что таких библиотек много. Существует несколько принципиально различающихся библиотек доступа, каждая из которых существует в нескольких версиях. В версии Office 2000, наряду со старыми библиотеками DAO (Data Access Objects), используется новая технология ADO (ActiveX Data Objects). И эти библиотеки могут мешать работе друг друга, поскольку в них встречаются одноименные объекты, которые на самом деле несовместимы друг с другом. Поэтому, при работе с кодом VBA в среде Access, в каждом конкретном случае необходимо задать ссылки на необходимые библиотеки и убрать ссылки на библиотеки, которые данный код не использует.

Описанный далее программный код реализован с использованием библиотеки DAO версии 3.6. среды MS Office 2000. (Он будет работать также в Office 97 с библиотекой DAO 3.5). В случае MS Office 2000 необходимо в окне ссылок (Сервис | Ссылки, Tools | References) задать ссылку на объектную библиотеку Microsoft DAO 3.6 Object Library, и убрать ссылку на библиотеку Microsoft ActiveX Data Objects 2.1 Library, или любую другую библиотеку объектов ADO.

Функция “Анализ баланса по клиенту”

Итак, приступим к разработке программного кода функции Анализ_Баланса.

Автоматизация работы с имеющимися данными 157

Для того чтобы обращаться к данным, содержащимся в текущей базе, без посредства форм или каких-либо иных объектов Access, необходимо объявить объектную переменную типа Database. Пусть она называется MyDb:

Dim MyDb As Database

Чтобы оперировать данными, содержащимися в таблицах базы данных, нам потребуются объекты типа Recordset. Объекты такого типа могут представлять не только таблицу, но, как в данном случае, могут использоваться для доступа к таблице “Клиенты”:

Dim Klients As Recordset

Чтобы переменная MyDb представляла текущую базу данных (то есть базу, открытую в данный момент в окне MS Access), необходимо выполнить присваивание:

Set MyDb = CurrentDb

Далее, чтобы Recordset-объект указывал на таблицу “Клиенты”, следует воспользоваться методом OpenRecordset текущей базы данных:

Set Klients = MyDb.OpenRecordset("Клиенты")

Теперь свойства и методы объектной переменной Klients обеспечивают полный доступ к таблице “Клиенты”. Свойства, относящиеся к содержимому таблицы, то есть к полям ее записей, указывают всегда на одну запись, а именно на текущую запись. Перемещать указатель текущей записи можно при помощи специальных методов: MoveNext — перемещает указатель на одну запись вперед, MoveFirstа — устанавливает указатель на первую запись, и т. д.

Просмотр записей таблицы Access при помощи цикла Do While…Loop

Чтобы узнать, не достигнут ли конец таблицы, достаточно прочитать значение свойства EOF

— в конце таблицы оно вернет значение True. Таким образом, чтобы просмотреть таблицу “Клиенты” от начала до конца, следует воспользоваться конструкцией вида:

Klients.MoveFirst

Do While Not Klients.EOF

...

Klients.MoveNext

Loop

Новым здесь является оператор цикла Do While…Loop. Заключенные в нем операторы выполняются до тех пор, пока остается истинным условие While. Как только, благодаря вызовам метода MoveNext, будет достигнут конец таблицы, свойство EOF вернет значение True и условие

Not Klients.EOF примет значение False.

см. также в приложении раздел “Цикл Do … Loop”.

Чтобы не принимать во внимание записи, где не указана фамилия клиента, наложим дополнительное условие:

Klients.MoveFirst

Do While Not Klients.EOF

If Klients.Fields("Фамилия") <> "" Then

...

End If

Klients.MoveNext

Loop

158 Глава 6. Access: автоматизация офисной базы данных

Отсюда видно, как следует обращаться к значению поля в текущей записи Recordset- объекта. Его семейство Fields содержит все поля, а к элементами семейства можно обращаться по именам или по номерам.

Создание SQL-запроса

Итак, требуется проанализировать баланс операций по каждому клиенту. Для этого используем еще один объект типа Recordset, только на этот раз он будет представлять не таблицу целиком, а результат запроса к таблице. Какой запрос нужно создать? На обычном языке он должен звучать примерно следующим образом: “только те записи из таблицы “Операции”, которые в поле “КодКлиента” содержат значение, равное Klients.Fields("КодКлиента")”, то есть коду текущего клиента в ходе просмотра таблицы “Клиенты”. Используем для этого две строковые переменные (их необходимо предварительно объявить As String): В строку KodKlienta загрузим значение кода из текущей записи в таблице “Клиенты”:

KodKlienta = Klients.Fields("КодКлиента")

А в строковой переменной SQLstr сформируем SQL-запрос, то есть запрос к таблице на “языке структурированных запросов” SQL:

SQLstr = "SELECT * FROM Операции WHERE КодКлиента=" + KodKlienta

Если, например, код текущего клиента равен 7, то в результате получится строка SELECT * FROM Операции WHERE КодКлиента=7. Чтобы загрузить в Recordset-объект Operations (который, конечно, необходимо предварительно объявить) только те записи из таблицы “Клиенты”, которые удовлетворяют условиям запроса, необходим оператор следующего вида:

Set Operations = MyDb.OpenRecordset(SQLstr)

Просмотр записей, содержащихся в переменной Operations, можно организовать точно таким же образом, как это было сделано в отношении Recordset-объекта Klients:

Do While Not Operations.EOF

...

Operations.MoveNext

Loop

Чтобы выполнить подсчеты по операциям данного клиента, используем переменные денежного типа Currency: в переменной SumIn будут накапливаться суммы приходных операций, а в переменной SumOut — расходные суммы. Величину суммы можно найти в поле “СуммаОперации”, а тип операции определить по значению в поле “КодОперации”:

Do While Not Operations.EOF

If Operations.Fields("КодОперации") = 2 Then

SumIn = SumIn + Operations.Fields("СуммаОперации")

Else

SumOut = SumOut + _

Operations.Fields("СуммаОперации")

End If

Operations.MoveNext

Loop

Сразу после этого объект Operations необходимо “уничтожить”, и освободить занятую им память, поскольку при следующем проходе большого цикла нам потребуется совершенно другой объект Operations, отражающий операции по другому клиенту:

Автоматизация работы с имеющимися данными 159

Operations.Close

Set Operations = Nothing

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

If SumOut > SumIn Then

MsgBox("Отрицательное сальдо по клиенту " + _ Klients.Fields("Фамилия"))

...

End If

Использование функции MsgBox для организации интерфеса, предоставляющего пользователю выборочно управлять выполнением программы

Функция MsgBox позволяет не только выводить сообщения, но и сформировать интерфейс для принятия пользователем решения. Например, при помощи константы-параметра vbYesNo можно создать в окне сообщения кнопки Äà и Íåò (рис. 6.6).

Рис. 6.6. Окно сообщения содержит кнопки Да и Нет

Сравнивая возвращенное функцией MsgBox значение с соответствующими константами, можно определить, которую из кнопок выбрал пользователь:

If MsgBox("Отрицательное сальдо по клиенту " _ + Klients.Fields("Фамилия") + _

". Создать письмо?", vbYesNo, "Отрицательное сальдо") _ = vbYes Then

...

...

End If

Последний параметр функции MsgBox определяет здесь надпись в строке заголовка окна сообщения. Операторы между Then и End If получат управление только в том случае, если пользователь нажмет кнопку Äà. Внутри этой конструкции можно предпринять какие-то действия, например, сгенерировать готовое письмо данному клиенту.

Вопрос о предпринимаемых действиях будет рассмотрен чуть ниже, а пока закончим проход по таблице “Клиенты”.

см. в этой главе раздел “Использование объектов Word для формирования письма из данных таблицы Access”.

После того, как цикл просмотра Recordset-объекта Klients выполнит свою задачу, необходимо закрыть таблицу “Клиенты”, к которой подключен объект,

Klients.Close

и освободить память от всех объектов:

160 Глава 6. Access: автоматизация офисной базы данных

Set Klients = Nothing

Set MyDb = Nothing

На этом макрос завершает свою работу.

Принятие решения по результатам анализа данных

Программный код, который был только что разработан, служит для анализа данных, накопленных в базе данных. Цели анализа могут быть самыми разными, например, можно учитывать даты операций, выделяя какие-то диапазоны времени, или выделять клиентов по размеру сумм операций. В каждой конкретной базе данных есть свои актуальные задачи и цели. Но в чем смысл и цель анализа данных? Что должно явиться результатом?

Результат должен состоять в принятии каких-то решений и в выполнении каких-то действий. Действия эти могут быть самыми различными — в рассматриваемом примере логичным действием представляется создание письма нерадивому клиенту. Причем, поскольку данные текущего клиента у нас “под рукой”, а смысл ситуации вполне однозначен, содержание и реквизиты такого письма могут быть сгенерированы полностью автоматически. Для того чтобы и форматирование письма могло быть выполнено автоматически, потребуется использовать в VBA-коде модуля Access обращения к объектам Word.

Итак, создадим VBA-код, который должен выполняться в рамках конструкции If MsgBox…=

vbYes Then…, то есть в случае, когда пользователь при получении запроса на создание письма щелкнет на Äà.

Использование объектов Word для формирования письма из данных таблицы Access

Прежде всего, следует задать ссылку на объектную библиотеку Word, поскольку предполагается обращаться к объектам Word из программного модуля Access. Полагаем, детали такой операции читателю напоминать уже не требуется.

см. подробнее в гл. 2 раздел “Как задать ссылку на библиотеку объектов Word в среде MS Excel”.

Далее нужно объявить необходимые объектные переменные (сделать, это, конечно, лучше всего в начале кода функции):

Dim WD As Word.Application

Dim MyLetter As New LetterContent

Переменная WD будет представлять в этом примере приложение Word (такие объекты уже рассматриварись в предыдущих главах).

см. также в приложении раздел “Объектные переменные”.

Объекты типа LetterContent

Переменная MyLetter представляет собой нечто новое. (Новым является также слово New в объявлении этой переменной — благодаря этому слову экземпляр объекта будет создан автоматически, без каких-либо дополнительных усилий). Это специальный объект, служащий для создания писем. Объекты типа LetterContent являются своеобразными “переносчиками” параметров для Мастера писем Word. Все свойства такого объекта представляют собой реквизиты письма. Заполнив эти свойства значениями, можно запустить мастер писем (метод RunLetterWizard) и передать ему в качестве параметра этот объект. При этом все диалоговые окна мастера окажутся уже заполненными и пользователю останется только щелкнуть на кнопке Готово. Впрочем, можно обойтись и без появления мастера писем на экране, использовав метод SetLetterContent активного документа, — при этом документ автоматически превратится в правильно составленное и отформатированное письмо.

Автоматизация работы с имеющимися данными 161

Постановка задачи

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

WordRunning) логического типа Boolean, которая будет сигнализировать о факте запуска приложения Word. Если объект WD еще не создавался (WordRunning=False), то, при необходимости создать письмо следует его создать, присвоив затем WordRunning значение True. Если же окажется, что WordRunning=True, значит, создавать объект WD не требуется и достаточно лишь добавить новый документ в его семейство Documents.

Dim WordRunning As Boolean

...

WordRunning = False

...

If Not WordRunning Then

Set WD = CreateObject("Word.Application") WD.Visible = True

WordRunning = True

End If

В самом конце функции Анализ_Баланса, переменная WordRunning понадобится нам еще один раз:

On Error Resume Next

If WordRunning Then WD.Quit

Если окно Word в процессе анализа открывалось, то будет предпринята попытка его закрыть. Пользователь получит запрос на сохранение документа при закрытии и будет иметь возможность щелкнуть на кнопке Отмена. Если Word запускался, но уже закрыт пользователем, то возникнет ошибка выполнения, которая будет проигнорирована благодаря оператору On Error Resume

Next.

Автоматическая генерация письма клиенту

Итак, переходим непосредственно к автоматической генерации письма клиенту.

Вначале создадим текст собственно письма, который присвоим в качестве значения строковой переменной LetterText, предварительно объявив ее, конечно, как String:

LetterText = _

"Считаем необходимым обратить Ваше внимание на то," + _ "что Ваш баланс в операциях с нашей компанией " + _ "принял отрицательное значение и составляет " + _

Str(SumIn - SumOut) + " руб."

Эта конструкция, при помощи функции Str, встраивает в текст значение отрицательного баланса по текущему клиенту таблицы “Клиенты”. Для удобства выполнения действий с многочисленными свойствами объекта типа LetterContent, следует воспользоваться оператором

With…End With:

With MyLetter

162 Глава 6. Access: автоматизация офисной базы данных

Свойство DateFormat отвечает за дату письма. При помощи функции Date$ присвоим ему текущую системную дату:

.DateFormat = Date$

Включим отображение верхнего и нижнего колонтитулов:

.IncludeHeaderFooter = True

Выберем шаблон письма из папки шаблонов, пусть это будет Современное письмо (разумеется, местонахождение папки шаблонов Office — в данном случае это папка Templates — может меняться от компьютера к компьютеру):

.PageDesign = _ "D:\Office\Templates\1049\Современное письмо ltr.dot"

Выберем стиль письма при помощи свойства LetterStyle:

.LetterStyle = wdModifiedBlock

Чтобы задать имя получателя, нужно обратиться к текущей записи таблицы “Клиенты” и “склеить” значение свойства RecipientName из полей “Имя”, “Отчество” и “Фамилия”:

.RecipientName = _ Klients.Fields("Имя") + _

"" + Klients.Fields("Отчество") + _

"" + Klients.Fields("Фамилия")

Адрес получателя (свойство RecipientAddress) получить гораздо проще, — для него в таблице отведено специальное поле:

.RecipientAddress = Klients.Fields("Адрес")

В качестве “инструкций по пересылке” (свойство MailingInstructions) укажем “ДЕЛОВАЯ КОРРЕСПОНДЕНЦИЯ”:

.MailingInstructions = "ДЕЛОВАЯ КОРРЕСПОНДЕНЦИЯ"

Далее нужно позаботиться о метке (свойство AttentionLine): “Внимание”:

.AttentionLine = "Внимание!"

Тема письма задается свойством Subject:

.Subject = "Отрицательный баланс"

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

.ReturnAddress = "ООО Универсал, малая Ивановская 1"

Имя отправителя — вероятно, это должно быть имя ответственного работника фирмы:

SenderName = "Петров П.П."

Строка “уважений и наилучших пожеланий” определяется свойством Closing:

.Closing = "С уважением,"

Наименование организации:

Соседние файлы в папке Книги