Документи Office 2000 і їхні проекти. Об’єктна модель і програмна робота з програмними проектами, Basic, Програмування, статті

Володимир Білліг, VBstep.ru

Усюди, де тільки можна, я не втомлювався повторювати, як заклинання, що для офісного програміста метою роботи є створення документа, частиною якого є програмний проект, нерозривно пов’язаний з документом. Більш того, в серйозних розробках мова завжди йде про створення системи документів, а, отже, і про систему програмних проектів, пов’язаних з цими документами, так і за допомогою посилань безпосередньо один з одним. Програмні проекти в такій системі можуть мати загальний пул пам’яті, загальні процедури, що викликаються з будь-якого проекту. Про проектування такої системи документів, про зв’язуванні програмних проектів посиланнями я детально розповідав в своїй книзі [2. В.А. Білліг “VBA в Office 2000. Офісне програмування”, гл.2] Тепер же я хочу розглянути відносини між документами і проектами з об’єктної точки зору, розглянути основний об’єкт VBProject, що задає проект, його компоненти, його зв’язок з об’єктом Document і колекцією VBProjects, хочу розглянути питання програмної роботи з цими об’єктами. Усюди в цьому тексті я буду говорити про документи Word і їх програмних проектах. Але потрібно розуміти, що об’єктна модель проектів практично одна й та ж для всіх документів Office 2000. Зауважу також, що багато що з того що я буду розповідати відноситься і до попередньої версії Office 97.

На відміну від інших документів Office документи Word володіють тією особливістю, що з кожним з них пов’язані два проекти. Один – Normal – стандартний проект, спільний для всіх документів, а другий – відображає специфіку документа. Цей другий проект зазвичай і називають проектом даного документа і саме про нього піде основний наша розмова. Але перш кілька слів про проект Normal. Всі документи Word відкриваються за замовчуванням на основі шаблону Normal, частиною цього шаблону і є, пов’язаний з шаблоном проект Normal. Всі макроси, що зберігаються в проекті Normal є доступними для будь-якого з документів Word. Саме сюди поміщає програміст всі макроси, стандартні модулі або модулі класів, які на його думку будуть використовуватися всіма документами Word на даному комп’ютері. Потрібно однак розуміти, що при розміщенні макросу в проекті Normal, мова повинна йти дійсно тільки про загальнозначущих програмних інструментах. В інших випадках все програмні компоненти повинні бути пов’язані з проектом конкретного документа. Зауважу до речі, що іноді приміщення програмних компонент, найчастіше макросів, в проект Normal відбувається через непорозуміння, оскільки за замовчуванням місцем розміщення створюваних MacroRecorder макросів є саме проект Normal, а не проект даного документа. Так що будьте уважні в таких ситуаціях. Але давайте перейдемо до розгляду об’єктної моделі проекту документа Word.

Об’єкт Document має властивість VBProject, що повертає об’єкт, що задає програмний проект даного документа. Перш, ніж поговорити про цей об’єкт, давайте підійметься на кілька поверхів вище. Я нагадаю, що кореневим об’єктом, що містить всі об’єкти VBA, є об’єкт VBE (Visual Basic Environment). У цього об’єкта є тільки властивості, що дозволяють отримати доступ до об’єктів VBA, що лежать на нижніх рівнях ієрархії. Зокрема, такі властивості об’єкта VBE, як ActiveWindow, ActiveVBProject, SelectedVBComponent дозволяють отримати доступ до активного вікна, активному програмного проекту або вибраної компоненті проекту. Властивості AddIns, CommandBars, Windows дозволяють отримати доступ до колекцій елементів AddIn, панелей, і вікон. Є, звичайно, в об’єкта VBE і властивість VBProjects, що повертає колекцію всіх відкритих програмних проектів. Зауважте, якщо одночасно відкрито N документів Word, то колекція VBProjects буде містити не менш ніж N +1 елемент, ѕ по одному проекту на документ плюс загальний для всіх проект Normal. Бачите, я говорю обережно ѕ “не менш”. У середовищі Office 97 їх було б рівно N +1, в Office 2000 їх може бути більше.

У Office 2000 можливості роботи з програмними проектами документів істотно розширилися. В об’єкта VBProject та колекції VBProjects в Office 2000 з’явилася велика кількість нових властивостей і методів. Зауважу, що певну складність у вивчення цього матеріалу вносить та обставина, що в довідковій системі довідки по багатьом об’єктам, пов’язаним з проектами, відсутні або, що ще гірше, містять неповні відомості, не відображають дійсної картини. Так було і в Office 97, така ж ситуація має місце і в Office 2000.

Скажімо кілька слів про колекцію VBProjects. У Office 97 в цієї колекції були три простих властивості ѕ Count, Parent, VBE і єдиний метод – Item. У неї не було методів Add і Delete, так як проекти були міцно пов’язані зі своїми документами і віддалялися і з’являлися одночасно зі своїми документами. У Office 2000 у цієї коллецию з’явилися три методи для додавання проектів в колекцію ѕ Add, AddFromFile, AddFromTemplate, відповідно з’явився і метод Remove для видалення елементів колекції. Крім цього, колекція має методи SaveAs і FileName для збереження проектів, з’явилися також і нові властивості, зокрема властивість StartProject.

З цими новими властивостями і методами ще належить розібратися. В “звичайної” Premium версії Office 2000 вони не работатют. Мабуть, їх можна використовувати у версії Developer. Я поки не провів необхідних досліджень і тому не готовий дати точну відповідь навіть на таке просте питання, що значить додати проект в колекцію VBProjects, чи означає це, що він буде пов’язаний з усіма відкритими документами?

Маючи колекцію VBProjects, дістатися до окремого проекту неважко, робиться це звичайним для колекцій способом, Можна, звичайно, використовувати метод Item, але, простіше за все, вказати індекс елемента в колекції, при цьому роль індексу може грати і ім’я проекту. Так що спуститися по ієрархії зверху вниз неважко: VBE.VBProjects (1) дає посилання на перший проект колекції. Також просто і від окремого проекту перейти вгору по ієрархії. Об’єкт VBProject має властивість Collection, що дозволяє отримати колекцію VBProjects, а його властивість VBE дозволяє піднятися ще вище в ієрархії і дістатися до кореневого об’єкта VBE; від кореня можна вже спуститися в будь-яку задану точку. Отже, розібравши, як пов’язаний об’єкт VBProject з об’єктами, вищестоящими в ієрархії, давайте більш докладно зупинимося на його властивості та методи.

У Office 97 об’єкт класу VBProject мав тільки властивості і не мав методів. Про дві властивості цього об’єкта ѕ Collection і VBE я вже згадав, вони дозволяють піднятися по ієрархії. Властивості Description і Protection дозволяють отримати опис проекту і визначити, чи захищений він. Властивості HelpFile і HelpContextId дозволяють вказати довідкову систему по проекту, якщо така існує. Найбільш важливими, мабуть, властивостями є властивості VBComponents і References. Властивість VBComponents повертає колекцію компонент проекту: модулі, класи і форми, що входять в проект. Маючи цю колекцію, можна перейти до відповідної компоненті і програмно працювати з нею. Властивість References дозволяє отримати доступ до колекції посилань на елементи, доступні з даного проекту. Елементи цієї колекції відповідають посиланнях, наведених у пункті References меню Tools в середовищі редактора VB.

У Office 2000 об’єкт VBProject отримав кілька нових властивостей, наприклад, властивості: CompatibleOleServer, IsDirty, Type. Перше з них повертає або встановлює рядок, що містить сумісний сервер Автоматизації для проекту. Булево властивість IsDirty, як зазвичай для властивості з таким ім’ям вказує на внесення змін в проект з моменту останнього його збереження. Властивість Type задає тип проекту, що вказує на те, що проект може ставитися, наприклад, до ActiveXDLL або ActiveXControl. Об’єкт VBProject тепер має і методи. Метод MakeCompiledFile записує проект у вигляді DLL, ім’я якої задається новим властивістю BuildFileName. Метод SaveAs дозволяє зберегти проект в зазначеному місці.

Зміни в складі властивостей і методів об’єктів VBProject і VBProjects симптоматичні. Вони відображають тенденцію до возрастенію потреб офісних програмістів при роботі з програмними проектами документів. На мою відчуттю, Office все ширше застосовується для створення досить серйозних програм, що включають систему документів і пов’язану з нею систему програмних проектів

Перейдемо тепер до розгляду прикладів роботи з об’єктом VBProject. Почнемо з аналізу термінальних властивостей проектів:

Public Sub WorkWithProjects() 'Аналіз термінальних властивостей проектів колекції VBProjects
'Dim MyProject As VBProject
 Dim MyProject As Object Debug.Print "Число проектів в колекції -", VBE.VBProjects.Count
    For Each MyProject In VBE.VBProjects
	With MyProject Debug.Print "Ім'я файлу -",. FileName Debug.Print "Ім'я проекту",. Name Debug.Print "Тип проекту",. Type Debug.Print "Опис проекту",. Description Debug.Print "Статус захисту",. Protection Debug.Print "Статус проекту",. Mode 'Debug.Print "Зміни Вносилися?",. IsDirty Debug.Print "Ім'я Dll",. BuildFileName 'Debug.Print "Початковий статус",. StartMode
	End With
    Next MyProject
End Sub

Сама програма досить зрозуміла. Я просто в налагоджувальному режимі друкую число відкритих проектів і в циклі по колекції VBProjects для кожного з проектів друкую значення його основних термінальних властивостей. Зверніть увагу на дві обставини. У перших двох рядках програми двічі описана змінна MyProject ѕодін раз як VBProject, інший – як Object. Це не випадково. Коли я пишу текст програми, я користуюся першим з цих оголошень, задаючи раннє зв’язування. Тоді я отримую потрібні мені підказки про властивості та методи цього об’єкта і вкладених в нього об’єктів. Без цих підказок життя програміста стає нестерпним. Коли ж я переходжу на виконання програми, то доводиться користуватися другим варіантом оголошення, оскільки через недоробки в Office 2000 нормальна робота з цим об’єктом в документах Word можлива тільки при такому вигляді оголошення, хоча в Excel працюють і явні оголошення типу VBProject. Зауважте також, що звернення до деяких властивостях закоментовані. Це пов’язано з тим, що не всі заявлені нові властивості і методи реально працюють в Premium версії Office 2000. Слід відзначити ще один невеликий, але прикрий “жучок”, яка у цьому місці. При появі підказки ряд існуючих властивостей і методів не показується, хоча вони фактично працюють, наприклад, не показується властивість Protection і Mode.

Списки властивостей і методів об’єкта VBProject, але й не тільки цього об’єкту, переглядаються в трьох різних місцях ѕ в довідковій системі, у вікні перегляду (браузері об’єктів) та при появі підказки в момент програмування, на жаль, можуть не збігатися.

Є сенс навести результати тестової друку при роботі цієї процедури. Я виконував її в той момент, коли були відкриті два документи Word, один з них текст цієї глави, другий ѕ тестовий документ, що містить тексти виконуваних процедур. Ось результати друку:

Число проектів в колекції - 3  Ім'я файлу - E: \ O2000 \ Book2 \ CD \ Ch1 \ DocOne.doc Ім'я проекту DocOneProject Тип проекту 100  Опис проекту Цей проект містить приклади глави 2 Статус захисту 0  Статус проекту 0  Ім'я Dll E: \ O2000 \ Book2 \ CD \ Ch1 \ DocOne.DLL Ім'я файлу - E: \ o2000 \ vBA2000 \ Normal Ім'я проекту Normal Тип проекту 100  Опис проекту Статус захисту 0  Статус проекту 0  Ім'я Dll E: \ o2000 \ vBA2000 \ Normal.DLL Ім'я файлу - E: \ O2000 \ Book2 \ Ch1 \ Ch1.DOC Ім'я проекту Project Тип проекту 100  Опис проекту Статус захисту 0  Статус проекту 0  Ім'я Dll E: \ O2000 \ Book2 \ Ch1 \ Ch1.DLL

Як і належить, при двох відкритих документах колекція VBProjects містить три проекти. Проекту мого тестового документа я дав ім’я (DocOneProject) і вказав для нього опис.

У наступному прикладі займемося аналізом компонент і посилань проекту. При цьому я не обмежуся печаткою властивостей компонент і посилань, але додам в проект нову компоненту, створивши програмно нову форму:

Public Sub WorkWithVBProject() 'Компоненти та посилання проекту
'Dim MyProject As VBProject
Dim MyProject As Object
'Dim MyComp As VBComponent
Dim MyComp As Object
'Dim MyRef As Reference
Dim MyRef As Object
Set MyProject = ActiveDocument.VBProject
  With MyProject 'Друк імені і типу для кожної існуючої компоненти проекту Debug.Print "Число компонент проекту -",. VBComponents.Count
    For Each MyComp In .VBComponents Debug.Print "Ім'я компоненти -", _ MyComp.Name, "Тип -", MyComp.Type
      If MyComp.Name = "NewMacros" Then MyComp.Name = "DocOneMacros"
    Next MyComp 'Додавання форми - нової компоненти проекту
    .VBComponents.Add vbext_ct_MSForm 'Друк імені і типу для кожної існуючої компоненти проекту Debug.Print "Число компонент проекту -",. VBComponents.Count
    For Each MyComp In .VBComponents Debug.Print "Ім'я компоненти -", _ MyComp.Name, "Тип -", MyComp.Type
      If MyComp.Name = "NewMacros" Then MyComp.Name = "DocOneMacros"
    Next MyComp 'Друк імені і типу для кожної існуючої посилання проекту
    For Each MyRef In .References Debug.Print "Ім'я посилання -", _ MyRef.Name, "Тип -", MyRef.Type
    Next MyRef
  End With
End Sub

Наведу результати отладочной друку по завершенні роботи цієї процедури:

Число компонент проекту - 4 Ім'я компоненти - ThisDocument Тип - 100 Ім'я компоненти - Examples Тип - 1 Ім'я компоненти - DocOneMacros Тип - 1 Ім'я компоненти - Examples1 Тип - 1 Число компонент проекту - 5 Ім'я компоненти - ThisDocument Тип - 100 Ім'я компоненти - Examples Тип - 1 Ім'я компоненти - DocOneMacros Тип - 1 Ім'я компоненти - Examples1 Тип - 1 Ім'я компоненти - UserForm1 Тип - 3 Ім'я посилання - VBA Тип - 0 Ім'я посилання - Word Тип - 0 Ім'я посилання - stdole Тип - 0  Ім'я посилання - Normal Тип - 1  Ім'я посилання - Office Тип - 0  Ім'я посилання - MSForms Тип - 0  Ім'я посилання - VBIDE Тип - 0

Звичайно, головним підсумком роботи процедури є не стільки отладочная друк, скільки поява нової форми, яку можна заповнювати програмно або уручну.

Наведені приклади програмної робота з проектом досить прості. Два складніших прикладу на цю тему можна знайти в моїй вже згадуваній книжці [2. стр. 442 – стр. 446], де я відповідав на два питання одного з читачів:

При відповіді на перше запитання я, по-перше, показав, як програмно додаються в створену форму елементи управління, по-друге, як використовувати властивість Designer для додання формі, з якою працює програміст, статусу режиму проектування.

Зараз я хочу розглянути одну важливу тему при роботі з програмними проектами. Іноді робота програміста з проектом полягає в тому, що він програмно повинен змінити сам текст проекту, додаючи нові модулі, нові процедури і обробники подій, коректуючи текст модуля і його окремих процедур, створюючи або змінюючи свій проект, як кажуть, “на льоту”. Я вже говорив про те, що, завдяки колекції VBComponents, можна дістатися до кожного модуля проекту, а завдяки властивості CodeModule, отримати код модуля. Властивістю CodeModule володіє об’єкт VBComponents (“NameofModule”), що задає модуль проекту з ім’ям NameOfModule. При виклику цієї властивості повертається об’єкт CodeModule, що визначає код модуля. У цього об’єкта багато важливих і корисних властивостей і методів, необхідних при програмної роботи з кодом проекту. Завдяки таким властивостям як CountOfLines, CountOfDeclarationsLines, ProcCountLines, можна дізнатися число рядків у модулі, число рядків у розділі оголошень, число рядків у процедурі модуля із заданим при виклику ім’ям. Працюючи з властивістю Members, можна отримати повну інформацію про всі елементи модуля. Властивості ProcBodyLine і ProcStartLine повертають номер рядка, з якої починається процедура або попередньої їй рядки. Властивість Lines повертає задане число рядків процедури, властивість ProcOfLine повертає ім’я процедури, яка містить задану при виклику рядок. Якщо властивості об’єкта CodeModule дозволяють проаналізувати склад модуля і дістатися до кожної з його процедур, то методи об’єкта дозволяють вставляти, заміняти і видаляти рядки коду, так що можна “на льоту” провести корекцію процедури, видалити або додати нову процедуру та / або оголошення змінної.

Методи AddFromFile і AddFromString дозволяють додати в модуль текст, збережений в або у файлі, або безпосередньо в рядку. Перший метод, як правило, використовується для введення великих змін в модулі, другий при невеликих коректувань. Зауважимо, що якщо потрібно повністю додати новий модуль, то зручніше користуватися методом AddFile або AddFromTemplate колекції VBComponents. Методи InsertLines, DeleteLines і ReplaceLines дозволяють вставити, видалити або замінити рядки програмного тексту у зазначеній точці. Функція CreateEventProc дозволяє створювати процедури зазначених подій. Функція Find дозволяє здійснювати повномасштабний пошук в модулі.

У яких ситуаціях виникає необхідність в програмному створення або коригування коду проекту? На перший погляд тексти програм завжди можна створювати “вручну”. Однак так здається тільки “на певий погляд”. На практиці така потреба виникає досить часто. Зараз я розгляну одну з таких ситуацій, в якій має сенс програмне створення коду проекту. У цьому прикладі я використовую більшість з вищезгаданих властивостей і методів об’єкта CodeModule. Змістовно, завдання, яке вирішується у прикладі, полягає в наступному:

Необхідно створити шаблон документа, такий, щоб всі документи, що відкриваються на основі шаблону, містили обробник події Open, однаково реагуючи на кожне відкриття документа. Зауважу, що хоча сам шаблон може містити обробник цієї події, але документи, що відкриваються на його основі, володіти цим обробником не будуть. Однак додати цей обробник в документ можна програмним шляхом, в той момент, коли створюється документ на основі шаблону. Зрозуміло, що для цього необхідно задати відповідний код в обробнику події New нашого шаблону. Щоб трохи ускладнити завдання і зробити її більш конкретної, будемо також вважати, що документи, створювані на основі шаблону повинні мати змінну – лічильник, що стежить за числом відкриття документа. Такі лічильники корисні, коли користувачеві надається демо-версія, розрахована на фіксоване число відкриттів документа. Таким чином, шаблон повинен гарантувати також поява змінної, назвемо її Counter, в колекції Variables кожного нового документа, створюваного на основі шаблону.

Отже наша мета полягає в тому, щоб створити шаблон документа. В обробнику події New для цього шаблону, що викликається в той момент, коли на основі шаблону створюється новий документ, передбачити вирішення наступних завдань:

Зрозуміло, що, крім усього іншого, цей приклад демонструє програмне створення коду проекту документа. Для вирішення цього завдання нам доведеться широко використовувати властивості і методи об’єкта CodeModule, так що настала його черга. Ось код обробника події New, вбудований в шаблон документа з ім’ям DocWithCounter:

Private Sub Document_New()
Const MyPath = "e:\O2000\Book2\Cd\Ch1\"
Const Lin1 = "OpenDoc"
Dim Beg As Long 'Створення змінної - зберігача інформації в новому документі
With ActiveDocument.Variables
  If Not ExistVar("CounterDoc") Then 'Додаємо змінну
    .Add Name:="CounterDoc", Value:=0
  End If
End With
With ActiveDocument.VBProject.VBComponents("ThisDocument").CodeModule 'Створення процедури - події в новому документі
  Call .CreateEventProc("Open", "Document") 'Визначення точки вставки в процедуру
  'Beg = .ProcStartLine("Document_Open", 0)
  Beg = .ProcBodyLine("Document_Open", 0) 'Вставка тексту в процедуру
  Call .InsertLines(Beg + 1, Lin1)
End With 'Додавання модуля в проект нового документа
ActiveDocument.VBProject.VBComponents.Add (vbext_ct_StdModule) 'Перейменування модуля
ActiveDocument.VBProject.VBComponents("Module1").Name = "AddedModule"
With ActiveDocument.VBProject.VBComponents("AddedModule").CodeModule 'Вставка тексту процедури з файлу
  .AddFromFile (MyPath & "AddingModule.bas")
End With
End Sub	

Процедура досить добре прокоментована, тим не менше я дозволю звернути Вашу увагу на наступні моменти:

  1. Спочатку в колекцію Variables нового документа вставляється мінлива Counter. Нагадаю, що змінні цієї колекції є частиною документа, зберігаються разом з ним і тому час їх життя збігається з часом життя документа. Вони можуть виступати в ролі зберігачів інформації між сеансами роботи.
  2. Інтерес представляє рядок:
    With ActiveDocument.VBProject.VBComponents _
    	("ThisDocument").CodeModule

    Розглянемо детальніше ланцюжок викликів, що породжуються цим рядком. Виклик ActiveDocument повертає новий документ, що створюється на основі шаблону. Виклик VBProject повертає проект цього документа, очевидно, що змістовного коду в цьому проекті поки що немає. Тим не менш, у цьому проекті є модуль зі стандартним ім’ям ThisDocument, так що виклик VBComponents (“ThisDocument”) поверне цей модуль. Виклик CodeModule поверне об’єкт CodeModule, що містить поки що порожній код модуля, з яким я і починаю працювати.

  3. Виклик CreateEventProc (“Open”, “Document”) програмно створить в цьому модулі обробник події Open для об’єкта Document. Але поки це буде тільки заготовка обробника з порожнім кодом.
  4. У цю заготовку я додаю свій код. І як завжди, я строю дуже простий обробник події, що складається з одного рядка (“OpenDoc”) – виклику відповідної процедури стандартного модуля. Зауважте, я використовую виклик методу InsertLines, щоб вставити заготовлену у вигляді константи цей рядок в тіло обробника події.
  5. На наступному кроці я додаю в проект документа новий модуль, даю йому ім’я “AddedModule” і з раніше заготовленого файлу заповнюю текст цього модуля. Але хочу звернути Вашу увагу, в цей момент не тільки добвятся процедури, що зберігаються в цьому файлі, але й сам модуль отримає ім’я “AddingModule” на ім’я модуля, експортованого раніше в цей файл, так що моя робота по створенню імені “AddedModule” виявилася марною. Тим не менш, додавання тексту модуля з файлу проходить успішно. Наведу текст процедур, що зберігаються в додавати модулі:
    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
    Public Sub OpenDoc() 'Використання лічильника Counter 'Для підрахунку числа відкриттів документа
    Dim myLocal As Integer 'Локальна змінна отримує значення лічильника
    With ActiveDocument
    	If ExistVar("CounterDoc") Then
    		myLocal = .Variables("CounterDoc") MsgBox "Число відкриттів документа" & _
    			.Name & vbCrLf & myLocal, _
    			vbExclamation, _ "Число відкриттів документа!" 'Збільшуємо і зберігаємо лічильник
    		myLocal = myLocal + 1
    		.Variables("CounterDoc") = myLocal
    	Else MsgBox "У документа" &. Name _ & "Немає лічильника числа відкриттів", _
    			vbExclamation, _ "Число відкриттів документа!"
    	End If
    End With
    End Sub

Як бачите, в цьому прикладі документи отримують програмний проект при народженні. В цьому проекті є стандартний модуль, обробник подій і все це створюється “на льоту” програмним шляхом.

Тепер я хочу розглянути ще один приклад, який є відповіддю на наступне питання одного з моїх читачів:

Завдання Сергія Шершньова (так звуть мого читача) полягала в тому, що, працюючи з системою документів, йому в залежності від вибору користувача необхідно було підключати програмні проекти тих чи інших документів. Щоб отримати доступ в головному документі до процедур підключених проектів, йому необхідно було попередньо програмно встановити посилання на спільні проекти. Не буду вдаватися в усі тонкощі його проблем і розгляну лише дві конкретні завдання, ѕ як програмно включити (виключити) посилання на програмний проект і як викликати процедури підключених проектів.

Для відповіді на ці питання розглянемо наступну ситуацію. Нехай у нас є головний документ DocOne і два інших документа DocTwo і DocThree. Всі три документи мають програмними проектами. Відкривши головний документ, ми цікавимося уподобаннями користувача і, в залежності від його вибору підключаємо документ DocTwo або DocThree, викликаючи потім відповідні процедури підключеного документа. По ходу справи необхідно також вміти відключити раніше підключений документ. Розглянемо одне з можливих рішень цієї задачі.

Перш, ніж перейти до безпосереднього вирішення завдання, зроблю одне зауваження. Рішення завдання починається зі з’ясування уподобань користувача. Типове рішення задачі про вибір уподобань користувача полягає в тому, що йому пред’являється форма, що містить список всіх допустимих можливостей. У цьому списку користувач і задає свій множинний вибір. Про роботу зі списками в формах я розповідав досить докладно, наприклад в [1. В.А. Білліг, М.І. Дехтяр “VBA і Office 97. Офісне програмування”, стор 520 – 533], [2. стр. 60 – 65]. Зараз я обмежусь більш простою процедурою, яка враховує мій приватний випадок роботи з документами:

Public Sub ChooseProject(NoF As String, NoP As String) Const Msg = "Введіть ім'я файлу," &  _ "Зберігає документ і його програмний проект!"
	NoF = InputBox(Msg, "Projects", "DocTwo.doc") Const Msg1 = "Введіть ім'я проекту, що зберігається в документі!"
	NoP = InputBox(Msg1, "Projects", "DocTwo")
End Sub

Зауважте, для роботи з посиланнями, – їх програмного підключення і відключення необхідно знати ім’я файлу, в якому зберігається сам підключається документ і ім’я проекту цього документа. У моєму прикладі я проектам у всіх документах давав ім’я, що збігається з ім’ям документа. Це знайшло відображення в установках, що задаються за умовчанням у функції InputBox.

Виклик функції ChooseProject дозволяє встановити переваги користувача. Знаючи їх, можна включити відповідне посилання на документ. Ось як це робиться:

Public Sub CreateRef() 'Створення посилання на проект з ім'ям NoP 'Зберігається у файлі з ім'ям NoF
  Dim MyPath As String
  Dim MyRef As Object
  Dim NameOfProject As String
  Dim NameOfFile As String 'Вибір додається проекту
  Call ChooseProject(NameOfFile, NameOfProject) 'Запам'ятовування глобальних змінних програми
  NoF = NameOfFile
  NoP = NameOfProject
  With ActiveDocument
    MyPath = .Path 'Вставка посилання
    If Not ExistRef(NameOfProject, MyRef) Then
      .VBProject.References.AddFromFile MyPath & _
	"\" & NameOfFile
    End If
    CallProcFromProject Debug.Print "Файл -", NoF, "Ім'я проекту -", NoP
  End With
End Sub

Як бачите, для підключення посилання я використовую метод AddFromFile колекції References. В якості параметра необхідно задати повний шлях до файлу, який містить документ з підключається проектом. Ім’я проекту при цьому вказувати не потрібно, – воно автоматично буде знайдено в документі. Зверніть увагу на використання змінної MyPath, що задає шлях до активного документа. Я припускаю, що активним є мій головний документ. Друге, більш сильне припущення полягає в тому, що всі спільні документи знаходяться в одному каталозі з головним. Це дозволяє безболісно переносити всі документи на інший комп’ютер і розміщувати їх в будь-якому каталозі.

Зауважте також, що перш, ніж додати посилання, я перевіряю можливість існування її в колекції посилань, щоб виключити її повторну запис. Булева процедура ExistRef вирішує цю задачу. Перший параметр NameOfProject є вхідним і задає ім’я проекту, посилання на який ми шукаємо. Другий параметр – MyRef є вихідним і задає об’єкт класу Reference, знайдену посилання в разі успіху пошуку. Ось текст цієї функції:

Public Function ExistRef(Name As String, Refery As Object) As Boolean 'Визначає наявність посилання з 'Іменем Name в колекції References 'Повертає посилання при її виявленні
  Dim MyRef As Object
  Set Refery = Nothing
  ExistRef = False
  For Each MyRef In ActiveDocument.VBProject.References
    If MyRef.Name = Name Then
      Set Refery = MyRef
      ExistRef = True
      Exit For
    End If
  Next MyRef
End Function

Програмно видалити посилання також просто, як і її додати. Ось текст відповідної процедури:

Public Sub RemoveRef() 'Видалення посилання на проект з ім'ям NoP
	Dim MyRef As Object
	Dim NameOfProject As String
	Dim NameOfFile As String 'Вибір видаляється проекту
	 Call ChooseProject(NameOfFile, NameOfProject) 'Видалення посилання
	If ExistRef(NameOfProject, MyRef) Then
		ActiveDocument.VBProject.References.Remove MyRef
	End If
End Sub

Для видалення посилання я використовую метод Remove колекції References. Як параметр цього методу необхідно передати об’єкт, що задає удаляемую посилання. На щастя у нас вже написана функція ExistRef, яка на ім’я видаляється проекту поверне посилання на нього. Тому всі проблеми з видаленням посилання тим самим вирішені.

Тепер, коли ми вміємо включати і вимикати посилання на програмний проект залишилося розглянути, як звертатися до елементів цього проекту, ѕ його загальнодоступним змінним, об’єктів і процедурам. Нагадаю, [2. стр. 66 -75] для звернення до елемента проекту, на який є посилання, необхідно вказати його повне ім’я, що включає ім’я проекту, ім’я модуля і власне ім’я елемента. У моєму прикладі я буду викликати процедуру з одним і тим же власним ім’ям TestPrint, яка є в обох підключаються проектах DocTwo і DocThree. Щоб викликати процедуру того проекту, який підключений в поточний момент, очевидно, необхідно організувати розбір випадків. Це неважко зробити, знаючи ім’я (імена) підключених проектів. Тут є правда деякий підводний камінь. Справа в тому, що у відповідному Case – операторі тільки ім’я одного елемента буде визначено, того елементу, який належить підключеному проекту, імена викликаються елементів з інших, непідключених проектів будуть неопределено, більш того, вважатися неоголошеним, що негайно призведе до звичайної синтаксичну помилку. З цієї ситуації є простий вихід. Але перш, ніж розповісти про нього давайте поглянемо на текст відповідної процедури, що займається розбором випадків:

Public Sub CallProcFromProject() 'Виклик процедури підключається проекту 'Розбір випадків, який проект підключений
	If NoP = "DocTwo" Then
		Proc2
	ElseIf NoP = "DocThree" Then
		Proc3
	End If
End Sub
Public Sub Proc2() 'Виклик процедури проекту 2
	DocTwo.DocTwoModule.TestPrint
End Sub
Public Sub Proc3() 'Виклик процедури проекту 2
	DocThree.DocThreeModule.TestPrint
End Sub

У нашій простій ситуації розбором займається найпростіший оператор IF, який в залежності від імені проекту викликає процедуру TestPrint того чи іншого проекту. Але зауважте, я не став в текст цієї процедури вставляти явний виклик процедури проекту, замінивши його на виклик внутрішньої процедури, яка є обгорткою цього виклику. Таким чином, процедура CallProcFromProject є синтаксично коректною, більш того вона викликає синтаксично коректну процедуру, якщо, звичайно, розбір працює коректно. Синтаксично некоректні процедури не викликаються, їх існування в проекті не заважає його нормальній роботі. Це, звичайно, “виверт”, але дозволяє досягти заданої мети. Хочу звернути увагу на ще один аспект, пов’язаний з роботою цієї процедури. Якщо Ви помітили ця процедура викликається в тілі процедури CreateRef, відразу ж після створення посилання на підключається проект. Давайте еше раз повернемося до методу AddFromFile, що створює посилання в колекції References. Звичайно, головним підсумком роботи цього методу буде не тільки створення посилання, але і явне підключення нового документа з його проектом. Це означає, що, якщо відповідний документ не був відкритий до моменту створення посилання, то він автоматично відкриється. Необхідно мати на увазі ще один наслідок цього процесу, ѕ після завершення процедури, що створює посилання в зв’язку з підключенням нового проекту, відбувається скидання виконання поточного проекту і, зокрема, всі глобальні змінні будуть обнулені, а отже будуть втрачені значення змінних, що зберігають імена підключаються проектів. Саме тому виклик процедури CallProcFromProject робиться в тій же процедурі, в якій новий проект підключається. В іншому випадку необхідно було повторно запитувати у користувача імена підключаються проектів.

На закінчення хочу висловити кілька загальних зауважень. Перш за все, слід зазначити, що програмне підключення і відключення посилань може бути корисним не тільки при роботі з підключаються програмними проектами. Список можливих Com – об’єктів, що підключаються до документа через меню References великий, і часто виникає необхідність в програмному їх підключенні.

Хочу відзначити також, що є два альтернативних варіанти, коли слід створювати систему програмних проектів, пов’язаних посиланнями. Для першого варіанту характерна ситуація, коли з “різних точок загального проекту системи необхідно викликати одні й ті ж процедури “, коли є спільний пул даних, з яким работатют всі проекти, загальні процедури обробки цих даних. У цьому варіанті легко спроектувати структуру системи проектів у вигляді дерева, в корені якого буде проект, який містить загальні дані і процедури і на який будуть посилатися всі інші проекти. У такій ситуації всі посилання відомі на етапі проектування, їх можна встановити вручну і ніяких проблем немає. Саме цей варіант я докладно розглядав в [2. гл.2]. Зараз же розглядався інший варіант, коли “з однієї точки проекту необхідно викликати різні процедури “. В цьому випадку програмне підключення проектів може бути краще.

Слід також сказати, що реальна проблема С. Шершнева виявилася складніше описаної і не вкладається ні в один з двох розглянутих варіантів. У його ситуації не можна було заздалегідь написати процедуру CallProcFromProject, оскільки розбираються в ній випадки з’являлися динамічно. Сподіваюся, зрозуміло, що виходом у такій ситуації є коригування коду цієї процедури програмно, “на льоту” в той момент, коли з’являється новий “випадок”, новий клієнт в системі, що створюється С.Шершневим. Але, як було показано, зробити це можливо і не так вже й складно.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*