Работа с документами и класс Document
Вспомним, что мы уже знаем о документах Word. Когда открывается приложение, создается коллекция документов Documents, содержащая открытые документы. Если приложение Word создается в момент открытия документа Word, (заметим, что объект Word.Application может быть создан и в программном проекте другого приложения, например, Excel), то в начальный момент коллекция содержит минимум один новый или ранее существовавший документ. Программно новый документ добавляется в коллекцию методом Add, а уже существующий - методом Open объекта Documents. Чтобы добраться до нужного документа в коллекции, достаточно указать его индекс - имя файла, хранящего документ, - или его порядковый номер в коллекции. Для той же цели можно использовать и метод Item, но обычно он опускается. Метод Save позволяет сохранить документ, а метод Close, сохраняя документ в файле, закрывает его и удаляет из коллекции.
При применении метода Open обязательно указывать имя, а точнее путь к открываемому файлу. Однако задание конкретного пути всегда чревато неприятностями, поскольку при любом переносе системы местоположение файла может измениться. В одной из предыдущих процедур этой лекции WorkWithSearch я демонстрировал возможность использования свойства Path для нахождения пути к документу. Часто предпочтительнее предоставить пользователю возможность выбирать открываемый файл, хранящий документ. Для этого, конечно, можно использовать объект Dialog, при вызове методов которого открывается соответствующее диалоговое окно.
Вот еще один пример использования уже знакомого нам объекта Dialog FileOpen:
Public Sub WorkWithDialogs()
Dim dlg As Dialog
'Открытие документа в диалоге с пользователем
'Метод Show ведет диалог и открывает документ
Dialogs(wdDialogFileOpen). Show
'Метод Display ведет диалог, не открывая документа,
'но позволяя получить имя файла.
Set dlg = Dialogs (wdDialogFileOpen)
If dlg. Display = -1 Then 'нажата кнопка Open
Documents.Open FileName:=dlg.Name
End If
End Sub
Листинг 1.26.
Теперь подробнее рассмотрим свойства и методы документа - объекта класса Document. Это основной объект, свойства, события и методы которого следует знать основательно. Объект Document не менее сложен, чем объект Application.
Коллекции объекта Document:
Рассмотрим список коллекций, входящих в состав объекта Document:
Bookmarks
Characters (Range)
CommandBars
Comments
DocumentProperties
Endnotes
Fields
Footnotes
FormFields
Frames
Hyperlinks
Indexes
InlineShapes
HorizontalLineFormat
ListParagraphs
Lists
ListParagraphs
Range
ListTemplates
ListLevels
Font
Paragraphs
ProofreadingErrors (Range)
Revisions
ReadabilityStatistics
Scripts
Sections
Sentences (Range)
Shapes
StoryRanges (Range
Styles
Subdocuments
Tables
TablesOfAuthoritiesCategories (TableOfAuthoritiesCategory)
TablesOfAuthorities (TableOfAuthorities)
TablesOfContents (TableOfContents)
TablesOfFigures (TableOfFigures)
Variables
Versions
Windows
Words (Range)
Среди объектов, вложенных в объект Document на первом уровне иерархии, коллекции составляют явное большинство - их 36. Каждая коллекция содержит элементы одного класса. Как правило, имя класса коллекции строится как множественное число (по правилам английского языка) от имени класса элемента коллекции. Например, коллекция Documents содержит объекты класса Document, а коллекция Paragraphs содержит объекты класса Paragraph. В тех случаях, когда это правило не выполняется, в скобках указано имя класса для объектов, входящих в коллекцию. Заметьте, например, что коллекции предложений, слов и символов документа Word состоят из объектов класса Range.
Для некоторых из объектов этого списка указаны и встроенные в них объекты, лежащие на следующем уровне иерархии. В этих случаях курсивом выделены те объекты, которые не являются коллекциями.
Простые объекты, вложенные в объект Document:
Объектов, вложенных на верхнем уровне иерархии в объект Document, существующих в одном экземпляре и не имеющих соответственно коллекций, сравнительно немного. Вот их список:
Email
EmailAuthor
Envelope
Frameset
HTMLProject
LetterContent
MailMerge
MailMergeDataSource
MailMergeFields
PageSetup
LineNumbering
TextColumns
Range
RoutingSlip
VBProject
WebOptions
В Office 2000 у объекта Document появились три новых объекта в сравнении с предыдущей версией. Это объекты: Email, Frameset и WebOptions.
Итак, только на верхнем уровне в объект Document встроено около 50 объектов, определяющих его свойства. Большинство из этих объектов устроены достаточно сложно, - почти все содержат коллекции внутри себя. Попробуем упорядочить эту "тьму" объектов.
Классы, задающие структуризацию текста документа:
Текст - это основа большинства документов. Его можно структурировать, оперируя различными единицами при решении тех или иных задач преобразования текста. Минимальной единицей текста является символ. Другие естественные единицы - это слова, предложения, абзацы. Более крупными частями текста являются страницы, параграфы и главы. Важно уметь оперировать с последовательностями таких единиц. Какие же классы объектов предоставляет Word для этой цели?
Классы Characters, Words, Statements, Paragraphs позволяют работать с последовательностями (коллекциями) символов, слов, предложений, абзацев. Может показаться удивительным, но классов, соответствующих таким элементам, как символ, слово или предложение, нет. Элементом коллекций Characters, Words и Statements является объект класса Range. Это один из самых важных объектов, необходимых для понимания работы с текстами. Объект Range позволяет работать как с одним символом, так и с последовательностью символов. Документы, поддокументы, абзацы, разделы - все они имеют метод или свойство Range, возвращающее объект Range, представляющий область, связанную с объектом, вызвавшим метод (свойство) Range. Эту область можно рассматривать как интервал, задаваемый первым и последним символом текста данной области. Поэтому работа с текстом, так или иначе, ведется через методы и свойства объекта Range.
Чуть позже мы продолжим знакомство с классами Characters, Words, Statements, Paragraphs и Range.
Документ и его части:
До сих пор мы говорили только о тексте как основной части документа Word. Но документ имеет сложную структуру, и в нем можно выделить разные по назначению части. Каждой части документа, каждому понятию, определяющему эту часть, соответствует свой класс объектов, а чаще всего два класса, задающие объекты и их коллекцию. Рассмотрим основные части документа.
Разделы и поддокументы:
Сейчас мы обсудим более подробно две коллекции Sections и SubDocuments, задающие разделы документа и поддокументы, входящие в состав основного документа. Почти в каждом текстовом документе можно встретить символы, слова, предложения и абзацы. Что же касается более крупных единиц текста, то в разных документах они называются по-разному. Чаще всего, приходится встречаться со страницами и листами документа, но используются и такие термины как параграфы, главы, части, разделы документа. В объектной модели Word, к сожалению, нет таких объектов как Page и Pages, соответствующих таким естественным единицам текстового документа как страницы документа и их коллекции. В документе Word следующей крупной единицей после абзаца является раздел - объект класса Section. Все разделы одного документа составляют коллекцию Sections.
Что же такое раздел, как и для чего он создается? Раздел в документе всегда можно создать руками, для чего достаточно вставить символ разрыва документа - Section break, вызвав пункт Break из меню Insert. Символ разрыва может быть разного типа, задавая следующую страницу, четную или нечетную страницу, столбец и другие виды раздела. Таким образом, можно руками разбить документ на страницы, вставляя подходящие символы разрыва в нужных местах. В этом случае разделы будут выступать в роли страниц документа. Иногда разделы создаются автоматически, например, при представлении некоторой части документа в виде нескольких столбцов. Часть документа, представленная в виде нескольких столбцов, представляет отдельный раздел.
При программной работе разделы можно создавать двояко, используя метод InsertBreak, которым обладают объекты Range и Selection, или вызывая метод Add коллекции Sections. Метод Add(Range, Start) имеет два параметра:
Range задает область, начинающую новый раздел, или, что тоже, область, перед началом которой будет вставлен символ разрыва.
Start задает тип символа разрыва.
Заметьте, в методе InsertBreak указывается только второй параметр, поскольку новый раздел начинается с объекта Range (Selection), вызвавшего метод.
Рассмотрим пример, в котором документ с именем DocThree, программно разбивается на разделы. Наше разбиение гарантирует, что соответствующие главы и параграфы документа находятся в отдельных разделах, что облегчает дальнейшую работу с ними. Вот как выглядит процедура, решающая эту задачу:
Public Sub WorkWithSections()
'В документе DocThree создаются разделы документа
'Вначале документ не имеет разделов
Dim sect As Section
Dim myr As Range
Documents("Docthree").Activate
With ActiveDocument
Debug.Print "В документе разделов - ", .Sections.Count
'Выделение последнего раздела
.Sections(.Sections.Count).Range.Select
Debug.Print "Абзацев в разделе - ", Selection.Paragraphs.Count
Debug.Print "Предложений в разделе - ", Selection.Sentences.Count
Debug.Print "Слов в разделе - ", Selection.Words.Count
Debug.Print "Символов в разделе - ", Selection.Characters.Count
'Добавление разделов
Set myr = .Paragraphs(11).Range
myr.Select
myr.InsertBreak wdSectionBreakNextPage
Set myr = .Paragraphs(18).Range
.Sections.Add myr, wdSectionEvenPage
Set myr = .Paragraphs(29).Range
.Sections.Add myr
.Sections.Add
'Повторная печать после создания новых разделов
Debug.Print "В документе разделов - ", .Sections.Count
'Выделение первого раздела
Set sect = .Sections(1)
Debug.Print "Абзацев в разделе - ", sect.Range.Paragraphs.Count
Debug.Print "Предложений в разделе - ", sect.Range.Sentences.Count
Debug.Print "Слов в разделе - ", sect.Range.Words.Count
Debug.Print "Символов в разделе - ", sect.Range.Characters.Count
End With
End Sub
Листинг 1.27.
В этой процедуре показано, как происходит выделение раздела, подсчет некоторых характеристик раздела, например, подсчет числа абзацев, предложений и символов раздела. Показано, как создаются разделы документа, используя как метод InsertBreak так и метод Add коллекции Sections. Метод Add вызывается как с явно заданными параметрами, так и параметрами, задаваемыми по умолчанию.
Перейдем теперь к рассмотрению понятия поддокумент и способов работы с объектами, задающими поддокументы. Есть некоторый разумный предел размера одного документа. Если в документе больше 10-20 страниц, работать с ним становится неудобно. В этом случае в нем целесообразно выделить главный документ и поддокументы. Главный документ и поддокументы являются, по сути, документами, связанными ссылками, с каждым из которых можно работать независимо. Вот пример выделения поддокумента из главного документа:
Public Sub WorkWithSubDoc()
'Работа с поддокументами
Dim DocPath As String
Dim myr As Range
'Открываем и активизируем документ DocThree
DocPath = Documents("DocOne").Path
Documents.Open (DocPath & "\Docthree")
Documents("Docthree").Activate
With ActiveDocument
Debug.Print "Число поддокументов =", .Subdocuments.Count
If .Subdocuments.Count = 0 Then
If .Sections.Count = 1 Then 'Выделение разделов
WorkWithSections
End If
'Выделение поддокумента, начиная с третьего раздела и до последнего
Set myr = .Range(Start:=.Sections(3).Range.Start, _
End:=.Sections(.Sections.Count).Range.End)
.Subdocuments.AddFromRange myr
Debug.Print "Теперь число поддокументов =", .Subdocuments.Count
End If
End With
End Sub
Листинг 1.28.
Наш документ DocThree вначале не имел поддокументов. Этот документ предварительно разбивается на разделы, если это еще не было сделано, а затем в нем выделяется поддокумент, начиная с третьего раздела и кончая последним разделом документа. Метод AddFromRange класса SubDocuments создает поддокумент, выделяя из главного документа область, заданную параметром Range.
Заметьте, область, задаваемая параметром Range, должна начинаться с заголовка, имеющего один из стандартных стилей Heading. При сохранении документа, поддокумент удаляется из документа и сохраняется в отдельном файле. Имя файла строится автоматически, используя текст заголовка, начинающего поддокумент. В главном документе остается ссылка на поддокумент. При желании его всегда можно слить с главным, но с ним можно работать и как с независимым документом. Вот как выглядит наш главный документ после того, как в нем выделен поддокумент:
Рис. 1.9. Главный документ с выделенным поддокументом
Таблицы в документах:
Таблицы, таблицы, таблицы… Серьезных документов без них не бывает. Они могут быть разных типов. С документом Word связываются следующие классы коллекций, задающие те или иные таблицы: Tables, TablesOfContents, TablesOfFigures, TablesOfAuthoritiesCategories, TablesOfAuthorities.
Класс Table определяет "обычные" таблицы с произвольным количеством строк и столбцов и произвольным заполнением полей. Остальные классы задают таблицы специального вида. В следующем примере открывается документ с именем DocThree и в него вставляется несколько таблиц:
В начало документа вставляется специальная таблица, позволяющая автоматически создать оглавление нашего документа. Эта таблица представляет элемент коллекции TablesOfContents. Замечу, что автоматическое создание оглавления предполагает использование для заголовков документа стандартных стилей. Если же Вы используете для этих целей собственные стили с именами, отличными от Heading, то программно создать оглавление не удастся.
В конец этого документа вставляется обычная таблица. Здесь же происходит заполнение ячеек таблицы, что позволяет продемонстрировать возможности программной работы с элементами таблицы.
На следующем шаге процедуры WorkWithTables вызывается процедура, вставляющая в документ еще одну автоматически заполняемую таблицу специального вида элемент коллекции TablesOfFigures. О ней поговорим чуть позже, а пока приведем текст процедуры, решающей эти три задачи:
Public Sub WorkWithTables()
'Работа с таблицами
Dim DocPath As String
Dim myr As Range
Dim ToF As TableOfFigures
Dim MyTable As Table
Dim i As Integer, j As Integer
'Открываем и активизируем документ DocThree
DocPath = Documents("DocOne").Path
Documents.Open (DocPath & "\Docthree")
Documents("Docthree").Activate
With ActiveDocument
'Создание оглавления - Table of Contents
.TablesOfContents.Add .Range(Start:=0, End:=0)
'Создание обычной таблицы myTable в конце документа
Set MyTable = .Tables.Add(Range:=.Paragraphs.Last.Range, _
NumRows:=2, NumColumns:=3)
'Заполнение таблицы
For i = 1 To MyTable.Rows.Count
For j = 1 To MyTable.Columns.Count
MyTable.Cell(i, j).Range.InsertAfter i + j
Next j
Next i
End With
'Вызов процедуры работы с таблицами ссылок
WorkWithTablesOfFigures
End Sub
Листинг 1.29.
Поскольку обычные таблицы является частью большинства документов Word, то стоит сказать о них чуть подробнее. При работе вручную таблицы можно вставлять в документ двумя способами. Более простые таблицы с фиксированным числом строк и столбцов можно вставить в документ, вызвав из меню Table (Таблица) пункт Insert (Вставить). В открывшемся диалоговом окне можно указать число строк и столбцов добавляемой таблицы и некоторые ее свойства, управляющие размерами ее ячеек. Если же необходимо построить таблицу более сложного вида, то из меню Table вызывается пункт Draw (Рисовать). В этом случае в руках у пользователя "появляется" карандаш и с его помощью можно нарисовать таблицу довольно сложной конфигурации. Я уже говорил ранее, что MacroRecorder не может следить за действиями пользователя, рисующего такую таблицу. Однако он вполне справляется, когда пользователь вставляет таблицу с фиксированным числом строк и столбцов и, например, заполняет ее ячейки. Собственно говоря, объект Table и работа с ним в предыдущей процедуре является программным отображением моих действий по созданию и работе с такими таблицами. Возникает естественный вопрос, а можно ли программно создать таблицу сложной конфигурации, например, подобную таблице Менделеева, можно ли работать программно с такими таблицами? Ответ, естественно, положителен. Программное построение таблицы сложной конфигурации обеспечивается тем, что, используя метод Cell, можно получить доступ к любой из ячеек таблицы, (в предыдущем примере показано, как это можно сделать), а затем к отдельной ячейке можно применить метод Split, расщепив ее на нужное количество строк и столбцов. Вот пример программной работы с таблицей Менделеева:
Sub WorkWithDrawingTable()
'В этой процедуре демонстрируется работа
'с рисованной таблицей Менделеева
Documents("ExampleOfTable").Activate
Dim DrawTable As Table
Set DrawTable = ActiveDocument.Tables(1)
With DrawTable
Debug.Print "Столбцов - ", .Columns.Count
Debug.Print "Строк - ", .Rows.Count
Debug.Print .Cell(5, 1).Range.Text
'Усложняем конфигурацию
.Cell(4, 5).Split 2, 3
End With
End Sub
Листинг 1.30.
Замечу, что программно работать с такими таблицами довольно сложно, так как здесь трудно понять, какие индексы будет иметь та или иная ячейка таблицы, после того как, например, одна из ячеек таблицы расщеплена на несколько ячеек. Так что можно понять, почему MacroRecorder отказывается транслировать действия пользователя, рисующего сложную таблицу, и не может создать текст соответствующего макроса. Он (MacroRecorder) не может разобраться, с какой ячейкой работает пользователь в текущий момент. В заключение еще раз повторю, что программно работать с таблицами сколь угодно сложной конфигурации, допустимой в Office 2000, хотя и сложно, но вполне возможно.
Помимо "обычных" таблиц есть возможность создавать и работать с большим числом специальных таблиц, заполняемых автоматически при их создании. Пример одной из таких таблиц - таблицы, создающей оглавление документа, уже приведен. Есть еще несколько видов специальных таблиц, аналогичных таблице оглавления. Такие таблицы позволяют строить автоматически ссылки на иллюстрации, используемые в документах, на цитируемые источники и так далее. Рассмотрим теперь более подробно работу с еще одной из специальных таблиц, содержащей ссылки на иллюстрации, используемые в документе. В документах Word типичным является использование большого числа иллюстраций - таблиц, графиков, диаграмм. Зачастую, наряду с оглавлением документа полезно в документе иметь аналоги оглавления, содержащие ссылки на иллюстративные элементы документа. Для этой цели и используются специальные таблицы объекты класса TableOfFigures. Вот процедура, создающая две такие таблицы, первая из которых содержит ссылки на графики, вторая на таблицы:
Public Sub WorkWithTablesOfFigures()
'Работа с таблицами ссылок на иллюстрации документа
Dim DocPath As String
Dim myr As Range
Dim ToF As TableOfFigures
Dim capt As CaptionLabel
'Открываем и активизируем документ DocThree
DocPath = Documents("DocOne").Path
Documents.Open (DocPath & "\Docthree")
Documents("Docthree").Activate
With ActiveDocument
Set myr = Selection.Range
myr.Select
'Создаем таблицы ссылок на графики и таблицы
'Оба заголовка должны быть элементами коллекции CaptionLabels
.TablesOfFigures.Add Range:=myr, Caption:="График"
.TablesOfFigures.Add Range:=myr, Caption:="Table"
For Each ToF In .TablesOfFigures
Debug.Print ToF.Caption
Next ToF
For Each capt In Application.CaptionLabels
Debug.Print capt.Name
Next capt
End With
End Sub
Листинг 1.31.
Заметьте, что заголовки иллюстративных элементов, попадающих в соответствующие таблицы, должны быть "настоящими" заголовками и содержаться в коллекции CaptionLabels, о работе с которой я уже подробно рассказывал.
Рисунки и элементы управления. Объекты класса TableOfFigures:
Word предоставляет широкие возможности для появления рисунков в его документах. При работе вручную рисунки в документах появляются по-разному: во-первых, можно вставлять уже готовые рисунки в текст документа, можно также вставить элемент управления Image и связать с ним готовый рисунок, во-вторых, можно воспользоваться широким набором инструментов рисования, собранными на панели Drawing (Рисование). С помощью этих инструментов можно вставлять готовые рисованные объекты самых разных типов, начиная с прямоугольников и овалов, кончая блок-схемами, фигурными стрелками и звездами. Большие возможности представляют инструменты Curve (Кривая), FreeForm (Полилиния), Scribble (Рисованная Кривая). Чтобы добраться до них, необходимо на панели Drawing выбрать вкладку AutoShapes (АвтоФигуры), затем Lines (Линии), а затем нажать нужную кнопку. В этот момент в руках у пользователя "появляется" обычный карандаш, с помощью которого можно нарисовать любой рисунок произвольной (свободной) формы, проводя линии выбранным цветом и выбранной толщиной. После чего весь рисунок можно закрасить (залить) нужным цветом. Созданный рисунок можно вращать, растягивать или сжимать, в общем, выполнять основные операции, допустимые в графических редакторах.
Все, что можно делать вручную, можно делать и программно, поскольку есть соответствующие объекты со своими свойствами и методами. Все рисунки, размещаемые в документе в слое "рисования", с объектной точки зрения являются объектами класса TableOfFigures или InlineShape. Но, обратите внимание, коллекции TableOfFiguress(Shape), InlineShapes(InlineShape) содержат рисунки документа, но не только их! Рисунки это только один из возможных типов объектов, хранящихся в этих коллекциях. ActiveX- и OLE-объекты, рисованные тексты, созданные средствами Word Art, также являются элементами этих коллекций. Напомню, что элементы управления, размещаемые непосредственно в документе, являются OLE- объектами, об этом я уже говорил во введении. Но, заметьте, одновременно они являются и объектами класса TableOfFigures, точнее InlineShape. Так что при размещении в документе, например, командной кнопки или элемента Image, как бы оно не выполнялось вручную или программно, в коллекции InlineShapes появится новый элемент. Объекты разных типов, находящиеся в этих коллекциях, объединяет то, что всех их можно отнести к рисованным объектам и размещаются они в документе в слое рисования.
Коллекции TableOfFiguress и InlineShapes близки по своей природе. Как правило, при программном создании объектов многие из них можно поместить по желанию либо в коллекцию TableOfFiguress, либо в InlineShapes. Отличаются элементы этих двух коллекций тем, как они привязаны к документу - первые могут свободно перемещаться по документу, вторые жестко привязаны к заданной области документа и ведут себя подобно символам текста документа. Но, заметьте, элементы той и другой коллекции имеют метод Convert (ConvertToShape, ConvertToInlineShape), позволяющий конвертировать объект класса InlineShape в объект класса TableOfFigures и обратно. Коллекция TableOfFiguress содержит больше типов элементов, чем коллекция InlineShapes, поэтому преобразование не всегда возможно.
Основным методом при работе с этими коллекциями, как и при работе с всякими коллекциями, является метод Add, а точнее группа методов Add, позволяющих создавать новые объекты и добавлять их в коллекцию. Рассмотрим несколько модификаций метода Add, используемых при работе с этими коллекциями:
Прежде всего, следует упомянуть метод Addshape, первый параметр которого задает тип добавляемого объекта. Поскольку, как я уже говорил, типов рисованных объектов достаточно много, то соответствующая константа, задающая тип, может принимать около сотни различных значений. По этой причине, чаще всего, пользуются не этим общим методом создания TableOfFigures-объектов, а частными методами, позволяющими создавать TableOfFigures-объекты определенного типа.
Вот методы, позволяющие создавать TableOfFigures-объекты, аналогичные тем, что создаются вручную при работе с инструментами панели Drawing: AddCallout, AddCurve, AddLine, AddPolyline, BuildFreeForm. Последний из этих методов соответствует двум уже упоминавшимся инструментам FreeForm и Scribble. При работе с этим методом создается объект класса FreeForm, который затем преобразуется в объект класса TableOfFigures. Метод AddOleObject используется для создания OLE-объектов, в частности для создания элементов управления.
Метод AddOleControl используется для создания ActiveX объектов.
Метод AddTextBox позволяет создать текстовые окна.
Метод AddPicture позволяет добавлять рисунки в документ.
Метод AddTextEffect позволяет создавать художественные надписи, так как это делает Word Art.
У нас уже есть достаточное число примеров на использование этих методов.
В следующем примере я вставляю в документ два рисунка. Один из них будет добавлен в коллекцию TableOfFiguress, второй в коллекцию InlineShapes. Заметьте, во втором случае у метода AddPicture есть параметр Range, позволяющий "привязать" рисунок к определенному месту документа. Первый же рисунок можно свободно передвигать по документу.
Public Sub AddTwoShapes()
'добавляются рисунки в коллекцию TableOfFiguress и InlineShapes
Dim Item As AutoCaption
Dim MyPath As String
Documents ("DocOne").Activate
MyPath = ActiveDocument.Path
'Отключим вставку автозаголовка для рисунков
Set Item = Word.Application.AutoCaptions("Microsoft Word Picture")
item.AutoInsert = False
With ActiveDocument
'Рисунок добавляется в коллекцию TableOfFiguress
.Shapes.AddPicture FileName:=MyPath & "\cat.gif"
'Рисунок добавляется в коллекцию InlineShapes
'привязывается к первому параграфу документа.
.InlineShapes.AddPicture FileName:=MyPath & "\mouse.bmp", _
Range:=.Paragraphs.First.Range
End With
End Sub
Листинг 1.32.
Взгляните, как выглядит наш документ после добавления рисунков:
Рис. 1.10. Документ после добавления рисунков
Заметьте, кошку можно перемещать по документу, а мышка "привязана". Хочу обратить Ваше внимание еще на одну особенность этой процедуры. Мне пришлось перед вставкой рисунков отключить автоматическое добавление заголовка из-за небольших "жучков" в Office 2000.
При работе метода AddPicture в Office 2000 появляются два небольших "жучка", если для рисунков включен автозаголовок "Microsoft Word Picture". Во-первых, неверен текст заголовка, сопровождающего рисунок, он уведомляет о вставке таблицы (Table). Во-вторых, для объекта TableOfFigures текст заголовка закрывает сам рисунок.
При работе с визуальными объектами документа, которые, как теперь понятно, являются членами коллекции TableOfFiguress, часто необходимо выделить из всей коллекции некоторую совокупность объектов, объединить их в подколлекцию и работать с ней аналогично тому, как мы работаем с массивом. Для реализации такой возможности имеется специальный класс TableOfFiguresRange, который может содержать как одиночный объект, так и все объекты TableOfFigures документа, совпадая с коллекцией TableOfFiguress. Создать объекты этого класса (подколлекции) можно двояко. Первый способ состоит в том, что из коллекции TableOfFiguress явно выделяются некоторые элементы перечислением их индексов или имен и заданная совокупность становится объектом TableOfFiguresRange. Задать перечисление можно, используя свойство Range коллекции TableOfFiguress, сочетая это свойство с возможностью определить объект Range с помощью массива индексов Array. В нижеследующем примере такая возможность будет продемонстрирована. Другая возможность основана на том, что объект Selection имеет свойство TableOfFiguresRange, возвращающее коллекцию объектов TableOfFigures, входящую в область выделения. Заметьте, что выделенными должны быть объекты TableOfFigures, а не область текста документа.
Еще одна часто возникающая потребность при работе с визуальными объектами связана с необходимостью объединения группы объектов в один объект. Для этих целей коллекция TableOfFiguresRange имеет метод Group, возвращающий одиночный объект TableOfFigures. В последствии такой объект можно разгруппировать и создать на его основе подколлекцию.
Давайте рассмотрим теперь пример, в котором создается некоторая группа объектов TableOfFigures разного типа. В момент создания каждому из объектов дается имя, эти имена используются для создания подколлекции, содержащей эту группу объектов. Затем на основе подколлекции создается некоторый групповой объект. Вот как выглядит код, решающий все эти задачи:
Sub GroupOfShapes()
'Создание группы объектов TableOfFigures разных типов
'Создание объекта (коллекции) TableOfFiguresRange
'Группирование объектов
Dim SR As TableOfFiguresRange, SH As TableOfFigures
With ActiveDocument.shapes
'Добавляем текстовое окно. Координаты Left-Top-Width-Height относительно якоря
.AddTextbox(msoTextOrientationHorizontal, 220, _
40, 120, 30).Select
Selection.ShapeRange.Name = "Parts"
Selection.ShapeRange.TextFrame.TextRange.Select
Selection.Collapse
Selection.TypeText Text:="Части документа"
Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
'Добавляем стрелку
.AddLine(280, 70, 280, 100).Select
Selection.ShapeRange.Name = "Ar1"
Selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadTriangle
'Добавляем линию
.AddLine(120, 100, 440, 100).Select
Selection.ShapeRange.Name = "Lin1"
'Добавляем стрелку
.AddLine(120, 100, 120, 130).Select
Selection.ShapeRange.Name = "Ar2"
Selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadTriangle
'Добавляем стрелку
.AddLine(440, 100, 440, 130).Select
Selection.ShapeRange.Name = "Ar3"
Selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadTriangle
'Добавляем текстовое окно
.AddTextbox(msoTextOrientationHorizontal, 80, _
130, 120, 30).Select
Selection.ShapeRange.Name = "Part1"
Selection.ShapeRange.TextFrame.TextRange.Select
Selection.Collapse
Selection.TypeText Text:="Рисунки"
'Добавляем текстовое окно
.AddTextbox(msoTextOrientationHorizontal, 400, _
130, 120, 30).Select
Selection.ShapeRange.Name = "Part2"
Selection.ShapeRange.TextFrame.TextRange.Select
Selection.Collapse
Selection.TypeText Text:="Таблицы"
'Группирование объектов
Set SR = .Range(Array("Parts", "Ar1", "Lin1", _
"Ar2", "Ar3", "Part1", "Part2"))
SR.Select
HowManyShapes
Set SH = SR.Group
SH.Name = "Fig.1"
SH.Select
HowManyShapes
End With
End Sub
Листинг 1.33.
Текст процедуры получился довольно длинным, что объяснимо, поскольку в ней создается довольно большое число различных объектов. Тем не менее, надеюсь, что сделанные ранее пояснения и комментарии в тексте позволяют понять все детали ее работы. По ходу дела вызывается небольшая процедура HowManyShapes, текст которой стоит также привести, поскольку она демонстрирует второй способ создания коллекции TableOfFiguresRange:
Public Sub HowManyShapes()
Dim SR As TableOfFiguresRange
Dim SH As TableOfFigures
Set SR = Selection.ShapeRange
For Each SH In SR
Debug.Print SH.Name
Next SH
Debug.Print Selection.ShapeRange.Count
End Sub
Листинг 1.34.
Вот как выглядят отладочные результаты в окне проверки Immediate:
Parts Ar1 Lin1 Ar2 Ar3 Part1 Part2 7 Fig.1 1
Листинг 1.35.
Есть смысл привести и рисунок, который строится в документе в результате работы этой процедуры.
Рис. 1.11. Рисунок, созданный программно
Списки в документах:
Коллекции Lists(List), ListParagraphs(ListParagraph), ListTemplates(ListTemplate) используются при работе со списками. Списки широко используются в документах всякий раз, когда имеешь дело с перечислением, скажем, пунктов повестки дня или деталей какого-либо механизма или объекта. Списки можно оформлять в соответствии с некоторым шаблоном. Существуют две группы шаблонов: нумерованные списки и списки-бюллетени. Коллекция ListTemplates содержит шаблоны оформления списков, а класс ListTemplate описывает конкретный шаблон. Шаблон применяется к списку абзацев и придает ему структуру, заданную шаблоном. Коллекция Lists содержит те списки документа (списки абзацев), что оформлены как нумерованные списки или списки-бюллетени. Коллекция ListParagraphs представляет список абзацев всех списков документа. Свойством ListParagraphs, которое возвращает объект соответствующего класса, обладает не только документ, но и объекты List и Range. Так что при наличии списка - объекта List можно выделить список его абзацев. Чаще приходится выполнять обратную операцию - применять к списку абзацев один из возможных шаблонов, придав ему "настоящую" структуру списка. Тогда используют объект ListFormat, как показано в следующей процедуре:
Public Sub WorkWithLists()
'работа со списками
'Открываем документ DocTwo
Dim MyPath As String
Dim myRange As Range
MyPath = Documents("DocOne").Path 'должен быть открыт
Documents.Open MyPath & "\DocTwo.doc"
Documents("DocTwo").Activate
With ActiveDocument
Debug.Print "Списков в документе - ", .Lists.Count
Debug.Print "Они занимают -", .ListParagraphs.Count, " абзацев"
'Создаем новый список
Set myRange = .Range(Start:=.Paragraphs(3).Range.Start, _
End:=.Paragraphs(6).Range.End)
myRange. ListFormat.ApplyBulletDefault
Debug.Print "Теперь списков -", .Lists.Count
Debug.Print "Они занимают - ", .ListParagraphs.Count, "абзацев"
'Повторное применение отменяет форматирование
myRange. ListFormat.ApplyBulletDefault
Debug.Print "Теперь списков -", .Lists.Count
Debug.Print "Они занимают - ", .ListParagraphs.Count, "абзацев"
End With
End Sub
Листинг 1.36.
Заметьте, что для того чтобы отменить форматирование совокупности абзацев как списка, применяется тот же метод ListFormat. Приведем результаты отладочной печати:
Списков в документе - 1
Они занимают - 3 абзацев
Теперь списков - 2
Они занимают - 7 абзацев
Теперь списков - 1
Они занимают - 3 абзацев
Листинг 1.37.
Вначале в документе был один список из трех пунктов. Затем мы выделили некоторую область (4 абзаца), создали связанный с ней объект ListFormat и применили к нему шаблон списка-бюллетеня, вызвав метод ApplyBulletDefault. В результате появился еще один список заданной структуры из четырех пунктов, что подтверждает отладочная печать и что можно видеть при пошаговом выполнении этой программы. Заметьте, список абзацев теперь содержит все абзацы обоих списков. Использование объекта ListParagraphs может быть весьма полезным в ряде случаев, когда возникает необходимость обработки всех списков документа или его части. Обратите внимание, для отмены форматирования абзацев в виде списка я использовал тот же оператор, что и для включения форматирования.
Сноски, комментарии и исправления в документах:
При работе с документом в нем могут быть созданы некоторые важные элементы, невидимые при обычном просмотре и появляющиеся при принятии специальных мер. Такими частями документа являются сноски, комментарии и исправления. Этим понятиям соответствуют классы объектов и коллекций этих объектов: FootNotes(FootNote), EndNotes(EndNote), Comments(Comment), Revisions( Revision)
Рассмотрим их чуть более подробно:
Сноски один из способов комментирования документа, применяемый, как правило, автором документа. Сноски позволяют, не прерывая плавного изложения материала, дать определение применяемого термина или сделать ссылку на соответствующую литературу. Они могут быть двух видов: подстраничные (внизу страницы) и концевые (в конце документа). Для программиста подстраничные сноски составляют коллекцию FootNotes, концевые - EndNotes.
Класс Comment и коллекция Comments задают комментарии. Содержательно, комментарии могут быть как авторскими, так и комментариями рецензента. Обычно, когда я пишу свои тексты, у меня время от времени возникает желание прокомментировать те или иные свои высказывания, рассказать некоторые подробности, которые, возможно, излишни в основном тексте. Конечно, в этом случае можно было бы обойтись без комментариев и оформлять свои замечания особым стилем. Наибольшую ценность приобретают комментарии, когда созданный документ посылается на рецензию. У рецензента появляется возможность комментировать основные положения документа, не меняя сам документ и не производя в нем явных вставок. Так я поступаю, когда читаю присланные мне работы моих студентов. При чтении документа с комментариями их всегда можно увидеть либо на странице комментариев, либо установив курсор мыши на номере комментария. Так что комментарий это важный элемент при совместной работе над документом. Заметьте, что объект Comment содержит не только текст комментария, но и фамилию его автора, страницу, на которой помещен комментарий, и другие подробности.
После создания первоначального варианта документа, над которым работает коллектив авторов, начинается его "отладка". Правку документа - внесение исправлений - может делать как один автор, так и группа авторов. Одна из "великих" возможностей Word, обеспечивающая совместную работу над документами, состоит в том, что каждый из создателей документа может править его (документ) как угодно, не испортив при этом исходный текст. Правки накапливаются в коллекции Revisions. Затем, работая в специальном режиме, каждую правку - объект класса Revision можно принять или отвергнуть.
Конечно, также как и тексты, сноски, комментарии и исправления создаются, как правило, вручную. Тем не менее, часто приходится программно работать с этими объектами. Вот, например, типично возникающие задачи: собрать все комментарии данного автора, принять или отвергнуть все исправления, сделанные автором N.
Я приведу сейчас пример, где в документ будут добавлены комментарии и сноски:
Public Sub WorkWithComments()
'работа с комментариями, сносками
'Открываем документ DocTwo
Dim MyPath As String
Dim myRange As Range
Dim Fnote As Footnote, Enote As Endnote
MyPath = Documents("DocOne").Path 'DocOne должен быть открыт
Documents.Open MyPath & "\DocTwo.doc"
Documents("DocTwo").Activate
With ActiveDocument
Set myRange = .Sections(2).Range.Paragraphs(2).Range
.Comments.Add myRange, "Программный проект этого документа" _
& vbCrLf & " содержит примеры главы 1"
.Comments(1).Author = "Владимир Биллиг"
'Показ комментария
ActiveWindow.View.SplitSpecial = wdPaneComments
.Comments. ShowBy = "Владимир Биллиг"
'Передвигается объект Range и устанавливаются сноски:
'подстраничная и конечная
myRange.Move Unit:=wdParagraph, Count:=1
.Footnotes.Add Range:=myRange, _
Text:="документ DocTwo используется для экспериментов."
myRange.Move Unit:=wdParagraph, Count:=1
'нумерация конечных сносок с начала страницы
'.Endnotes.NumberingRule = wdRestartPage
.Endnotes.Add Range:=myRange, _
Text:="документ DocThree используется для экспериментов."
'Печать сносок
For Each Fnote In .Footnotes
Debug.Print Fnote.Range
Next Fnote
For Each Enote In .Endnotes
Debug.Print Enote.Range
Next Enote
End With
End Sub
Листинг 1.38.
Вначале в документ добавляется комментарий, для чего используется метод Add, имеющий в данной ситуации два параметра. Первый - Range - задает точку (область) вставки, второй - текст комментария. При выполнении метода область, заданная объектом Range, подсвечивается и помечается специальной меткой комментария. Свойство Author позволяет указать автора сделанного комментария. Комментарий можно непосредственно просмотреть в тексте документа, установив курсор на метке комментария. Метод ShowBy выводит в окно специального вида все комментарии, выполненные автором, имя которого указано как аргумент метода.
Далее добавляются в документ сноски. И здесь объект Range указывает точку вставки сносок. Заметьте: в этом фрагменте при работе с объектом Range используется метод Move. Его первый параметр - Unit - задает единицы текста, в которых ведется счет при перемещении объекта Range; второй - задает количество единиц и направление перемещения: положительные значения задают перемещение вперед по тексту, отрицательные - назад. Сами сноски вставляются аналогично комментариям методом Add. Подстраничные сноски нумеруются арабскими цифрами, концевые - римскими. Параметр Reference метода Add позволяет задать для нумерации сносок специальные символы.
Наш следующий пример связан с введением исправлений. Я уже говорил, что исправления, как и любая работа с текстом, делаются обычно вручную, а программно они лишь обрабатываются. Но в моем примере я демонстрирую программное введение исправлений, более того, эти исправления выполняются от имени двух различных авторов. Исправления, сделанные одним из них, будут приняты, второго отвергнуты. Заметьте: для программной вставки исправлений метод Add неприменим. Исправления делаются точно так же, как и обычные изменения исходного документа, но выполняются они в специальном режиме правки. Чтобы его включить, требуется задать свойству Track Revisions значение True. Но давайте обратимся к примеру:
Public Sub WorkWithRevisions()
'работа с исправлениями
'Открываем документ DocTwo
Dim MyPath As String
Dim MyRange As Range
Dim Revis As Revision
'DocOne должен быть открыт
MyPath = Documents("DocOne").Path
Documents.Open MyPath & "\DocTwo.doc"
Documents("DocTwo").Activate
With ActiveDocument
'Работа с исправлениями
. ShowRevisions = True
'Удаляем все имеющиеся исправления
. Revisions.RejectAll
'Добавляем новый абзац
.Paragraphs.Add
.Paragraphs.Last.Range.Text = "В книгах для программистов" _
& " тексты программ играют важную роль"
.Paragraphs.Last.Range.Select
'Вводим исправления в последний абзац (автор Fooler)
.TrackRevisions = True
Application.UserName = "Fooler"
Selection.Range.Text = "В книгах для программистов" _
& " тексты программ не играют особой роли," _
& " а лишь усложняют понимание"
'Добавляем новый абзац
.TrackRevisions = False
.Paragraphs.Add
.Paragraphs.Last.Range.Text = "В книгах для программистов" _
& " тексты программ играют важную роль."
.Paragraphs.Last.Range.Select
'Вводим исправления в последний абзац (автор Thinker)
.Track Revisions = True
Application.UserName = "Thinker"
Selection.Range.Text = "В книгах для программистов" _
& " тексты программ весьма полезны," _
& " если только это хорошие программы."
For Each Revis In . Revisions
Debug.Print Revis.Author, Revis.Date, Revis.Range.Text
If Revis.Author = "Fooler" Then
Revis.Reject
ElseIf Revis.Author = "Thinker" Then
Revis.Accept
End If
Next Revis
Debug.Print . Revisions.Count
.TrackRevisions = False
. ShowRevisions = False
Application.UserName = "Vladimir Billig"
End With
End Sub
Листинг 1.39.
Обратите внимание, что в отличие от объектов Comment свойство Author объектов Revision может быть использовано только для чтения, поэтому мне пришлось менять фамилию автора правок более хитрым способом. Конечно, выполнять эту программу рекомендуется в пошаговом режиме, чтобы можно было проследить за процесссом появления правок в документе, а затем их принятия в одном случае и отказа от них во втором.
Вот так выглядит окно комментариев и фрагмент нашего тестового документа, в ходе выполнения этой программы в тот момент, когда правки были сделаны обоими авторами:
Рис. 1.12. Документ, содержащий комментарии, сноски и правки
Гипертекстовый документ. Закладки и гиперссылки:
Гипертекстом называется текст, содержащий ссылки. Конечно, настоящим гипертекстовым документом может быть только документ в электронной форме, поскольку в этом случае становится возможным непосредственный переход по ссылкам. Гипертекстовый документ перестает быть линейным текстом, читаемым от начала до конца. Порядок его чтения (работы с документом) определяется в момент просмотра документа и зависит от выбора гиперссылок лицом, работающим с документом. Когда я говорю о гипертекстовом документе, то применительно к документам Office 2000 это не отражает в полной мере истинную картину. В Office 2000 следует говорить о системе гипертекстовых документов, связанных ссылками. Действительно ссылки в документе Word могут задавать переход к:
элементу того же документа, заданного с помощью закладки или стиля Heading,
элементу другого документа Word,
элементу, расположенному на Web-странице,
именованному элементу рабочей книги Excel,
именованному слайду Power Point,
другому документу, файлу или Web-странице.
Гиперссылки и закладки это два основных инструмента, используемых при создании гипертекстовых документов. Закладка создается в точке, в которую производится переход, точке назначения, а гиперссылка - в точке, из которой производится переход, точке отправки.
Гипертекстовые документы являются одним из широко используемых типов документов. Так строятся сегодня практически все справочные системы. Пожалуй, для всех учебных пособий, особенно в электронной форме, гипертекстовая форма представления материала является наиболее подходящей, поскольку позволяет самому ученику определять оптимальный путь изучения учебного материала. Замечу, что, несмотря на всю полезность гиперссылок, злоупотреблять этим средством не стоит. Изобилие ссылок также плохо, как и линейный текст.
Давайте рассмотрим теперь пример, в котором два документа Word и один документ Excel будут связаны между собой взаимными гиперссылками. Я покажу, как программно устанавливаются закладки и гиперссылки. В документе Excel роль закладок играют имена элементов. Вот процедура, устанавливающая закладки:
Public Sub CreateBookmarks()
'Создание закладок
'Создадим закладку в документе DocOne и свяжем ее с рисунком
Dim MyPath As String
Documents("DocOne").Activate
With ActiveDocument
MyPath = .Path
.InlineShapes(1).Select 'рисунок мышки
.Bookmarks.Add "PictureOfMouse", Selection.Range
End With
'Создадим закладку в документе ExampleOfTable и свяжем ее с таблицей
Documents.Open (MyPath & "\ExampleOfTable")
Documents("ExampleOfTable").Activate
With ActiveDocument
.Tables(1).Select 'таблица Менделеева
.Bookmarks.Add "ТаблицаМенделеева", Selection.Range
End With
End Sub
Листинг 1.40.
Здесь, в двух разных документах создаются две закладки. Одна из них связывается с рисунком, вторая с таблицей. Конечно, чаще всего, закладки связываются с текстом, обычно с началом параграфа или определением понятия.
Рассмотрим теперь создание гиперссылок, некоторые из них будут ссылаться на только что созданные закладки:
Public Sub CreateHyperLinks()
'Создание гиперссылок
'Создадим три гиперссылки в документе DocOne
Dim MyPath As String
Documents("DocOne").Activate
With ActiveDocument
MyPath = .Path
'гиперссылка- элемент группового объекта TableOfFigures
'свяжем ее с закладкой этого же документа
.shapes(2).GroupItems(6).TextFrame.TextRange.Select
.Hyperlinks.Add Anchor:=Selection.Range, _
Address:="", SubAddress:="PictureOfMouse"
'гиперссылка- элемент группового объекта TableOfFigures
'свяжем ее с закладкой другого документа
.shapes(2).GroupItems(7).TextFrame.TextRange.Select
.Hyperlinks.Add Anchor:=Selection.Range, _
Address:=MyPath & "\ExampleOfTable.doc", _
SubAddress:="ТаблицаМенделеева", _
ScreenTip:="Переход к документу," _
& " содержащему таблицу Менделеева"
'гиперссылка- объект TableOfFigures
'свяжем ее с URL
.shapes(1).Select
.Hyperlinks.Add Anchor:=Selection.Range, _
Address:="http://www.microsoft.ru/offext/", _
SubAddress:="", _
ScreenTip:="Переход к Web- странице" _
& " программы Office Extensions"
End With
'Создадим гиперсссылку в документе ExampleOfTable
'и свяжем ее с закладкой документа DocOne
Documents("ExampleOfTable").Activate
With ActiveDocument
'Установка гиперссылки возврата в документ Word DocOne
.Tables(1).Select 'таблица Менделеева
Selection.MoveDown
Selection.Expand
.Hyperlinks.Add Anchor:=Selection.Range, _
Address:=MyPath & "\DocOne.doc", _
SubAddress:="PictureOfMouse", _
ScreenTip:="Возврат к документу DocOne"
'Установка гиперссылки перехода
'к именованному элементу документа Excel - BookOne
Selection.MoveDown
Selection.Expand
.Hyperlinks.Add Anchor:=Selection.Range, _
Address:=MyPath & "\BookOne.xls", _
SubAddress:="ТаблицаПродаж", _
ScreenTip:="Переход к элементу" _
& " c именем ТаблицаПродаж документа Excel - BookOne"
End With
End Sub
Листинг 1.41.
Обратите внимание, метод Add при создании гиперссылок имеет больше параметров, чем при создании закладок. При создании закладки достаточно указать имя закладки и область, с ней связанную. При создании гиперссылки указывается область, связанная с гиперссылкой, точка отправления и точка назначения, заданная двумя параметрами. Параметр Address указывает имя документа, к которому будет осуществлен переход, а параметр SubAddress указывает имя элемента внутри этого документа. Если поле адреса не указано, то переход осуществляется внутри того же документа, где находится точка отправления. Может быть опущено поле, задающее имя элемента, тогда осуществляется переход к самому документу. Одно из этих полей должно быть задано. Именем элемента в документах Word чаще всего выступает имя закладки. В документах Excel, как уже говорилось, в качестве имени элемента может выступать имя, содержащееся в коллекции имен Names данного документа, как показано в нашем примере. Заметьте, для двух первых точек отправления, определяющих положение гиперссылки, я выбрал текст, расположенный достаточно глубоко внутри группового объекта TableOfFigures. Сделано это специально, чтобы продемонстрировать некоторые особенности коллекции Hyperlinks документов Word. Взгляните, как выглядит эта часть документа, после установления гиперссылок:
Рис. 1.13. Документ с закладками
Конечно, чаще всего, установление закладок и гиперссылок делается вручную, а вот обработку уже созданных коллекций этих объектов приходится иногда выполнять программно. В следующем примерах я продемонстрирую работу по просмотру и удалению закладок и гиперссылок в процессе диалога с пользователем. Начнем с удаления закладок активного документа. Вот код процедуры, решающей эту задачу:
Public Sub RemoveBookmarks()
' Удаляет по запросу закладки активного документа
Dim MyBM As Bookmark
Dim Answer As String
With ActiveDocument
For Each MyBM In .Bookmarks
Answer = InputBox(Prompt:="Удалить закладку? " & vbCrLf _
& "Имя закладки - " & MyBM.Name, _
Title:="Удаление закладок", Default:="Да")
If Answer = "Да" Then MyBM.Delete
Next MyBM
End With
End Sub
Листинг 1.42.
Все делается достаточно естественно, свойство Name позволяет в диалоге указать имя закладки, а метод Delete удалить ее, если получено согласие пользователя. Аналогичная процедура просмотра и удаления гиперссылок выглядит немного сложнее:
Public Sub RemoveHyperlinks()
' Удаляет по запросу гиперссылки активного документа
Dim MyHL As Hyperlink
Dim Answer As String,NameHL As String
With ActiveDocument
Debug.Print .Hyperlinks.Count
For Each MyHL In .Hyperlinks
'определение объекта, с которым связана гиперссылка
If MyHL.Type = msoHyperlinkRange Then
NameHL = MyHL.Range.Text
Else:NameHL = MyHL.Shape.Name
End If
Answer = InputBox(Prompt:="Удалить гиперссылку? " & vbCrLf _
& "связана с объектом - " &NameHL & vbCrLf _
& "Имя целевого документа - " & MyHL.Address & vbCrLf _
& "Имя целевого элемента - " & MyHL.SubAddress, _
Title:="Удаление гиперссылок", Default:="Да")
If Answer = "Да" Then MyHL.Delete
Next MyHL
End With
End Sub
Листинг 1.43.
Как видите, для определения имени объекта, связанного с гиперссылкой, приходится вначале определить тип объекта, в зависимости от которого выбирается способ доступа к имени объекта. Однако главное, на что следует обратить внимание, это то, что коллекция Hyperlinks документа не содержит всех установленных в документе гиперссылок. В частности, в ней не содержатся те гиперссылки, которые я связал с текстами, находящимися внутри группового объекта TableOfFigures и показанными на предыдущем рисунке. Добраться программно до этих гиперссылок конечно можно, но через коллекцию Hyperlinks соответствующего объекта Range.
В следующем примере показано применение метода Follow, позволяющего осуществить переход по ссылке. Процедура позволяет перейти по ссылке в результате диалога с пользователем. Вот ее текст:
Public Sub FollowHyperlinks()
' Переход по запросу, следуя гиперссылке активного документа
Dim MyHL As Hyperlink
Dim Answer As String,NameHL As String
With ActiveDocument
For Each MyHL In .Hyperlinks
'определение объекта, с которым связана гиперссылка
If MyHL.Type = msoHyperlinkRange Then
NameHL = MyHL.Range.Text
Else:NameHL = MyHL.Shape.Name
End If
Answer = InputBox(Prompt:="Перейти, следуя гиперссылке? " ;amp; vbCrLf _
& "связана с объектом - " &NameHL & vbCrLf _
& "Имя целевого документа - " & MyHL.Address & vbCrLf _
& "Имя целевого элемента - " & MyHL.SubAddress, _
Title:="Переход по гиперссылке", Default:="Да")
If Answer = "Да" Then
MyHL.Follow
MsgBox ("Продолжаем работать!")
Exit For
End If
Next MyHL
End With
End Sub
Листинг 1.44.
Заметьте, переход по ссылке не означает прерывания работы программы. Открыв соответствующий документ и перейдя к указанному элементу внутри этого документа, процедура продолжит свое выполнение до завершения своей работы.
До сих пор в этом параграфе я много говорил о гипертекстовых документах и переходах по гиперссылке. Но хочу напомнить, что переходы внутри документа могут быть организованы и другими средствами. В частности в этой лекции я рассматривал работу с объектом Browser, который позволяет организовать переходы от элемента к элементу уже рассмотренных нами коллекций таблицам, комментариям, страницам, разделам, элементам других коллекций, среди которых есть, конечно же, и закладки. Говоря о роли закладок, следует отметить, что они используются не только для организации гипертекстовых документов. Их роль велика и в документах типа справочников, где приходится осуществлять частый поиск внутри документа. Закладки позволяют создать систему индексов, ускоряя поиск. Всем хорошо знаком этот прием при работе с обычным словарем, когда делаются закладки на каждой из букв алфавита. Я не буду приводить сейчас примера реализации этой идеи.
Работа с полями документа:
Поля (коллекция Fields) используются в документах Word достаточно широко и играют множество самых разных ролей. Поэтому стоит поговорить о них подробнее. В зависимости от назначения поля оно относится к одной из 9 возможных категорий. Нет смысла и возможности заниматься полным анализом этих категорий, но некоторые основные роли, которые играют поля, я перечислю:
Поля могут хранить в документе некоторую обновляемую информацию. Такими полями являются поля Date и Time из категории Date and Time, хранящие текущую дату и текущее время. В полях Author, FileName, KeyWords, NumPages и других полях из категории Document Information содержится подробная информация о документе авторе документа, файле, в котором он находится, числе занимаемых страниц. В полях UserName и UserAddress из категории User Information хранится информация о фамилии и адресе владельца компьютера, используемая по умолчанию для создания поля Author документа, для создания обратного адреса по умолчанию при работе с конвертами. Эта роль полей достаточно понятна. Ясно, как программно и вручную можно работать с такими полями. Заметим, что значения некоторых из упомянутых полей меняются автоматически, например, время, дата, число страниц документа (число страниц документа - 57, число символов - 149609, дата - 01.07.2006, время - 9:27).
Я только что вставил в текст, указанный в скобках, ряд полей и получил текущую информацию. За время печати этого замечания значения некоторых из этих полей число символов документа и время изменились.
Поля играют важную роль при создании документов с помощью слияния. Зачастую возникает необходимость создания группы однотипных документов, отличающихся лишь небольшими деталями. Типичным примером такого рода является группа рассылаемых писем, отличающихся адресом, названием компании и другими деталями. В этом случае создается главный документ, содержащий шаблон письма с полями и документ, хранящий источник данных. Поля главного документа задают переменную часть письма адрес, название компании и другие данные. Значения этих данных хранятся в источнике данных, который может быть таблицей Word, Access или Excel. Слияние главного документа с одной записью источника данных (строкой таблицы) приводит к появлению нового документа (письма). В слиянии могут участвовать как все записи источника данных, так и только часть из них, выбранная по запросу. В результате возникает необходимая совокупность писем. Имена полей в этом случае могут быть произвольными и представляют имена столбцов в таблице, задающей источник данных. Помимо этих полей в главном документе могут встречаться и поля Word из тех 9 категорий, о которых я уже говорил. В ситуациях слияния часто используются такие поля как Ask и Fill-In, позволяя в момент слияния запросить и добавить в документ индивидуальную информацию, не хранящуюся в записях источника данных. Поле Fill-In позволяет запросить данные при создании документа и сделать полученный ответ значением этого поля. Поле Ask работает аналогичным образом, за тем исключением, что ответ не становится значением поля, а связывается с некоторой создаваемой в этот момент закладкой. Затем эту закладку можно использовать в документе различными способами, принятыми для закладок. На деталях работы вручную останавливаться не буду, но пример такого главного документа приведу:
Рис. 1.14. Главный документ с полями разных типов
Заметьте, поля в главном документе подсвечены и среди них есть как поля, заданные источником данных, так и общие для Word поля, Ask, Fill-In, Formula, Date. Вот как выглядит источник данных, с которым я проводил эксперименты:
Рис. 1.15. Источник данных для слияния документов
Чтобы картина была полной, взгляните на один из документов, полученных в результате слияния главного документа и источника данных:
Рис. 1.16. Документ, полученный слиянием главного документа и источника данных
Еще одним видом документа Word, использующим поля, является электронная форма. Необходимость в таких документах возникает, например, при пересылке анкет по электронной почте. Получатель формы заполняет поля анкеты, которые могут быть элементами управления: обычными текстовыми окнами ввода, флажками или выпадающими списками. Как поля они имеют имена: FormTextBox, FormCheckBox, FormDropDown.
Говоря о полях, нельзя не упомянуть о таком важном и интересном поле, как поле Formula, позволяющее организовывать вычисления в документах Word. Эти вычисления, чаще всего, организуются в ячейках таблиц Word, но могут быть вставлены и в произвольное место документа. Пример такого поля приведен в главном документе. Строятся формулы аналогично формулам Excel и могут работать как с закладками, используемыми в качестве имен переменных, так и с ячейками таблиц Word. При вычислениях можно использовать некоторые из встроенных функций, допустимых в Excel. Тема вычислений в документах Word , его таблицах заслуживает, конечно, более подробного освещения, чем эти несколько строчек. Но обо всем сказать, нам не дано.
Есть и другие случаи применения полей в документах Word. Например, поля автоматически создаются при создании таблиц ссылок, о которых я рассказывал ранее.
Я постарался кратко описать достаточно сложную тему работы с полями в документах Word. Конечно, обычно работа с полями выполняется вручную. Вряд ли, например, есть смысл создавать документ слияния программным путем. Поэтому, говоря о программировании работы с полями, я ограничусь достаточно простыми примерами. В основе программной работы с полями лежит работа с коллекцией полей Fields(Field). В следующем примере анализируются (печатаются) основные свойства объекта Field:
Public Sub FieldsAnalise()
'Анализирует характеристики полей активного документа
Dim MyField As Field
With ActiveDocument
Debug.Print "Число полей - ", .Fields.Count
For Each MyField In .Fields
With MyField
Debug.Print "Код поля - ", .Code, _
"Вид поля - ", .Kind, _
"Тип поля - ", .Type, _
"Результат поля - ", .Result
End With
Next MyField
End With
End Sub
Листинг 1.45.
Приведем результаты отладочной печати для главного документа, имеющего обширную коллекцию полей разного типа:
Число полей - 12
Код поля - DATE \* MERGEFORMAT Вид поля - 2 Тип поля - 31 Результат поля - 13.12.99
Код поля - MERGEFIELD Country Вид поля - 2 Тип поля - 59 Результат поля - "Country"
Код поля - MERGEFIELD City Вид поля - 2 Тип поля - 59 Результат поля - "City"
Код поля - MERGEFIELD Address Вид поля - 2 Тип поля - 59 Результат поля - "Address"
Код поля - MERGEFIELD FirstName Вид поля - 2 Тип поля - 59 Результат поля - "FirstName"
Код поля - MERGEFIELD LastName Вид поля - 2 Тип поля - 59 Результат поля - "LastName"
Код поля - MERGEFIELD HomePhone Вид поля - 2 Тип поля - 59 Результат поля - "HomePhone"
Код поля - MERGEFIELD FirstName Вид поля - 2 Тип поля - 59 Результат поля - "FirstName"
Код поля - FILLIN "Введите имя (имена) близких!" \d "Близкие тебе люди" Вид поля - 2 Тип поля - 39 Результат поля - Близкие тебе люди
Код поля - ASK Vremya "Сколько лет не виделись?" \d "1" Вид поля - 2 Тип поля - 38 Результат поля - 2
Код поля - Ref Vremya Вид поля - 2 Тип поля - 3 Результат поля - 2
Код поля - = Vremya *365*24*60 Вид поля - 2 Тип поля - 34 Результат поля - 1051200
Листинг 1.46.
Большинство полей в этом документе это поля слияния (MergeField), значения которых берутся из источника данных в момент слияния главного документа с записями источника данных. Обратите внимание, ситуация с полями напоминает ситуацию с ячейками Excel, с одной стороны в ячейке содержится формула (в поле код поля), с другой стороны вычисленное по формуле значение (результат поля). По желанию всегда можно включить просмотр в полях либо кода поля, либо результата. По умолчанию, также как и для ячеек Excel, показывается результат поля. Свойство Kind, которое для всех полей нашего документа имеет одинаковое значение, указывает на то, как происходит автоматическое обновление значения поля, имеет ли поле результат.
Для программного создания полей используется, как обычно, метод Add коллекции Fields. Он имеет следующий синтаксис:
Add(Range, Type, Text, PreserveFormatting)
Листинг 1.47.
Параметр Range определяет область документа, в которую вставляется поле, Type тип поля, из-за разнообразия типов этот параметр имеет около сотни различных значений. Параметр Text чаще всего используется в тех случаях, когда нужно задать переключатели, определяющие специфику работы поля. Последний параметр определяет, будет ли сохраняться форматирование при обновлении значения поля.
Рассмотрим теперь пример программного добавления полей в документ. Я ограничусь достаточно простой ситуацией. Добавим в начало документа три поля, задающие автора документа, дату и время:
Public Sub CreateFields()
'Работа с полями
With ActiveDocument
'Добавление полей разного типа в начало документа
Dim myRange As Range
'Установить автора документа
' .Name = "Vladimir Billig"
Set myRange = .Range(Start:=0, End:=0)
.Paragraphs.Add myRange
.Paragraphs.Add myRange
.Paragraphs.Add myRange
myRange.Move Unit:=wdParagraph, Count:=-3
.Fields.Add Range:=myRange, Type:=wdFieldAuthor
myRange.Move Unit:=wdParagraph, Count:=1
.Fields.Add Range:=myRange, Type:=wdFieldDate
myRange.Move Unit:=wdParagraph, Count:=1
.Fields.Add Range:=myRange, Type:=wdFieldTime
'Еще один способ добавления полей на примере
'добавления поля автора с одновременным изменением автора документа
myRange.Move Unit:=wdParagraph, Count:=1
myRange.Select
.Fields.Add Range:=myRange, Type:=wdFieldEmpty, _
PreserveFormatting:=False
Selection.TypeText Text:="Author ""Fooler"""
'Печать полей
FieldsAnalyse
'Обновление полей
.Fields.Update
FieldsAnalyse
End With
End Sub
Листинг 1.48.
В этой процедуре добавляются три пустых абзаца в начало документа, а затем добавляются три поля. Затем демонстрируется еще один способ работы с полями, когда вначале задается пустое поле, а потом в нем печатается текст, определяющий это поле. Конечно, это должен быть разумный текст, определяющий тип поля и его характеристики. Так, зачастую, работают с полями вручную. Заметьте, необходимо обновить это поле, чтобы поле отражало значение результата. Печать результатов до обновления и после обновления позволяет проследить за изменениями значений полей. Приведем результаты отладки:
Число полей - 4
Код поля - Author \* MERGEFORMAT Вид поля - 2 Тип поля - 17 Результат поля - Vladimir Billig
Код поля - DATE \* MERGEFORMAT Вид поля - 2 Тип поля - 31 Результат поля 14.12.99
Код поля - TIME \* MERGEFORMAT Вид поля - 2 Тип поля - 32 Результат поля - 12:28
Код поля - Author "Fooler" Вид поля - 0 Тип поля --1 Результат поля -
Число полей - 4
Код поля - Author \* MERGEFORMAT Вид поля - 2 Тип поля - 17 Результат поля - Vladimir Billig
Код поля - DATE \* MERGEFORMAT Вид поля - 2 Тип поля - 31 Результат поля - 14.12.99
Код поля - TIME \* MERGEFORMAT Вид поля - 2 Тип поля - 32 Результат поля - 12:28
Код поля - Author "Fooler" Вид поля - 2 Тип поля - 17 Результат поля - Fooler
Листинг 1.49.
Работа с фрагментами:
StoryRanges(Range) - эта коллекция представляет совокупность частей документа, называемых фрагментами (Story). Количество различных фрагментов документа фиксировано. Нельзя добавлять элементы в эту коллекцию обычным способом, используя метод Add. Фрагменты появляются в коллекции, когда создается соответствующая часть документа. В этот момент определяется и тип фрагмента. Фрагменты имеют тип, задаваемый константами из перечисления wdStoryType. Главный фрагмент, конечно, - текст документа, тип которого задается константой wdMainTextStory. Фрагментами других типов являются комментарии, ссылки, колонтитулы. Заметьте: сам фрагмент является объектом Range. Так что благодаря фрагментам можно, например, работать с коллекцией комментариев, как с единой областью.
Приведем пример, где анализируются типы фрагментов активного документа:
Public Sub WorkWithStory()
'Работа с фрагментами (story)
'Анализ возможных типов фрагментов акивного докуммента
Dim curstory As Range
With ActiveDocument
For Each curstory In .StoryRanges
Select Case curstory.StoryType
Case wdMainTextStory
Debug.Print "Начало текста:", curstory.Paragraphs(1).Range.Text
Case wdCommentsStory
Debug.Print "Комментарии:", curstory.Text
Case wdEndnotesStory
Debug.Print "Концевые ссылки:", curstory.Text
Case wdFootnotesStory
Debug.Print "Подстраничные ссылки:", curstory.Text
Case Else
Debug.Print "Фрагмент другого типа:", curstory.Text
End Select
Next curstory
End With
End Sub
Листинг 1.50.
Тестовый документ состоит из фрагментов четырех типов, так как он, кроме текста, содержит комментарии и два типа ссылок. Вот результаты отладочной печати:
Начало текста: Vladimir Billig
Подстраничные ссылки: документ DocTwo используется для экспериментов.
Концевые ссылки: документ DocThree используется для экспериментов.
Комментарии: Page: 2 Программный проект этого документа содержит примеры главы 1
Листинг 1.51.
Переменные, которые живут долго:
У переменных век не долог. Локальные переменные, как правило, заканчивают существование в момент окончания работы процедуры, в которой они описаны. Глобальные переменные живут дольше, некоторые из них могут быть доступными в разных программных проектах системы документов в процессе работы с этими документами.
Однако и локальные и глобальные переменные "умирают" по окончании сеанса работы. Для того чтобы сохранять данные между сеансами работы, необходимо использовать файлы, базы данных и другие внешние источники данных. Замечу, что, конечно, есть еще возможность хранить данные, как часть самого документа, например, в таблицах Word или Excel. Но сейчас мы поговорим еще об одной интересной возможности, предоставляемой при работе с офисными документами. С каждым из документов можно связать коллекцию переменных типа Variant коллекцию Variables(Variable). Эта коллекция для программистов важна - ведь время жизни входящих в нее переменных совпадает со временем жизни документа. По существу речь идет о некотором специальном файле переменных, жестко связанном с самим документом и хранящимся вместе с ним. Тем самым появляется возможность сохранять информацию о работе документе между сеансами. Применения этого полезного средства могут быть самыми разными. Можно сохранять предпочтения, сделанные пользователем при первом сеансе работы с документом, различные настройки документа и так далее. Другое применение связано с предоставлением документа во временное владение с ограничением числа возможных запусков. Например, можно иметь счетчики, подсчитывающие количество вызовов некоторой процедуры и в зависимости от значения счетчика по-разному определять дальнейшую работу с данным документом. Переменные, входящие в коллекцию Variables, создаются не так, как обычные переменные, нет необходимости в их описании в разделе объявлений какого либо модуля. Они имеют фиксированный тип Variant и создаются методом Add, типичным для создания элементов коллекций. Этот метод имеет достаточно прозрачный синтаксис:
Function Add(Name As String, [Value]) As Variable
Листинг 1.52.
В момент создания задается имя переменной и, возможно, инициализирующее ее значение. Тип, как я уже говорил, установлен по умолчанию.
Вот несколько процедур, демонстрирующих работу с такими переменными. В частности, я показываю возможность отключения демо версии системы после завершения разрешенного числа запусков. Начнем с процесса создания переменной, являющейся счетчиком, следящим за числом запусков. Взгляните на соответствующую процедуру и вызываемую в ней функцию:
Public Sub CreateVar()
'Создание переменных - хранителей информации
With ActiveDocument.Variables
If Not ExistVar("Counter") Then
'Добавляем переменную
.AddName:="Counter", Value:=0
End If
End With
End Sub
Public Function ExistVar(Name As String) As Boolean
'Определяет наличие переменнойName в коллекции Variables
Dim MyVar As Variable
ExistVar = False
For Each MyVar In ActiveDocument.Variables
If MyVar.Name =Name Then
ExistVar = True: Exit For
End If
Next MyVar
End Function
Листинг 1.53.
При попытке повторного добавления одной и той же переменной в коллекцию Variables возникает ошибка. В тоже время процедура, в которой происходит добавление, может выполняться многократно. Поэтому я предусматриваю соответствующую проверку на существование переменной с заданным именем в коллекции Variables. Может возникнуть вопрос, куда поместить процедуру CreateVar, создающую переменную. Я поместил ее в процедуру, обрабатывающую событие "Open" документа, но можно упрятать этот вызов и более глубоко. Рассмотрим теперь, как происходит работа с переменной Counter:
Public Sub CheckCounter()
Const Limit = 10
'Счетчик Counter может быть использован в любой процедуре,
'позволяя следить за числом ее выполнения
With ActiveDocument
If .Variables("Counter") > Limit Then
'Исчерпан лимит нормальной работы демо-версии
Call MsgBox("Исчерпан лимит работы демо-версии", _
vbCritical, "Конец работы!")
Else ' продолжаем нормальную работу
Dim myLocal As Integer
'Локальные переменные могут работать с глобальным счетчиком
myLocal = .Variables("Counter")
Debug.Print "Счетчик = "; myLocal
'В конце работы увеличиваем значение счетчика
myLocal = myLocal + 1
.Variables("Counter") = myLocal
End If
End With
End Sub
Листинг 1.54.
Экспериментируя с тестовым документом, я открывал и закрывал его многократно, время от времени, вызывая процедуру CheckVar. После 11 ее запусков нормальное выполнение было прервано и выдалось предупреждающее сообщение:
Рис. 1.17. Документ в момент выдачи предупреждающего сообщения.
Текст и объекты Range и Selection::
Основной частью документа Word является, конечно, текст этого документа. Уже было сказано, как структурирован текст документа, рассмотрены коллекции - Characters, Words, Sentences, Paragraphs, которые позволяют работать с символами, словами, предложениями и абзацами текста. Говорил я также и о том, что только этими коллекциями не обойтись, и необходим общий класс объектов, позволяющий задать произвольную область текста. Таковыми являются два важных класса Range и Selection. Объекты этих классов широко используются при работе с текстом. Документы, поддокументы, разделы, все вышеупомянутые коллекции от Characters до Words имеют метод или свойство Range, возвращающие в качестве результата объект Range. Каждый объект Range задает область определения некоторого объекта, включая текст и все объекты, связанные с этим текстом - комментарии, ссылки и прочее. Так, если некоторый объект вызвал метод Range, то возвращаемый объект Range будет содержать область определения объекта, вызвавшего метод.
Объект Selection представляет выделенную область. Поскольку в каждом окне может быть только одна выделенная область, то одновременно может существовать лишь несколько объектов Selection по одному на каждое существующее окно или подокно. Заметим также, что, объект Selection всегда существует в окне, даже если и не сделано явного выделения некоторой области, в последнем случае объект Selection задает точку вставки, определенную позицией курсора.
Объект Document имеет метод Range, возвращающий объект Range, и метод Select, создающий объект Selection. Метод Range - это функция, возвращающая в качестве результата объект Range; метод Select - это процедура без параметров, которая создает объект Selection в качестве побочного эффекта. Заметьте существенную разницу между методами Range и Selection. В первом случае возвращается сам объект и поэтому можно запомнить возвращаемый объект и в программе одновременно работать с несколькими такими объектами. В случае вызова метода Selection объект не возвращается, следовательно, запомнить его нельзя, что и гарантирует уникальность объекта Selection.
Объект Range имеет метод Select, выделяющий область объекта Range, и определяющий, тем самым, новый объект Selection. Симметрично, объект Selection имеет свойство Range, возвращающее объект Range, соответствующий выделенной области.
Объекты Range и Selection столь же многообразны по своей структуре, как и объект Document. И даже большинство свойств у этих трех объектов одни и те же. Эти три объекта являются схожими. Это понятно, так как большинство частей документа: предложения, абзацы, разделы, таблицы, рисунки, комментарии, ссылки и многое другое - может составлять любую подобласть документа, в том числе и выделенную подобласть. Значит, большинство ранее описанных частей документа являются и частями (свойствами) объектов Range и Selection. Это приятно - ведь с большинством свойств этих объектов мы уже знакомы!
Объект Range напоминает матрешку: в каждую область вложена область поменьше. Вот пример корректного (хоть и не самого эффективного) задания объекта Range:
ActiveDocument.Range.Sections(1).Range.Paragraphs(1).Range.Sentences(1).Words(1).Characters(1)
Листинг 1.55.
Сколько объектов Range упоминается в этом предложении? Правильный ответ: 6, но не 3. Напомним: на нижнем уровне отсутствуют классы, определяющие символ, слово или предложение - все эти объекты принадлежат классу Range.
Для объекта Selection нельзя построить такую хитрую цепочку вложений - ведь метод Select не возвращает объект Selection. Но можно построить такую же "хитрую" последовательность операторов, которая задает сужающуюся область выделения:
Selection.Sections(1).Range.Select
Selection.Paragraphs(1).Range.Select
Selection.Sentences(1).Select
Selection.Words(1).Select
Листинг 1.56.
Работа с текстом:
Объекты Range и Selection позволяют выполнять основные операции над текстом (и не только над текстом): "выделить", "добавить", "заменить", "удалить". У наших объектов большой набор методов, позволяющих реализовать эти операции. Все рассматриваемые здесь методы принадлежат обоим объектам, если не сделана специальная оговорка.
Выделение:
Говоря в этом параграфе о выделении, я имею в виду не применение метода Select, а выделение в более широком смысле, умение задать некоторую подобласть данной области. Выделить некоторую часть текста означает, по существу, определение объекта Range или Selection. Оба объекта задают непрерывную область, а их свойства Start и End позволяют задать начало и конец области. Меняя эти свойства, можно задать нужную область выделения. Этот основной способ выделения мы не раз демонстрировали в наших примерах:
Dim myRange As Range, myRange1 As Range
With ActiveDocument
Set myRange = .Range(Start:=.Sections(2).Range.Paragraphs(3).Range.Start, _
End:=.Sections(3).Range.Paragraphs(5).Range.End)
Set myRange1 = .Sections(3).Range.Paragraphs(1).Range
End With
Листинг 1.57.
В первом случае при задании области используются параметры Start и End, во втором - задается вся область данного объекта. Область может изменяться автоматически при добавлении или удалении из нее части текста. Изменить область можно и путем ее перемещения. Этим занимается специальная группа методов перемещения Move. Прежде чем говорить о перемещении, рассмотрим сжатие области. Нередко нужна пустая область - точка вставки, параметры Start и End которой совпадают. Поэтому простейший способ сжатия - задать совпадающие значения этих параметров:
myRange.Start = myRange.End
Листинг 1.58.
Для сжатия области можно применять специальный метод сжатия - Collapse(Direction). Область стягивается в начальную или конечную позицию. Направление сжатия задает параметр Direction, принимающий значения wdCollapseStart или wdCollapseEnd. По умолчанию область стягивается в начальную точку (значение параметра: wdCollapseStart). Если сжимается абзац, и он стягивается в конечную точку, точка вставки переносится за метку конца абзаца и устанавливается в начало следующего абзаца. Если такой эффект нежелателен, после сжатия применяется метод перемещения MoveEnd, передвигающий точку вставки назад на один символ:
Set myRange = ActiveDocument.Paragraphs(1).Range
myRange.Collapse Direction:=wdCollapseEnd
myRange.MoveEnd Unit:=wdCharacter, Count:=-1
Листинг 1.59.
Если есть методы сжатия области, то должны быть и методы расширения области. Основным из них является метод Expand(Unit). В зависимости от значения параметра Unit область можно расширить на слово, предложение, абзац, раздел, на строку или столбец таблицы, или на всю таблицу. Для объекта Selection область можно расширить на всю строку. Для расширения области на весь фрагмент можно использовать метод WholeStory, что впрочем эквивалентно вызову метода Expand(Unit := wdStory)
Метод Move является основным методом перемещения. Остальные методы - в той или иной степени его модификации. Метод Move(Unit, Count) сжимает область в точку, стягивая ее в начало или конец, и затем перемещает точку вставки. Параметр Unit определяет единицы перемещения, а Count - количество этих единиц и направление стягивания и перемещения (по умолчанию 1). Положительные значения этого параметра задают стягивание к концу и перемещение вперед, отрицательные - стягивание в начало и перемещение назад. Само стягивание означает перемещение на одну единицу. Метод возвращает количество единиц, на которое фактически произошло перемещение, или 0, если оно не осуществлено. Параметр Unit принимает значения wdCharacter (по умолчанию), wdWord, wdSentence, wdParagraph, wdSection, wdStory, wdCell, wdColumn, wdRow и wdTable.
Методы перемещения на сам текст не влияют - лишь изменяют область, заданную объектами Range и Selection. Поэтому эти методы применимы только к переменным типа Range, но не к фиксированным областям. Например, запись:
ActiveDocument.Paragraphs(1).Range.Move
Листинг 1.60.
не имеет эффекта, поскольку область первого абзаца - вещь неизменяемая.
Метод Move стягивает область в точку, которая и перемещается, поэтому после его выполнения область исчезает, и остается только точка вставки. Методы MoveStart и MoveEnd перемещают начальную или конечную точку области, обычно расширяя тем самым область.
Конечно, для перемещения по тексту документу есть много различных возможностей, кроме группы методов Move. Стоит упомянуть группу методов Next, основным из которых является метод Next(Unit,Count). Основное отличие от метода Move с теми же параметрами состоит в том, что метод Next возвращает сам объект Range, в отличие от метода Move, возвращающего число символов, на которое произошло перемещение. Напомним также о тех возможностях перемещения, которыми обладают объекты Browser и Hyperlink, напомним о закладках, специально предназначенных для перехода к ним.
Удаление текста:
Метод Delete позволяет удалить текст. Вызванный без параметров, он удаляет вызывающий его объект Range или Selection. Если он применен в форме Delete(Unit,Count), удаляется часть текста в указанной области. Параметр Unit задает единицы, но при удалении возможны только два значения: wdWord и wdCharacter. Параметр Count задает количество удаляемых единиц. Если область стянута в точку, удаляются символы перед точкой вставки или после нее в зависимости от знака параметра Count. Вот несколько примеров:
'Удаляется текст в области объекта myRange
myRange.Delete
' Удаляются первые три слова из области myRange1
myRange1.Delete Unit:= wdWord, Count :=3
'Область стягивается в точку
myRange1.Collapse Direction := wdCollapseStart
'Удаляются три первых слова из области, предшествующей myRange1
myRange1.Delete Unit:= wdWord, Count :=-3
Листинг 1.61.
Вставка текста:
Группа методов Insert объектов Range и Selection позволяет осуществлять вставки в документ. Для вставки текста используются методы InsertBefore(Text) и InsertAfter(Text). Параметр Text типа String задает текст, вставляемый до или после области, заданной объектами Range или Selection. После вставки текста область автоматически расширяется, включая в себя добавляемый текст. Вот пример вставки нового абзаца в начало документа:
Dim myRange As Range
Set myRange = ActiveDocument.Range(Start:=0, End:=0)
myRange.Text = "дорогой "
myRange.InsertBefore "Мой "
myRange.InsertAfter "друг! "
myRange.InsertParagraphAfter
Листинг 1.62.
Свойство Text позволяет заменять текст в выделенной области, поэтому нет нужды вызывать метод Insert(Text), - лучше использовать свойство. Методы InsertBefore и InsertAfter безопасны, так как текст добавляется, не изменяя содержимого области. При вставке внутрь области, например, при использовании метода InsertSymbol или InsertParagraph, заменяется содержимое области. Эта вроде бы безобидная программка сотрет все содержимое документа, заменив его пустым абзацем:
Set myRange = ActiveDocument.Range
myRange.InsertParagraph
Листинг 1.63.
Говоря о методах вставки текста, нельзя не упомянуть о таком мощном методе, как InsertFile - он позволяет вставлять не только текст или таблицу Excel, но и целый документ, хранящийся в файле. Вот простой пример вызова метода, при котором в начало активного документа вставляется существующий документ:
Public Sub test1()
Dim myRange As Range
Dim myPath As String
With ActiveDocument
myPath = .Path
Set myRange = .Range(Start:=0, End:=0)
myRange.InsertFile myPath & "\DocTwo.doc"
End With
End Sub
Листинг 1.64.
Работа с буфером:
Известно, как полезен буфер при работе с одним и, особенно, с несколькими документами. Объекты Range и Selection в полной мере позволяют задействовать все возможности буфера. Метод Copy, не имеющий параметров, копирует объект (содержимое области) в буфер. Метод Cut, действуя аналогично, копирует объект в буфер, заодно удаляя его. Заметьте, что в отличие от предыдущей версии, теперь метод Cut работает, как ему положено, не только копируя объект, но и удаляя его, ранее удаления не происходило, вместо этого объект, вызывавший метод стягивался в точку. Метод Paste позволяет приклеить объект, помещенный в буфер. Рассмотрим пример:
.Public Sub test1()
Dim myRange As Range
Set myRange = ActiveDocument.Paragraphs(5).Range
myRange.Select
'Добавляем новый абзац
myRange.InsertAfter "New Text"
myRange.InsertParagraphAfter
'Выделяем и затем вырезаем добавленный абзац и помещаем его в буфер
myRange.MoveStart Unit:=wdParagraph
myRange.Select
Selection.Cut
'Добавляем новый абзац
Selection.InsertAfter "Новый текст!"
Selection.InsertParagraphAfter
'Добавляем абзац из буфера
Selection.Collapse Direction:=wdCollapseEnd
Selection.Paste
End Sub
Листинг 1.65.
Метод Paste позволяет "вклеить" содержимое буфера в область, заданную объектами Range и Selection. Эта операция опасна, так как происходит замена, а не добавление текста. Поэтому обычно метод Paste применяется к объектам Range и Selection, стянутым в точку вставки. В выполнении этого метода есть нюансы. У объекта Range содержимое буфера включается в его область, Объект Selection остается точкой вставки, расположенной после текста, добавленного из буфера.
Заметьте, в буфер можно копировать не только текст. В нашем следующем примере в буфер копируется рисунок, являющийся элементом коллекции TableOfFiguress. Напомню, что элементы этой коллекции вставляются аналогично символам текста и являются частью абзаца. В тестовом документе DocOne, с которым я работаю, в начало текста вставлен рисунок мышки. Я буду работать с первым абзацем этого текста, содержащим этот рисунок, как с обычным текстом, выделю рисунок, помещу его в буфер, а затем приклею в другом месте текста. Вот код соответствующей процедуры:
Public Sub CopyImage()
'Копирование рисунка через буфер
Dim MyRange As Range
Set MyRange = Documents("DocOne").Paragraphs(1).Range
With MyRange
'Первый абзац этого документа содержит рисунок
' - элемент коллекции TableOfFiguress.Выделяем рисунок
.MoveEnd Unit:=wdCharacter, Count:=-7
.Select
.Copy
End With
Set MyRange = ActiveDocument.Paragraphs(6).Range
MyRange.Select
Selection.Collapse Direction:=wdCollapseEnd
Selection.Paste
End Sub
Листинг 1.66.
Иногда в буфер копируют формат текста. Этим занимается метод CopyFormat, копирующий формат по первому символу объекта Selection. Если этот символ - метка абзаца, копируется формат абзаца. Методом CopyFormat обладает только объект Selection.
Метод PasteFormat применяет форматирование, хранящееся в буфере к объекту Selection.
Метод PasteSpecial позволяет явно управлять форматированием в момент вставки объекта из буфера.
