Продолжая разговор о связи Access и других приложений необходимо четко знать структуру того приложения, которым мы собираемся управлять. Волею судеб получилось так, что первое приложение, краткий экскурс по которому я хочу провести, будет Word.
Структура Word рассматривается в основном структура word97(8), но будут ссылки и на 9 версию.
Рассматривать все объекты и классы я не буду, но важные обьекты структуры будут мной отмечены. За описанием остальных обращайтесь к литературе, встроенной помощи и другим источникам.
Для получения "структурного дерева" приложения Word нажмите F1 в окне редактора кода VBA Word и в сведениях о программировании найдите пункт Microsoft Word Objects(Объекты Word). Там вы обнаружите более 35 коллекций и объектов. Обращаю ваше внимание только на объекты ,необходимые для понимания прочитанного материала в дальнейшем.
Объектная модель Word представляет собой иерархию объектов, подчиненных одному объекту application. Большинство объектов используют только библиотеку Word, однако, некоторые ссылаются и на другие библиотеки (например, объект assistant - помощник ссылается на библиотеку объектов office)
табличка объектов первого уровня иерархии объектной модели Word.(элементы с
пометкой "2000" доступны соответственно только в версиях Word начиная с
9)
Быстро пробежав по основным объектам, обратим свое внимание на выделенные объекты (семейства). Т.к. именно они будут часто использоваться в дальнейшем.
Хочется еще отметить, что объект Selection вынесен в верхушку иерархии Word и обращается к выделенной области или месту вставки активного документа приложения. Т.е. надо быть аккуратным, при обращении к свойствам этого объекта, пока открыты несколько документов Word, и не забывать, что Selection принадлежит объекту Application. И он единственный.
Семейство Documents предназначено для управления документами Word. Это наиболее употребляемый объект и поэтому приведу описание основных объектов и семейств подчиненных этому объекту.
Табличка объектов, подчиненных объекту Document .
В этой табличке были выделены те элементы
объекта document, что принадлежат семейству range
(диапазон).
Именно через этот элемент происходит доступ к любому
месту/символу/вставке/и т.д. Как вы успели, наверное, заметить, любой символ
документа принадлежит к семейству range. Причем один символ еще принадлежит к
подразделу characters, группа символов ограниченных
пробелами (точнее заканчивающиеся пробелами или знаками препинания) - к
подразделу words, набор символов ограниченных точками с
пробелом (переводами каретки, и т.п.) - к Sentences. На
мой взгляд - замечательная задумка. Само собой, что там, где есть что-то хорошее
- там есть и плохое.
Плюсы, наверное, очевидны:
* Быстрый доступ к
любому элементу - слову, предложению, и т.п.
* Проверка слов (символов,
предложений) происходит автоматически при добавлении чего-либо в семейство.
* Удобная навигация перехода - посимвольно, пословно, по предложениям.
*
Поиск по семейству проще.
* Статистика автоматическая - выделил - получил
отчет о наличии чего-либо.
Эти огромные преимущества над многими иными
способами отметки элементов текста, к сожалению, немного смазываются некоторыми
неприятностями.
* Большой текст - много элементов семейства - документ
сильнее загружает машину при работе с ним.
* Известно, что, например, работа
с массивом происходит быстрее.
* Ввиду заложенных особенностей каждого
элемента семейства range не всегда корректно работает поэлементная статистика,
для предотвращения чего навешивается проверка ошибок статистики и проч.
Нет, конечно же, Range работает правильно. Именно так, как и должно быть
по правилам элементов семейств. Но не всегда так, как хочется. Например: Данилов
М.Е. - Это 6 слов (точка - самостоятельное слово, пробел к словам не относится,
далее следует место вставки т.е. курсор - это тоже слово), и одно предложение.
Но проверка насильно мне указывает, что я забыл пробел после первой точки.
Ставим пробел - получили 6 слов, и 2 предложения. Именно этот момент чаще всего
вынуждает при авто переносе текста из какого-нибудь другого приложения office
использовать другие механизмы передачи данных (поля, закладки, переменные). Но
если вы точно знаете, сколько слов и предложений будет (было), то раскидать по
местам нужные нам данные не составит очень уж большого труда.
Принципы использования основных методов и свойств того или иного семейства Word не имеют особых различий с принципами общей работы с семействами в VBA. Поэтому обращать внимание на них нет необходимости, а при наличии таковой обращайтесь к встроенной помощи Word, там все понятно написано. А пока пример, на использование основных, отмеченных элементов.
Задача.
* Открываем новый документ на основе шаблона normal.
*
Проверим, нет ли там чего ненужного (слов, и т.п.).
* Создаем и вставляем
текст.
* Находим 10 Слово.
* Меняем его на него же, но с заглавной
буквы.
* Находим слово аксессс и меняем его на Access
* Находим символ
[@], причем не позднее 3-го предложения, меняем его на [№].
* Ищем "господа"
и добавляем ", а также дяденьки и тетеньки".
Все действия производим,
используя механизмы Word.
Public Sub
text_in_word() Dim word_obj As Object ' не стоит забывать что так тоже можно обьявлять переменную. Dim const_input_txt As String ' Это тот кусок текста, что нам надо перенести в ворд Dim Full_name_new_doc As String 'ПОЛНОЕ имя нашего документа будет лежать здесь Dim Name_new_doc As String 'имя нашего документа будет лежать здесь Dim i As Integer 'пригодится, мало ли чего..... :) Dim range_word As Word.Range 'а вот это стадия раннего связывания. {О раннем и позднем связывании можно прочитать здесь}. ' Ниже будет понятно зачем оно нам надо (не связывание а переменная) Dim range_character As Word.Range 'Ниже будет понятно зачем оно нам надо Dim range_sentences As Word.Range 'Ниже будет понятно зачем оно нам надо '' ----------стандартный модуль открытия объекта -------- Err.Clear 'очистка ошибок On Error Resume Next Set word_obj = GetObject(, "word.Application")'Проверили, вдруг ворд уже открыт If Err = 429 Then ' ну коли закрыт - откроем Set word_obj = CreateObject("word.Application") 'так или иначе, объект мы получили ElseIf Err <> 0 Then MsgBox "error" & Err ' если какая то еще ошибка - например пользователь не имеет доступа End If ' к ворд - пишем какая ошибка If Err <> 429 And Err <> 0 Then Exit Sub Else Err.Clear ' и выйдем, если ошиблись On Error GoTo 0 ' при этом еще происходит очистка ошибки аналогично Err.Clear '' ----------Ваяем далее -------- ''-----------Проверка свободного файла ----------------- On Error Resume Next If Dir("c:\text_in.doc", vbNormal) <> "" Then Kill "c:\text_in.doc" 'Этот файл будет шаблоном 'для последующего переноса данных. Если он уже есть тогда его удалим. If Err = 75 Then MsgBox "А кто то с этим файлом работает...." 'если файл занят другими пользователями Exit Sub ' остановим процедуру ElseIf Err <> 0 Then MsgBox Err ' при условии что призошла ошибка файловой системы Exit Sub ' тоже остановим процедуру End If On Error GoTo 0 ''------------------- место для файла есть, объект создали -------- ''----- Откроем новый файл, с шаблоном Normal --------- With word_obj 'Что бы не писать далее много раз имя переменной. 'И работать будет немного быстрее .Visible = True 'а то не видно, чего там происходит :) .Documents.Add Template:="normal.dot", NewTemplate:=False, _ DocumentType:=wdNewBlankDocument, Visible:=True ' добавили ЧИСТЫЙ НОВЫЙ документ, шаблон у него будет normal, он станет активным при открытии ' и будет именно документом, а не шаблоном Full_name_new_doc = .ActiveDocument.FullName ' получили ПОЛНОЕ имя нашего нового документа Name_new_doc = .ActiveDocument.Name ' получили имя нашего нового документа const_input_txt = "Уважаемые дамы и господа! Оповещаем Вас, что надо обработать " & _ "все нижеперечисленные заказы и развезти их по адресам:" & vbCrLf & " Заказ#234/исп " & _ vbCrLf & "тот кто не имеет аксесс к необходимым документам, тот будет отстранен до выяснения " & _ "причин присутствия наличия отсутствия!" 'Тут я использовал vbcrlf,как перенос строки, для получения 'более структурно различимого текста в документе. Но даже перенос можно было обработать програмно ' добавлением в слова перенос строки. If .ActiveDocument.FullName <> Full_name_new_doc Then .Documents(Name_new_doc).Activate If .ActiveDocument.Words.Count > 1 Then ' дело в том что в чистом документе есть 1 "слово" 'Это место вставки. For i = .ActiveDocument.Words.Count To 2 Step -1 'Задом наперед для того что бы не возиться с ' do while - end .ActiveDocument.Words(i).Delete 'убираем все слова поочередно, начиная с последнего 'я, конечно же, понимаю, что в НОВОМ документе нет ничего. Этот кусок нужен тем, кто работает ' с существующими документами. Next End If .Selection.Text = const_input_txt ' Использовали объект selection для вставки текста. 'т.к. именно наш документ сейчас активен - то все нормально.Но все же я проверил какой документ активен. 'ввиду того, что никаких выделеных областей в новом документе нет, а есть только область вставки '(палочка-курсор), то вставится именно в это место. If .ActiveDocument.Words.Count > 10 Then 'Мы то знаем что слов вставили более 10 шт. Но мало ли чего. .ActiveDocument.Words(10).Case = wdTitleWord 'Удобный метод range, по смене регистра For Each range_word In .ActiveDocument.Words 'Поиск во всем диапазоне слов If range_word.Text = "аксессc " Then range_word.Text = "Access " ' слово должно оканчиваться пробелом ' или знаком препинания или спец сиволом Next End If Set range_sentences = .ActiveDocument.Range(Start:=.ActiveDocument.Sentences(1).Start, End:=.ActiveDocument.Sentences(3).End) ' сделали конецом диапазона третье предложение ' сделали началом диапазона первое предложение range_sentences.Select For Each range_character In range_sentences.Characters ' только для 3х предложений If range_character.Text Like "@" Then range_character.Text = "№" ' like тоже можно использовать Next ' последнее, это добавление новых элементов в коллекцию. ' много вариантов. Например использовать метод replace ' можно использовать метод find, а потом selection и вставить текст ' ну раз уж мы используем коллекции. У words нет явного метода add. Думаю это сделано для ' избежания неразберихи, при введении этого метода явно. ' а вот метод insertafter (before) как пить дать то что надо! For Each range_word In .ActiveDocument.Words If range_word.Text = "господа" Then range_word.InsertAfter ", а также дяденьки и тетеньки" Next ' ну вроде все что обещался - сделал. Причем только методы ворд использовал. Full_name_new_doc = "c:\text_in.doc" 'Задали имя для записи документа .ActiveDocument.saveas filename:=Full_name_new_doc, fileformat:=wdFormatDocument 'вообще 'SaveAs имеет много параметров, так что я часть их просто не использовал .ActiveDocument.Close 'Закрыл именно этот документ If .Documents.Count = 0 Then .Application.Quit SaveChanges:=wdDoNotSaveChanges 'А вот тут как раз проверили, если нет более документов - можем вообще закрыть приложение. End With Set word_obj = Nothing 'очистили память End Sub |
Модуль запускается из Access и создает документ на диске С в корневом каталоге. Этот пример при пристальном осмотре должен научить использовать основные элементы семейства range объекта document приложения Word. В принципе, все вышеперечисленные механизмы позволят быстро ориентироваться в теле любого документа. Одно большое НО. Для передачи данных из другого приложения и создания шаблона надо выстроить четкий алгоритм расстановки данных.
Самое неприятное, что из-за 1 неправильного символа все может "поплыть". Тогда полученный документ нельзя будет назвать созданным автоматически - его же придется потом подправлять руками. Те механизмы, что я привел, легко позволят забрасывать в чистый новосозданный файл какие-то данные без каких-либо затруднений. Указывая разные диапазоны вставки, можно заполнять и уже существующие документы, хотя, что бы не испортить уже существующие данные надо четко представлять себе процесс работы. Самым важным фактором этого метода является "наличие отсутствия" как такового файла шаблона создаваемого документа, т.е. посредством него можно сделать т.н. "Защиту от Дурака" - любителя стирать непонятно зачем файлы типа "Шаблон_моей_программы.dot"
Что касается остальных, не менее важных элементов Document, то оставим их на следующий раз.
Напоследок маленький, но забавный и не особо нужный в работе пример. Кувыркатель индикатора клавиатуры (язык при этом меняется только в Word) при запуске модуля из Access. Не знаю, то, что в других приложениях office язык остается тем же, хотя индикатор сменился, правильно это или нет. Вам судить. Но, как факт, такое возможно:
Public Sub
shifted_lang() Dim word_obj As Word.Application 'Требует раннего связывания Err.Clear 'очистка ошибок On Error Resume Next Set word_obj = GetObject(, "word.Application") 'Проверили, вдруг ворд уже открыт If Err = 429 Then ' ну коли закрыт - откроем Set word_obj = CreateObject("word.Application") 'так или иначе, объект мы получили ElseIf Err <> 0 Then MsgBox "error" & Err ' если какая то еще ошибка - например пользователь не имеет доступа End If ' к ворд - пишем какая ошибка If Err <> 429 And Err <> 0 Then Exit Sub Else Err.Clear ' и выйдем, если ошиблись On Error GoTo 0 ' при этом еще происходит очистка ошибки аналогично Err.Clear If word_obj.Keyboard = 68748313 Then word_obj.Keyboard (1033) Else word_obj.Keyboard (1049) 'если английский - переключили на русский и наоборот. If word_obj.Documents.Count = 0 Then word_obj.Application.Quit SaveChanges:=wdDoNotSaveChanges 'Проверили, если нет более документов - можем вообще закрыть приложение. Set word_obj = Nothing 'очистили память End Sub |
Этот момент показывает наличие некоторых удобных, но недоступных Access, механизмов Word. Так что ждем. Вдруг и у Access такое будет возможно. Без WinApi.
Подведем итог всего, что тут написано.
Приложение Word достаточно сложно и имеет множество (мало сказано J ) элементов, способов и проч. Навигацию по документам можно легко осуществлять программно через метод range. Создание нового документа и передача в него данных из Access с использованием вышеприведенного метода сложно, однако при разработке четкого алгоритма позволит обойтись без лишнего шаблона. Доступ к механизмам замены, сортировки, и статистики можно также получить только через этот метод.
Остальные свойства/методы/элементы будут мною рассмотрены в дальнейшем.
С уважением, MAKC.
Danilov_maks@mail.ru http://mytraffic.narod.ru
Все приведенные модули проверены и работают.
О найденных неточностях
прошу сообщать на почтовый ящик.
При написании этой статьи использовалась литература издательства BHV г. Санкт - Петербург.Серия "Мастер".