Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: BookOne, BookTwo, BookThree, DocOne, DocFour.
Мы уже не раз говорили об идеологии офисного программирования, - целью разработки является создание системы документов, а не создание программного проекта (приложения). Программный проект это часть документа, хранится вместе с документом и не может быть отделен от него. Совокупность программных проектов всех документов, входящих в некоторую систему, может рассматриваться как программный проект этой системы документов. Возможны два важных частных случая:
Основная цель этой лекции рассмотреть, как связаны между собой программные проекты системы документов, какова структура программного проекта одного документа, его основные компоненты.
Но прежде, чем приступить к реализации этой цели, хочется рассмотреть хотя бы обзорно, некоторые, более общие вопросы, связанные с проектами и документами. Прежде всего, заметим, что " документ документу рознь". Одно дело, когда речь идет о настройке стандартного документа, придания ему некоторых дополнительных функций, совсем другое дело - разработка большой и сложной системы, затрагивающей многие службы офиса или предприятия. Считается, что Office 2000 - это масштабируемая среда разработки. Это означает, что она подходит для решения небольших, простых задач, так и для решения сложных задач. К простым задачам относится создание отдельных, возможно и сложных документов, создание сравнительно небольшой системы документов. Сложными задачами могут быть, например:
Есть еще одно измерение в разработке документов Office 2000. Оно связано с разработкой документов специального вида. Сюда относится разработка: шаблонов, Мастеров (Wizards), компонент, расширяющих функциональные возможности других документов. Эти компоненты могут быть двух видов - AddIns и ComAddIns. Дадим краткую характеристику этим видам документов:
AddIns являются компонентами, специфическими для того или иного приложения Office 2000. Один AddIn может использоваться, например, только в документах Word, другой - в документах Excel.Com AddIns - то компоненты, которые могут использоваться в разных приложениях. Достаточно, чтобы эти приложения допускали использование Com - объектов, построенных на основе Com - модели Com AddIns отличается от ActiveX объектов тем, что с программной точки зрения представляет DLL - динамически загружаемую библиотеку, в то время как ActiveX - это исполняемые файлы. Заметим, что ранее эти компоненты не могли быть созданы в офисной среде, необходимо было использовать для их создания Прежде, чем перейти к рассмотрению вопроса о том, что такое проект документа, скажем несколько слов о проектировании документа. Хотя эти термины близки по звучанию, но различны по смыслу. Тема проектирования документа (системы документов), его "жизненного цикла" лежит вне нашего рассмотрения, но хоть несколько слов ей уделить нужно и мы решили, что это можно сделать здесь в разделе о проектах документах.
Прежде всего, под "жизненным циклом" мы понимаем период от замысла программного продукта до его "кончины". Обычно рассматриваются следующие фазы этого процесса:
Проектирование <=> Разработка <=> Отладка <=> Развертывание и Сопровождение <=> Модификация
Все это называется циклом, поскольку после каждой фазы возможен возврат к предыдущим этапам. Вот некоторые штрихи к каждому из этапов:
AddIns, DLL, ActiveX и Com AddIns компоненты. Работая над одним проектом, думайте о будущем, - создавайте компоненты, допускающие их использование в других проектах.Замечание:
Хочу привести одно из свежих впечатлений о процессе отладки. Будучи бета - тестером Office 2000, я поразился, сколь мощной является построенная Microsoft система отладки своих продуктов. Специальный узел в Интернете для тестеров, конференции для них, удобный для тестеров способ публикации своих отчетов о найденных ошибках, практически моментальная реакция разработчиков на каждую из найденных ошибок.
От глобальных вопросов проектирования документов спустимся на землю и рассмотрим один единственный документ Office 2000. Сейчас нас будет интересовать, что собой представляет программный проект одного документа.
Каждый вновь создаваемый документ обладает определенной архитектурой, поскольку строится на основе каркаса документов, заданного совокупностью библиотек Office 2000. Каркас каждого конкретного документа определяется, прежде всего, тем, в каком приложении создается документ, - корневым объектом Application. Частью архитектуры вновь создаваемого документа является и создаваемый по умолчанию программный проект. Здесь и в дальнейшем, если только не будет делаться специальных оговорок, под программным проектом понимается совокупность модулей. Модули, составляющие программный проект, могут быть следующих типов:
Модули первого типа всегда связаны с объектами, реагирующими на события. Их главное назначение состоит в том, что они содержат обработчики таких событий. Объектов, имеющих события, довольно много, но только с некоторыми из них связываются модули. Попробуем с этим разобраться подробнее.
Когда создается документ Word, то появляется один такой объект с именем ThisDocument, задающий сам документ. При создании документа Excel наряду с объектом ThisWorkbook, задающим рабочую книгу, появляется коллекция объектов Sheets, задающих каждую страницу этой книги. Все упомянутые объекты могут реагировать на события, они являются основными объектами, с ними и связываются модули. Но, заметьте, при создании презентации в приложении Power Point объект ThisPresentation, задающий презентацию, и связанный с ним модуль автоматически не появляется. В этот момент должен возникнуть вопрос, а почему? Дело в том, что объект класса Presentation не реагирует на события, следовательно, и модуль не может появиться. Это же касается и объектов класса , - у них тоже нет событий.
Есть и другие объекты, с которыми логично было бы связать модули, но по умолчанию этого не делается. В каждом из приложений, наряду с объектом, задающим сам объект - презентацию, документ, рабочую книгу, присутствует и корневой объект Application, задающий само приложение. Этот объект, как известно, также реагирует на события. Почему же нет модуля, ему соответствующего? Здесь есть одна тонкость. Создаваемые по умолчанию объекты Application не реагируют на события. На события ApplicationWithEvents. Чуть позже мы рассмотрим, как создаются такие объекты и как появляются для них соответствующие модули.
До сих пор мы говорили о модулях, создаваемых по умолчанию для новых документов. Но такие модули могут появляться и в процессе работы над существующим документом. Наиболее известным видом таких модулей являются модули форм (диалоговых окон), составляющие в проекте отдельную папку Forms. Всякий раз, когда Вы добавляете в проект новую форму, появляется модуль, связанный с этой формой. Есть и другие ситуации, при которых появляются новые модули проекта, например, при добавлении нового листа в книгу Excel, появляется и соответствующий ему модуль.
Далеко не со всяким объектом, который может реагировать на события, связывается модуль. Для всех элементов управления ( Controls ), размещаемых в форме или непосредственно в документе Word, листе не имеют событий и потому с ними не связаны модули в момент создания презентации. Сейчас мы опровергнем это утверждение, уточним его. Если на слайде размещаются элементы управления - кнопки списки и прочее, то появляется модуль, связанный с этим слайдом, и обработчики событий, связанных с элементами управления размещаются в этом модуле.
Есть еще одна группа объектов, имеющих обработчиков событий в обязательном порядке, но не имеющая "своих" модулей. Возможно, Вы уже догадались, что речь идет о командах меню, командных кнопках и других элементах управления, размещаемых на панелях - объектах класса CommandBar. Макросы, задающие обработку событий, связанных с этими элементами, помещаются в стандартные модули.
Модули этого типа не создаются по умолчанию. Их создает программист явным образом. Каково их назначение? Можно ли без них обойтись? Сколько таких модулей следует создавать? Вот типичные вопросы, стоящие перед программистом в процессе работы над программным проектом. Постараемся сейчас на них ответить.
Стандартные модули - это основной вид модулей. Большую часть всех процедур и функций, которые пишет программист, следует размещать в стандартных модулях. Разумно иметь не один большой стандартный модуль, а некоторое множество относительно небольших модулей. В один модуль следует помещать набор функций, совместно вызывающих друг друга, связанных общей темой, общей функциональной направленностью. В модулях первого типа, связанных с объектами, следует помещать только те процедуры и функции, которые непосредственно обрабатывают события. Все общие процедуры следует выносить в стандартные модули. Вот три основные причины, по которым следует создавать небольшие стандартные модули:
Замечание:
Опцию "On
Итак, подводя итог, заметим, что разумно иметь набор стандартных модулей, располагая в них все содержательные процедуры обработки данных документа. И, конечно, если таких процедур много, то модули должны быть специализированными, объединяя под одной крышей данные и функционально связанные с ними процедуры и функции. Хотя все модули имеют одинаковый уровень иерархии, реально всегда выделяется головной модуль, в котором следует сосредоточить описание глобальных переменных, используемых всеми модулями документа. Во многих ситуациях стандартный модуль следует оформить как модуль класса.
Специальный тип модуля - модуль класса является той синтаксической формой, которая используется для создания классов, разработанных программистом для решения своей задачи. Учитывая важность темы, мы посвятили созданию классов отдельную лекцию этой книги.
Этот модуль создается автоматически при первом вызове Macrorecoder. В приложении Word все созданные Macrorecoder макросы размещаются в модуле с именем NewMacros. При создании макроса требуется указать, в модуль какого из проектов - Normal.dot или проекта, связанного с документом, должен быть записан макрос. В приложении Excel каждый новый макрос записывается в отдельный модуль. Это не всегда удобно и, зачастую, руками макросы объединяются в одном модуле, особенно, если все они решают некоторую общую задачу. Единственное отличие модулей макросов от стандартных модулей состоит в том, что они создаются системой, а не программистом. Поскольку это не столь существенно, то мы не будем в дальнейшем выделять этот тип модулей, и будем рассматривать их далее как стандартные модули.
Модули VBA имеют очень простую синтаксическую структуру. В отличие от большинства языков программирования здесь нет большого числа разделов, нет разделения на интерфейс и реализацию. Все сделано предельно просто. Каждый модуль вне зависимости от его типа имеет всего два раздела:
Sub ) либо функция ( Function ).Но коль скоро зашла речь о классификации методов, то давайте уточним определения некоторых, часто встречающихся понятий. Начнем с понятия "макрос". Под макросом мы будем понимать любую процедуру без параметров. Отсутствие параметров - это главный отличительный признак макроса. Чаще всего, о макросах идет речь в следующих контекстах:
CommandBar.Элементы командной панели имеют по существу одно событие и один метод, вызываемый в ответ на появление события. Этот метод, обрабатывающий событие, мы называем макросом. Но есть и другие объекты, у которых много событий и соответственно много методов, вызываемых в ответ на возникновение того или иного события. Такие методы мы называем обработчиками событий. Обработчики событий, как правило, макросы, но иногда они могут иметь параметры. Есть еще одно существенное ограничение, накладываемое на правило построения имени обработчика. Имя является двойным и составляется из имени объекта и имени события. Два имени разделяются (объединяются) знаком подчеркивания.
Давайте немного поговорим о том, как выглядит работа с модулями в среде Редактора
(рис 2.1) Окно проекта и окно кодаОкно проекта или, как еще говорят, Проводник Проекта (Project Explorer) отображает структуру проекта в привычном для всех Проводников виде - дерева раскрывающихся папок. В этом окне отображается программные проекты всех открытых документов. Так что, если Вы работаете с семейством документов, то Проводник покажет полностью программный проект всего семейства. Специальная папка References покажет взаимные связи проектов отдельных документов.
Давайте разберемся в структуре проекта, показанного на рисунке 2.1. Этот проект связан с MasterFNew. В данном случае мы имеем дело с единственным документом, хотя в окне показаны два проекта. Дело в том, что наш документ работает с Solver ). В Office 2000 AddIn, ссылку на который надо включить в меню References, что и приводит к появлению соответствующего проекта в Проводнике. Заметьте, проект Solver закрыт паролем от просмотра, что не позволяет просмотреть его структуру и тем более код его модулей. Что же касается проекта нашего документа, то его структура раскрыта полностью. Он содержит:
Form.Main играет роль основного модуля проекта и cодержит описание глобальных переменных всего проекта.Solver.Проект данного документа довольно типичен и содержит все типы модулей за исключением модулей классов.
Проект имеет несколько терминальных свойств, которые можно задать в окне "Project Properties" (Свойства Проекта), оно выводится после щелчка правой кнопкой мыши по названию проекта или одной из его папок в окне проектов и последующего выбора из контекстного меню команды Project Properties.
(рис 2.2) Окно свойств проектаВ окне две вкладки General и Protection. На вкладке General задается имя проекта, его описание, указываются файлы справки и
Имя проекта используется не только для идентификации проекта в окне просмотра. Оно играет важную роль в тех случаях, когда документ является частью общей системы документов или используется, как компонент, вызываемый в других документах ( COM объект). В последнем случае имя проекта идентифицирует компонент в реестре Windows. Во всех случаях проект можно рассматривать как компонент, отвечающий требованиям COM модели, Согласно принятой в Public - элементы. Имя проекта играет важную роль в этом процессе, - оно является именем библиотеки типов TypeLib. Эта библиотека содержит описание объектов и интерфейсов, которые поддерживает компонент. У имени проекта есть еще одна важная роль, оно используется также при построении полного имени открытых компонентов проекта. Всякий раз, когда в проекте A надо сослаться на Public переменную или Public метод проекта B, необходимо использовать полное имя открытого компонента:
имя_проекта.имя_модуля.имя_открытого_компонента
Заметьте, при именовании используется имя проекта, а не имя документа, содержащего проект.
Вкладка Protection позволяет защитить проект от просмотра и редактирования. В большинстве случаев при передаче документа пользователям, проект должен быть защищен как от несанкционированного просмотра его модулей, так и коррекции программного текста. Включив флажок "Lock project for viewing", Вы закрываете проект, его структура будет недоступна для просмотра, если неизвестен пароль. Мы описывали такую ситуацию ранее, когда говорили о проекте Solver, связанном с
Окно проекта согласовано с окном кода. Когда Вы выбираете некоторый модуль двойным щелчком в окне проекта, его код отображается в окне кода. Но окно кода отображает не только код модуля. Важную роль играют два раскрывающихся списка, расположенные в верхней части окна кода. Они играют особую роль для модулей, связанных с объектами, реагирующими на события. Ранее, мы уже говорили, что такие объекты, как главный документ, страницы Excel, слайды презентации, являются основными, в них может быть вложено большое число объектов, также реагирующих на события. На страницах Excel, например, можно размещать командные кнопки, списки и другие элементы управления. Поэтому модуль основного объекта связан фактически с большим числом объектов, реагирующих на события. Следовательно, нужно иметь возможность отображать структуру вложенных объектов.
Левый раскрывающийся список окна кода показывает все объекты, выбранного модуля, позволяя понять, какие объекты вложены в соответствующий основной объект. На рисунке 2.1 зафиксирован момент выбора модуля, связанного с объектом Sheet2 ("ПостановкаЗадачи"). Можно видеть, что на странице расположено довольно много элементов управления. Если выбрать в левом раскрывающемся списке один из объектов, как показано на рисунке, то правый раскрывающийся список отобразит список всех возможных событий данного объекта. Если теперь в правом списке выбрать одно из возможных событий, то в окне кода автоматически будет создана заготовка обработчика этого события. Ее можно наполнить содержанием. Все выбранные таким образом события считаются активными, в правом списке событий они выделены полужирным шрифтом, при их возникновении будет послано сообщение объекту, обработчик будет выполняться, даже если он пустой, - заготовка не была наполнена содержанием.
Конечно, при создании обработчиков событий вовсе не обязательно пользоваться такой технологией работы. Можно вообще не пользоваться заготовкой и самому полностью написать обработчик события, лишь бы выдерживались требования к имени обработчика, и, конечно же, нужно также помнить, что обработчики событий могут иметь и параметры. Поэтому мы рекомендуем пользоваться заготовками.
Для стандартных модулей левый раскрывающийся список окна кода не представляет интереса, а правый содержит имена всех методов стандартного модуля и имя раздела объявлений. Выбор имени из списка позволяет немедленно перейти в соответствующее место окна кода.
Пора, пожалуй, прервать плавный ход повествования и привести хоть небольшой фрагмент кода, иллюстрирующий высказанные в этой лекции утверждения. Мы отмечали роль стандартных модулей, одно из достоинств которых состоит в возможности их многократного использования. Рассмотрим следующую, часто возникающую на практике задачу. Пусть есть форма, содержащая два списка - элементы управления типа ListBox. При открытии формы пользователь может выбирать некоторые элементы в одном списке и переносить их в другой. Возникает вопрос, как организовать работу по переносу элементов, какие должны быть обработчики событий, где их разместить, где должна быть сосредоточена вся выполняемая работа по переносу элементов из списка в список в ответ на выбор пользователя. Решение кажется естественным и единственным, - все обработчики и вся обработка должна быть сосредоточена в модуле, связанном с формой. Действительно, для обработчиков событий это единственная возможность и поэтому кажется естественным, что и вся обработка должна быть сосредоточена в этом же модуле. Но, может быть, более эффективно, чтобы формальный обработчик события, находящийся в модуле формы, вызывал обычную процедуру с параметрами, которая может находиться в стандартном модуле общего назначения. Такая процедура может многократно использоваться разными формами, где есть списки и где возникает подобная задача.
На рис. 2.3 показана одна из таких форм. В нашем тестовом примере эта форма открывается по ходу дела при работе с
(рис 2.3) Форма TwoListsForm документа ExcelВ форме есть два списка и 4 командные кнопки. Перенос элементов из одного списка в другой можно осуществлять тремя разными способами:
Направление переноса задают командные кнопки. Знак на кнопке меняется в зависимости от того, какой из списков выбран - левый или правый. Кнопка "OK" на форме переносит данные из списка на лист Excel, кнопка "Cancel" завершает работу с формой без переноса данных.
Работу обработчиков событий, возникающих при работе с объектами формы, можно было бы без сомнения полностью описать в модуле, связанном с формой. Но мы поступили по-иному и создали стандартный модуль с именем ToolsMod, в который и вынесли содержательную часть обработки. Так что у нас появилось два модуля, - в одном находятся обработчики события, в другом - стандартном сосредоточен содержательный код, выполняющий обработку. Вот код модуля TwoLists, связанного с нашей формой, и содержащий обработчики событий:
Option Explicit
Private Sub CommandButton1_Click()
'Обработчик события Click кнопки "> <"
'Выборочный обмен данными между n- колоночными списками:
'ListBox1 <--> ListBox2
If CommandButton1.Caption = ">" Then
Call MoveSelectedItems(ListBox1.ColumnCount, ListBox1, ListBox2)
Else
Call MoveSelectedItems(ListBox2.ColumnCount, ListBox2, ListBox1)
End If
End Sub
Private Sub CommandButton2_Click()
'Обработчик события Click кнопки ">> <<"
'Перенос всех данных из одного n-колоночного списка
'в конец другого, возможно, не пустого списка: ListBox1 <--> ListBox2
If CommandButton2.Caption = ">>" Then
Call MoveAllItems(ListBox1.ColumnCount, ListBox1, ListBox2)
Else
Call MoveAllItems(ListBox2.ColumnCount, ListBox2, ListBox1)
End If
End Sub
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
'Обработчик события DblClick левого списка ListBox1 (имеет параметры!)
'При двойном щелчке выбранный элемент одного n-колоночного списка
'переносится в конец другого списка
Call MoveSelectedItems(ListBox1.ColumnCount, ListBox1, ListBox2)
End Sub
Private Sub ListBox2_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
'Обработчик события DblClick правого списка ListBox2
'При двойном щелчке выбранный элемент одного n-колоночного списка
'переносится в конец другого списка
Call MoveSelectedItems(ListBox2.ColumnCount, ListBox2, ListBox1)
End Sub
Private Sub CommandButton5_Click()
'Обработчик события Click кнопки "OK"
'Перенос данных из списка на лист Excel
'В область, заданную ячейкой с именем "Dom"
Call MoveListToRange(ListBox2.ColumnCount, ListBox2, "Dom")
Me.Hide
End Sub
Private Sub CommandButton6_Click()
'Обработчик события Click кнопки "Cancel"
Me.Hide
End Sub
Private Sub ListBox1_Enter()
'Обработчик события Enter, возникающего при получении фокуса
CommandButton1.Caption = ">"
CommandButton2.Caption = ">>"
End Sub
Private Sub ListBox2_Enter()
'Обработчик события Enter, возникающего при получении фокуса
CommandButton1.Caption = "<"
CommandButton2.Caption = "<<"
End Sub
Private Sub UserForm_Initialize()
'Обработчик события Initialize формы TwoListsForm
'Заполнение списка ListBox1
Dim MyArray(1 To 5, 1 To 2) As String
MyArray(1, 1) = "Петров" : MyArray(1, 2) = "Музыкант"
MyArray(2, 1) = "Сергеев" : MyArray(2, 2) = "Учитель"
MyArray(3, 1) = "Гурина" : MyArray(3, 2) = "Актриса"
MyArray(4, 1) = "Водкин" : MyArray(4, 2) = "Художник"
MyArray(5, 1) = "Козина" : MyArray(5, 2) = "Геолог"
ListBox1.ColumnCount = 2 : ListBox2.ColumnCount = 2
ListBox1.List() = MyArray
End Sub
Модуль TwoListsForm, связанный с формой, содержит 9 обработчиков событий, возникающих при работе с самой формой - ее инициализации, так и при работе пользователя с объектами, населяющими форму. Каждый из обработчиков выполняет свои специфические задачи. Когда пользователь переключается на работу с элементами левого или правого списка, то в тот момент, когда список получает фокус, возникает событие Enter. Обработчик события Enter изменяет заголовок у соответствующих командных кнопок, подготавливая, тем самым, передачу данных в нужном направлении. Такое автоматическое изменение направления передачи позволяет уберечь пользователя от ошибочных действий. Но, обратите внимание на главное в этом примере. Для удобства пользователя ему предоставлены три разных способа передачи данных из одного списка в другой. Поскольку обмен данными может вестись в двух направлениях, то при лобовом программировании нужно было бы написать 6 различных макросов. Мы же свели задачу к двум процедурам, вызываемых с разными значениями параметров. Тексты этих процедур мы поместили в стандартный модуль с именем ToolsMod, предполагая возможность его дальнейшего использования. Вот эти тексты:
Option Explicit
Public Sub MoveSelectedItems(ByVal n As Byte, ByVal ListBox1 As Object, _
ByVal ListBox2 As Object)
'Перемещает выделенные элементы первого списка в конец второго
'с одновременным удалением данных из первого списка.
'Оба списка имеют n столбцов.
Dim RowIndex1 As Byte, RowIndex2 As Byte, i As Byte, j As Byte
'Выборочный обмен данными между списками: ListBox1 -> ListBox2
With ListBox1
RowIndex2 = ListBox2.ListCount
RowIndex1 = 0
For i = 0 To .ListCount - 1
If .Selected(RowIndex1) Then
'Создается элемент нового списка и заполняется его первый столбец
ListBox2.AddItem .List(RowIndex1)
'Заполняются остальные столбцы элемента списка
For j = 1 To n - 1
ListBox2.Column(j, RowIndex2) = .Column(j, RowIndex1)
Next j
'Перемещенный элемент удаляется из списка
.RemoveItem RowIndex1
RowIndex2 = RowIndex2 + 1
Else
RowIndex1 = RowIndex1 + 1
End If
Next i
End With
End Sub
Public Sub MoveAllItems(ByVal n As Byte, ByVal ListBox1 As Object, _
ByVal ListBox2 As Object)
' Перемещает все элементы первого списка в конец второго,
' возможно, не пустого списка с одновременным удалением данных из
' первого списка. ListBox1 -> ListBox2
Dim RowIndex1 As Integer, RowIndex2 As Integer, i As Byte
RowIndex2 = ListBox2.ListCount
For RowIndex1 = 0 To ListBox1.ListCount - 1
With ListBox1
ListBox2.AddItem .List(0)
For i = 1 To n - 1
ListBox2.Column(i, RowIndex2) = .Column(i, 0)
Next i
RowIndex2 = RowIndex2 + 1
'Перемещенный,- это всегда первый элемент,удаляется из списка
.RemoveItem 0
End With
Next RowIndex1
End Sub
Public Sub MoveListToRange(ByVal n As Byte, List1 As Object, Dom As String)
'List1 - объект типа ListBox, состоящий из n столбцов.
' Его элементы переносятся в прямоугольную область активного листа,
' Dom - задает имя ячейки, расположенной в левом верхнем углу этой
' области.
Dim myr As Range
Dim i As Byte, j As Byte
Set myr = Range(Dom)
'Цикл по числу элементов списка.
For i = 0 To List1.ListCount - 1
'Цикл по числу столбцов списка.
For j = 0 To n - 1
myr.Offset(j, i) = List1.Column(j, i)
Next j
Next i
End Sub
Public Sub ClearRange(Dom As String)
'Эта процедура очищает содержимое области листа рабочей книги,
'заданной ячейкой с именем Dom
Dim myr As Range, Row As Byte, Col As Byte
Set myr = Range(Dom)
Col = 0: Row = 0
While myr.Offset(Row, Col) <> ""
While myr.Offset(Row, Col) <> ""
'Чистка содержимого
myr.Offset(Row, Col).ClearContents
Col = Col + 1
Wend
Row = Row + 1
Col = 0
Wend
End Sub
Две последние процедуры не связаны напрямую с рассматриваемой нами задачей перемещения данных между списками. Тем не менее, мы решили их привести, поскольку они являются частью нашего тестового примера. С другой стороны они также решают общие задачи, возникающие при работе с документами Excel и потому, по праву, помещены в стандартный модуль.
Завершая разговор о переиспользовании стандартных модулей, предлагаем взглянуть на форму, взятую из совсем другого документа - документа Word.
(рис 2.4) Форма TwoLists, взятая из документа WordХотя форма и похожа на форму, связанную с MoveSelectedItems и MoveAllItems. Конечно, думая о возможности многократного использования этих процедур, мы заранее побеспокоились о возможности работы с произвольным числом столбцов в списках.
Создавая стандартные модули, предполагайте возможность их многократного использования.
Замечание:
В заключение, хотим обратить Ваше внимание на одну важную программистскую деталь. Вот как выглядит заголовок одной из наших процедур:
Public Sub MoveAllItems(ByVal n As Byte, ByVal ListBox1 As Object, _
ByVal ListBox2 As Object)
Вы видите, что тип формальных параметров для ListBox1 и ListBox2 задан как Object и, следовательно, является ListBox и при этом передача параметров производится корректно. В приложении Excel при написании процедуры также можно указать тип ListBox, но возникнет ошибка в момент передачи параметров.
В процессе тестирования Office 2000 мы указали разработчикам на это рассогласование, оно должно быть учтено в последующих версиях. Будем надеяться, что в будущем мы сможем в процедурах при объявлении формальных параметров, являющихся объектами, указывать их настоящий тип.
Напомним, проект документа мы рассматриваем, как совокупность модулей разного типа. Компонентами каждого модуля являются переменные уровня модуля, описанные в разделе объявления модуля, и методы модуля.
Первое правило видимости гласит:
"Все компоненты модуля видимы в пределах этого модуля".
Это означает, что любая переменная из раздела объявлений модуля является глобальной по отношению к методам модуля, она доступна в каждом из методов, если только внутри метода нет объявления локальной переменной с тем же именем. Общедоступность глобальных переменных облегчает передачу информации между методами модуля. Вместе с тем общедоступность и плохое, опасное качество. Каждый метод может изменить глобальную переменную. Поэтому глобальных переменных не должно быть много. За судьбой каждой из них надо внимательно следить.
Каждый метод модуля также общедоступен внутри модуля и может быть вызван другими методами модуля. Понятно, это не касается обработчиков событий. Последние вызываются системой и не могут быть вызваны никакими другими методами модуля.
Второе правило видимости гласит:
"Область видимости компонент модуля расширяется на весь проект, если компонент объявлен со спецификатором
Public".
Каждый компонент модуля - переменная или метод может быть снабжен спецификатором области видимости, который имеет два возможных значения - Public и Private. Если задан спецификатор Public, то это означает, что компонент общедоступен в пределах всего проекта. Спецификатор Private делает компонент закрытым для других модулей проекта. Он видим только в своем родном модуле.
Если при объявлении переменных модуля спецификатор области видимости опущен и указано только ключевое слово Dim, то такие переменные считаются закрытыми, - действует спецификатор Private. Для методов спецификатор области видимости можно опускать. В этом случае действует следующее правило. Все методы стандартных модулей имеют по умолчанию спецификатор Public и являются доступными во всем проекте. Методы модулей - классов и модулей, связанных с объектами, по умолчанию являются закрытыми и имеют статус Private.
Есть еще одно важное правило, касающееся общедоступных компонент. Спецификатор Public еще не гарантирует, что имя компонента будет видимо вне модуля. Чтобы компонент был видимым вне модуля, следует использовать его полное имя, которое строится по обычным правилам построения сложных имен. Оно состоит из имен, разделенных точкой, - имени компонента, имени модуля и, возможно, имени проекта. Имя проекта может потребоваться в тех случаях, когда речь идет о системе взаимосвязанных документов. Внутри одного проекта его имя можно опускать, но, заметьте, нельзя опускать имя модуля для Public переменных модулей, связанных с объектами. Для них допустимы только полные имена. Вообще разумно не иметь Public переменных для таких модулей.
Для компонент, принадлежащих стандартным модулям, внутри одного проекта разрешается опускать имя модуля. Как правило, так и поступают, но, иногда, это может привести к коллизиям, если в нескольких стандартных модулях есть компоненты с одинаковым именем. Public - компоненты модулей классов используются так, как это принято в объектно-ориентированном программировании. Далее мы об этом поговорим подробнее.
До сих пор мы говорили о проекте одного документа. Давайте немного расширим рамки и поговорим о совокупности или системе документов, о том, как связаны между собой проекты такой системы.
Оставим пока в стороне создание и использование AddIns, Com AddIns, DLL, ActiveX, Internet документов. Им будет посвящен особый разговор. Пока поговорим о более понятных случаях использования совокупности документов. Прежде всего, заметим, что корневой объект Application любого из приложения Office 2000 содержит коллекцию документов. Так что изначально предполагается возможность открытия в одном приложении нескольких документов и совместной работы с ними. Но совместная работа не ограничивается однотипными документами, принадлежащими одному приложению. Уже в первой лекции мы подробно, с примерами рассмотрели возможность создания и одновременной работы с несколькими объектами Application. Следовательно, всегда есть возможность открытия совокупности документов разного типа и совместной работы с ними, предоставляя пользователю возможность переключаться по ходу дела от одного документа к другому.
Совместная работа с несколькими документами, возможно, разного типа типична для Office 2000. Программным проектом системы документов будем называть совокупность программных проектов всех документов, входящих в систему.
Остается ответить на главный вопрос, - могут ли взаимодействовать между собой проекты отдельных документов. Можно ли из одного проекта вызывать процедуры стандартного модуля другого проекта, можно ли пользоваться объектами класса другого проекта? Можно ли иметь глобальные переменные уровня системы документов для передачи информации от одного документа к другому? На все эти вопросы существует один ответ, - Да! С программным проектом системы документов в Office 2000 достаточно просто работать, достаточно просто организовать нужные связи между программными проектами отдельных документов, нужно только уметь это делать.
Наше определение системы документов достаточно общее, - совокупность открытых документов составляет систему. О связях между документами ничего не говорится. Пользователь сам может решать, с каким документом он будет работать в следующий момент, и волен свободно переходить от одного документа к другому. Но документы в системе могут быть связаны. Рассмотрим связи двух видов:
Два документа A и B связаны по вызову, если в одном из документов, например A, вызывается документ B. Документ A называют родителем, а документ B, соответственно, потомком. Совокупность связанных по вызову документов образует дерево связанных документов. Корень дерева - это тот первичный документ, из которого и начинаются вызовы других документов. Связи в дереве направлены от корня к потомкам.
Представив совокупность связанных по вызову документов в виде дерева, мы сознательно упростили картину, фактически, она гораздо сложнее, поскольку допустимы циклы. Вызов документа означает его открытие, если он не был еще открыт и его активизацию. Вполне допустимо, что несколько открытых документов в произвольном порядке и неоднократно становятся активными. Тем не менее, зачастую, особенно при проектировании полезно рассматривать древесную структуру организации связей по вызову.
Проекты A и B связаны по ссылке, если один из проектов, например B, ссылается на проект A. Проект A называют родителем, а проект B, соответственно, потомком. Совокупность связанных по ссылке проектов образует дерево связанных проектов. Корень дерева - это тот первичный проект, на который ссылаются остальные проекты. Связи в дереве ссылок направлены от потомков к корню. Вообще говоря, проекты, связанные по ссылке, также могли бы организовывать более сложную структуру, чем дерево. Действительно, взаимные ссылки кажутся допустимыми. Но это не так. Помните, циклические ссылки проектов VBA документов не разрешены. Так что при разработке проекта системы документов Office 2000 исходите из древесной структуры организации связей между проектами. На рисунке 2.5 показаны три возможные структуры организации системы документов Office 2000.
(рис 2.5) Три структуры организации системы документовРассмотрим теперь технический вопрос, как организовать ссылку из проекта B на проект A. Для этого нужно сделать две вещи, во-первых, дать собственные имена проектам, во-вторых, - включить ссылку в проекте B на проект A. Имя нужно дать, по крайней мере, родительскому проекту A. О том, как это делается, мы уже рассказали в разделе "Свойства проекта" этой лекции. На рис. 2.2 показано соответствующее диалоговое окно.
Задание ссылки выполняется также стандартным образом. В окне Редактора Visual Basic нужно выбрать соответствующий проект, в нашем примере проект B, и из меню Tools выбрать команду References. Если документ, содержащий проект A открыт, то в окне ссылок наряду с другими возможными для ссылок компонентами появится флажок с именем проекта A, останется только его включить.
Когда речь идет о системе документов, то один из основных вопросов - организация обмена информацией между отдельными документами системы. Когда речь идет о системе проектов, то, как правило, существует сравнительно небольшая по объему общая информация, которая используется во всех проектах. Сейчас мы и поговорим о том, как организовать такой общий информационный пул, содержащий глобальную информацию уровня системы проектов. Задача состоит в том, чтобы хранимые данные могли быть доступны во всех проектах для чтения и записи. Решить задачу можно по-разному, перечислим основные способы:
Public переменных, в совокупности и определяющих общую для всей системы информацию. Систему документов спроектируем так, чтобы их проекты были связаны по ссылке, и корнем дерева был основной документ, хранящий глобальную информацию. В этом случае каждый проект будет иметь доступ к этой информации. Напомним, циклические ссылки между проектами не разрешены, отсюда следует, что нельзя часть информации поместить в проект А, часть в проект В и сделать эту информацию общедоступной для обоих проектов. Возможна только древесная организация хранения общей информации. Самая общая информация хранится в корневом проекте, в "дочерних" проектах хранится информация необходимая потомкам этих дочерей, и так до терминальных проектов.Заканчивая обсуждение этого вопроса, еще раз подчеркнем, что способ передачи информации между проектами, основанный на общем информационном пуле, как и во всех случаях работы с глобальными переменными, является опасным. Поэтому объем этой информации не должен быть большим и легко контролируемым, так что по этим соображениям первые два способа являются наиболее предпочтительными, а программисты, скорее всего, выберут первый из них. В заключение, напомним, что передача информации между проектами через общую область важный, но не единственный способ организации взаимодействия. Основным и надежным способом передачи данных для программистов является вызов Public методов проекта, - передача им аргументов и получение результатов, то, что называется аппаратом формальных - фактических параметров.
Перейдем теперь к примерам организации взаимодействия системы документов. Один из примеров на эту тему мы уже приводили в предыдущей лекции. Возможно, стоит еще раз обратиться к нему и разобрать, как из документа Word происходит переключение на работу с
(рис 2.6) Открытие рабочей книги, содержащей ссылкиПоскольку в данной рабочей книге есть ссылки на другие рабочие книги, то при открытии документа появляется сообщение о том, что, возможно, следует обновить документ, поскольку после его закрытия могли измениться документы, на которые он ссылается. На листе ChooseBook ". Эту кнопку пользователь должен нажать в тот момент, когда он стоит на распутье, и должен выбрать, с какой следующей книгой он хочет работать. Вот текст обработчика события " Click ":
Private Sub CommandButton5_Click()
ChooseBook
End Sub
Public Sub ChooseBook()
'Выбор книги, с которой будет работать пользователь
Const PathDir As String = "e:\O2000\CD2000\Ch2\"
Dim Answer As Variant
Dim CurrentBook As String
Dim Book As Workbook
Dim Found As Boolean
Answer = InputBox(prompt:= _
"Выберите книгу, с которой будете работать (1/2/3)", Default:=1)
Select Case Answer
Case 1
CurrentBook = "BookOne.xls"
Case 2
CurrentBook = "BookTwo.xls"
Case 3
CurrentBook = "BookThree.xls"
Case Else
CurrentBook = "I don't know such book"
End Select
MsgBox ("Your choose is " CurrentBook)
'Проверяем есть ли уже книга в коллекции
Found = False
For Each Book In Workbooks
If Book.Name = CurrentBook Then
Found = True
Exit For
End If
Next Book
If Found Then
'Активизируем выбранную книгу
Workbooks(CurrentBook).Activate
ElseIf CurrentBook = "I don't know such book" Then
MsgBox ("I don't know such book")
Else 'Добавляем книгу в коллекцию
Workbooks.Open PathDir CurrentBook
Workbooks(CurrentBook).Activate
End If
End Sub
Пользователю предоставляется право выбора одной из трех
Public Sub PrintGlobal()
Dim MyrOne As Range, MyrTwo As Range, MyrThree As Range
Debug.Print ("Работает процедура PrintOne")
Debug.Print ("Печать глобальных переменных")
Debug.Print (One "-" Two "-" Three)
Debug.Print ("Допустимы ссылки на ячейки различных рабочих книг!")
Set MyrOne = Range("[BookOne.xls]Sheet1!D1")
Debug.Print MyrOne
Set MyrTwo = Range("[BookTwo.xls]Sheet1!D1")
Debug.Print MyrTwo
Set MyrThree = Range("[BookThree.xls]Sheet1!D1")
Debug.Print MyrThree
MsgBox ("Everything is OK! Look at Immediate Window")
End Sub
Вот результаты отладочной печати в конце работы этой процедуры:
Работает процедура PrintOne Печать глобальных переменных My value is One -My value is One -My value is One Допустимы ссылки на ячейки различных рабочих книг! BookOne BookTwo BookThree
Всем глобальным переменным предварительно было присвоено одно и то же значение. Книги BookOne, BookTwo, BookThree.xls на первой странице в ячейке D1 хранят свои названия, их и печатает наша процедура, вызываемая в проекте одной из этих книг.
Рассмотрим теперь систему документов из тех же трех книг BookOne, BookTwo, BookThree.xls, проекты которых связаны ссылками. Наша основная цель, - продемонстрировать на примере работу трех проектов с общей информацией и общими методами, вызываемыми во всех проектах.
Вначале несколько слов об интерфейсе нашей системы документов. Мы сделали его достаточно простым. На первом рабочем листе каждой из трех книг расположены три командные кнопки: ChooseBook, ChangeGlobal, PrintGlobal.
(рис 2.7) Командные кнопки управления системой документовКогда пользователь щелкает первую из этих кнопок, соответствующий обработчик, открывает диалоговое окно, позволяет выбрать нужную книгу и активизирует ее. Вторая из кнопок отвечает за работу с общей информацией. В каждом из документов общая информация изменяется соответствующим образом. Обработчик третьей кнопки демонстрирует работу с общими методами. Перейдем теперь к деталям организации совместной работы трех документов и их проектов. Мы начали с того, что проектам дали имена, совпадающие с именами книг. Проект книги BookOne является в нашей системе основным, - он хранит глобальную информацию, общие методы и на него ссылаются остальные проекты системы. Глобальную информацию будем представлять тремя общими переменными, расположив их в стандартном модуле ModuleOne проекта BookOne:
Option Explicit 'Глобальные переменные системы документов One - Two - Three Public One As String, Two As String, Three As String
Три обработчика события Click для трех командных кнопок вызывают три общих метода ChooseBook, ChangeGlobal, PrintGlobal:
Private Sub CommandButton5_Click()
ChooseBook
End Sub
Private Sub CommandButton4_Click()
ChangeGlobal
End Sub
Private Sub CommandButton3_Click()
PrintGlobal
End Sub
Назначение процедуры ChooseBook и ее текст уже приведены. Заметим только, что эта общая процедура вызывается соответствующими обработчиками во всех трех модулях. Мы поместили ее не в стандартный модуль проекта BookOne, а в модуль, связанный с листом Sheet1 книги BookOne. Это, однако, ничуть не мешает ее вызову из других проектов. Процедура ChangeGlobal, помещена в стандартный модуль с именем ModuleOne. Она позволяет работать с глобальными переменными, и поскольку в каждом проекте работа с ними выполняется по-своему, то она не является общей процедурой. В каждом проекте есть свой вариант этой процедуры, который и вызывается обработчиком Click командной кнопки ChangeGlobal. Заметим, что когда речь идет об основном проекте, где описаны глобальные переменные, при обращении к ним не требуется использовать полные имена:
Public Sub ChangeGlobal()
One = "My value is One "
Two = "My value is One "
Three = "My value is One "
End Sub
В стандартный модуль ModuleOne мы поместили и процедуру PrintGlobal. Текст ее тоже уже приведен. Заметим, что в каждом проекте будет свой вариант этой процедуры, но в каждом из них на последнем этапе работы будет вызываться процедура PrintGlobal проекта BookOne. В стандартном модуле находится функция Plus1, которая будет вызываться из других проектов. Это очень простая функция, но она выполняет свою важную роль, демонстрируя передачу информации от проекта к проекту через аппарат формальных - фактических параметров процедур и функций. Вот ее текст:
Public Function Plus1(ByVal X As Integer) As Integer
Plus1 = X + 1
End Function
Проект BookTwo ссылается на проект BookOne, использует его общие переменные и вызывает его методы - ChooseBook, PrintGlobal, Plus1. Он является хранителем общей информации для проектов BookTwo и BookThree:
Option Explicit 'Глобальная переменная документов BookTwo, BookThree Public TwoThree As String
Поскольку все три документа устроены одинаково, то достаточно привести тексты одного обработчика и двух процедур, задающие специфику работы в проекте этого документа:
Private Sub CommandButton1_Click()
BookOne.Sheet1.ChooseBook
End Sub
Public Sub ChangeGlobal()
'Изменение значений глобальных переменных системы документов
BookOne.ModuleOne.Two = "My value is Two "
TwoThree = "My value is Two - Two "
End Sub
Public Sub PrintGlobal()
Dim Number As Integer
Debug.Print ("Работает процедура PrintTwo")
Debug.Print ("Печать глобальных переменных")
Debug.Print TwoThree
'Вызов процедур и функций проекта BookOne
Number = BookOne.ModuleOne.PLus1(1)
Debug.Print ("Это книга " Number)
ChangeGlobal
BookOne.ModuleOne.PrintGlobal
End Sub
Полагаю, что эти тексты достаточно красноречивы и не нуждаются в дальнейших пояснениях. Несколько слов теперь о третьем документе системы. Он введен только для того, чтобы показать, что при росте системы новых трудностей не возникает. Вот его тексты:
Private Sub CommandButton1_Click()
BookOne.Sheet1.ChooseBook
End Sub
Public Sub ChangeGlobal()
'Изменение значений глобальных переменных системы
BookOne.ModuleOne.Three = "My value is Three "
BookTwo.ModuleTwo.TwoThree = "My value: Two-Three"
End Sub
Public Sub PrintGlobal()
Dim Number As Integer
Debug.Print ("Работает процедура PrintThree")
Number = BookOne.ModuleOne.Plus1(2)
Debug.Print ("Это книга " Number)
ChangeGlobal
BookTwo.ModuleTwo.PrintGlobal
End Sub
Приведем результаты отладочной печати, полученные при нажатии кнопки PrintGlobal в книге BookThree:
Работает процедура PrintThree Это книга 3 Работает процедура PrintTwo Печать глобальных переменных My value: Two-Three Это книга 2 Работает процедура PrintOne Печать глобальных переменных My value is One -My value is Two -My value is Three Допустимы ссылки на ячейки различных рабочих книг! BookOne BookTwo BookThree
В заключение приведем вид окна проектов, где отображены все проекты нашей системы.
(рис 2.8) Три проекта One - Two - Three
Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: BookOne, BookTwo, BookThree, DocOne, DocFour.
Мы уже не раз говорили об идеологии офисного программирования, - целью разработки является создание системы документов, а не создание программного проекта (приложения). Программный проект это часть документа, хранится вместе с документом и не может быть отделен от него. Совокупность программных проектов всех документов, входящих в некоторую систему, может рассматриваться как программный проект этой системы документов. Возможны два важных частных случая:
Основная цель этой лекции рассмотреть, как связаны между собой программные проекты системы документов, какова структура программного проекта одного документа, его основные компоненты.
Но прежде, чем приступить к реализации этой цели, хочется рассмотреть хотя бы обзорно, некоторые, более общие вопросы, связанные с проектами и документами. Прежде всего, заметим, что " документ документу рознь". Одно дело, когда речь идет о настройке стандартного документа, придания ему некоторых дополнительных функций, совсем другое дело - разработка большой и сложной системы, затрагивающей многие службы офиса или предприятия. Считается, что Office 2000 - это масштабируемая среда разработки. Это означает, что она подходит для решения небольших, простых задач, так и для решения сложных задач. К простым задачам относится создание отдельных, возможно и сложных документов, создание сравнительно небольшой системы документов. Сложными задачами могут быть, например:
Есть еще одно измерение в разработке документов Office 2000. Оно связано с разработкой документов специального вида. Сюда относится разработка: шаблонов, Мастеров (Wizards), компонент, расширяющих функциональные возможности других документов. Эти компоненты могут быть двух видов - AddIns и ComAddIns. Дадим краткую характеристику этим видам документов:
AddIns являются компонентами, специфическими для того или иного приложения Office 2000. Один AddIn может использоваться, например, только в документах Word, другой - в документах Excel.Com AddIns - то компоненты, которые могут использоваться в разных приложениях. Достаточно, чтобы эти приложения допускали использование Com - объектов, построенных на основе Com - модели Com AddIns отличается от ActiveX объектов тем, что с программной точки зрения представляет DLL - динамически загружаемую библиотеку, в то время как ActiveX - это исполняемые файлы. Заметим, что ранее эти компоненты не могли быть созданы в офисной среде, необходимо было использовать для их создания Прежде, чем перейти к рассмотрению вопроса о том, что такое проект документа, скажем несколько слов о проектировании документа. Хотя эти термины близки по звучанию, но различны по смыслу. Тема проектирования документа (системы документов), его "жизненного цикла" лежит вне нашего рассмотрения, но хоть несколько слов ей уделить нужно и мы решили, что это можно сделать здесь в разделе о проектах документах.
Прежде всего, под "жизненным циклом" мы понимаем период от замысла программного продукта до его "кончины". Обычно рассматриваются следующие фазы этого процесса:
Проектирование <=> Разработка <=> Отладка <=> Развертывание и Сопровождение <=> Модификация
Все это называется циклом, поскольку после каждой фазы возможен возврат к предыдущим этапам. Вот некоторые штрихи к каждому из этапов:
AddIns, DLL, ActiveX и Com AddIns компоненты. Работая над одним проектом, думайте о будущем, - создавайте компоненты, допускающие их использование в других проектах.Замечание:
Хочу привести одно из свежих впечатлений о процессе отладки. Будучи бета - тестером Office 2000, я поразился, сколь мощной является построенная Microsoft система отладки своих продуктов. Специальный узел в Интернете для тестеров, конференции для них, удобный для тестеров способ публикации своих отчетов о найденных ошибках, практически моментальная реакция разработчиков на каждую из найденных ошибок.
От глобальных вопросов проектирования документов спустимся на землю и рассмотрим один единственный документ Office 2000. Сейчас нас будет интересовать, что собой представляет программный проект одного документа.
Каждый вновь создаваемый документ обладает определенной архитектурой, поскольку строится на основе каркаса документов, заданного совокупностью библиотек Office 2000. Каркас каждого конкретного документа определяется, прежде всего, тем, в каком приложении создается документ, - корневым объектом Application. Частью архитектуры вновь создаваемого документа является и создаваемый по умолчанию программный проект. Здесь и в дальнейшем, если только не будет делаться специальных оговорок, под программным проектом понимается совокупность модулей. Модули, составляющие программный проект, могут быть следующих типов:
Модули первого типа всегда связаны с объектами, реагирующими на события. Их главное назначение состоит в том, что они содержат обработчики таких событий. Объектов, имеющих события, довольно много, но только с некоторыми из них связываются модули. Попробуем с этим разобраться подробнее.
Когда создается документ Word, то появляется один такой объект с именем ThisDocument, задающий сам документ. При создании документа Excel наряду с объектом ThisWorkbook, задающим рабочую книгу, появляется коллекция объектов Sheets, задающих каждую страницу этой книги. Все упомянутые объекты могут реагировать на события, они являются основными объектами, с ними и связываются модули. Но, заметьте, при создании презентации в приложении Power Point объект ThisPresentation, задающий презентацию, и связанный с ним модуль автоматически не появляется. В этот момент должен возникнуть вопрос, а почему? Дело в том, что объект класса Presentation не реагирует на события, следовательно, и модуль не может появиться. Это же касается и объектов класса , - у них тоже нет событий.
Есть и другие объекты, с которыми логично было бы связать модули, но по умолчанию этого не делается. В каждом из приложений, наряду с объектом, задающим сам объект - презентацию, документ, рабочую книгу, присутствует и корневой объект Application, задающий само приложение. Этот объект, как известно, также реагирует на события. Почему же нет модуля, ему соответствующего? Здесь есть одна тонкость. Создаваемые по умолчанию объекты Application не реагируют на события. На события ApplicationWithEvents. Чуть позже мы рассмотрим, как создаются такие объекты и как появляются для них соответствующие модули.
До сих пор мы говорили о модулях, создаваемых по умолчанию для новых документов. Но такие модули могут появляться и в процессе работы над существующим документом. Наиболее известным видом таких модулей являются модули форм (диалоговых окон), составляющие в проекте отдельную папку Forms. Всякий раз, когда Вы добавляете в проект новую форму, появляется модуль, связанный с этой формой. Есть и другие ситуации, при которых появляются новые модули проекта, например, при добавлении нового листа в книгу Excel, появляется и соответствующий ему модуль.
Далеко не со всяким объектом, который может реагировать на события, связывается модуль. Для всех элементов управления ( Controls ), размещаемых в форме или непосредственно в документе Word, листе не имеют событий и потому с ними не связаны модули в момент создания презентации. Сейчас мы опровергнем это утверждение, уточним его. Если на слайде размещаются элементы управления - кнопки списки и прочее, то появляется модуль, связанный с этим слайдом, и обработчики событий, связанных с элементами управления размещаются в этом модуле.
Есть еще одна группа объектов, имеющих обработчиков событий в обязательном порядке, но не имеющая "своих" модулей. Возможно, Вы уже догадались, что речь идет о командах меню, командных кнопках и других элементах управления, размещаемых на панелях - объектах класса CommandBar. Макросы, задающие обработку событий, связанных с этими элементами, помещаются в стандартные модули.
Модули этого типа не создаются по умолчанию. Их создает программист явным образом. Каково их назначение? Можно ли без них обойтись? Сколько таких модулей следует создавать? Вот типичные вопросы, стоящие перед программистом в процессе работы над программным проектом. Постараемся сейчас на них ответить.
Стандартные модули - это основной вид модулей. Большую часть всех процедур и функций, которые пишет программист, следует размещать в стандартных модулях. Разумно иметь не один большой стандартный модуль, а некоторое множество относительно небольших модулей. В один модуль следует помещать набор функций, совместно вызывающих друг друга, связанных общей темой, общей функциональной направленностью. В модулях первого типа, связанных с объектами, следует помещать только те процедуры и функции, которые непосредственно обрабатывают события. Все общие процедуры следует выносить в стандартные модули. Вот три основные причины, по которым следует создавать небольшие стандартные модули:
Замечание:
Опцию "On
Итак, подводя итог, заметим, что разумно иметь набор стандартных модулей, располагая в них все содержательные процедуры обработки данных документа. И, конечно, если таких процедур много, то модули должны быть специализированными, объединяя под одной крышей данные и функционально связанные с ними процедуры и функции. Хотя все модули имеют одинаковый уровень иерархии, реально всегда выделяется головной модуль, в котором следует сосредоточить описание глобальных переменных, используемых всеми модулями документа. Во многих ситуациях стандартный модуль следует оформить как модуль класса.
Специальный тип модуля - модуль класса является той синтаксической формой, которая используется для создания классов, разработанных программистом для решения своей задачи. Учитывая важность темы, мы посвятили созданию классов отдельную лекцию этой книги.
Этот модуль создается автоматически при первом вызове Macrorecoder. В приложении Word все созданные Macrorecoder макросы размещаются в модуле с именем NewMacros. При создании макроса требуется указать, в модуль какого из проектов - Normal.dot или проекта, связанного с документом, должен быть записан макрос. В приложении Excel каждый новый макрос записывается в отдельный модуль. Это не всегда удобно и, зачастую, руками макросы объединяются в одном модуле, особенно, если все они решают некоторую общую задачу. Единственное отличие модулей макросов от стандартных модулей состоит в том, что они создаются системой, а не программистом. Поскольку это не столь существенно, то мы не будем в дальнейшем выделять этот тип модулей, и будем рассматривать их далее как стандартные модули.
Модули VBA имеют очень простую синтаксическую структуру. В отличие от большинства языков программирования здесь нет большого числа разделов, нет разделения на интерфейс и реализацию. Все сделано предельно просто. Каждый модуль вне зависимости от его типа имеет всего два раздела:
Sub ) либо функция ( Function ).Но коль скоро зашла речь о классификации методов, то давайте уточним определения некоторых, часто встречающихся понятий. Начнем с понятия "макрос". Под макросом мы будем понимать любую процедуру без параметров. Отсутствие параметров - это главный отличительный признак макроса. Чаще всего, о макросах идет речь в следующих контекстах:
CommandBar.Элементы командной панели имеют по существу одно событие и один метод, вызываемый в ответ на появление события. Этот метод, обрабатывающий событие, мы называем макросом. Но есть и другие объекты, у которых много событий и соответственно много методов, вызываемых в ответ на возникновение того или иного события. Такие методы мы называем обработчиками событий. Обработчики событий, как правило, макросы, но иногда они могут иметь параметры. Есть еще одно существенное ограничение, накладываемое на правило построения имени обработчика. Имя является двойным и составляется из имени объекта и имени события. Два имени разделяются (объединяются) знаком подчеркивания.
Давайте немного поговорим о том, как выглядит работа с модулями в среде Редактора
(рис 2.1) Окно проекта и окно кодаОкно проекта или, как еще говорят, Проводник Проекта (Project Explorer) отображает структуру проекта в привычном для всех Проводников виде - дерева раскрывающихся папок. В этом окне отображается программные проекты всех открытых документов. Так что, если Вы работаете с семейством документов, то Проводник покажет полностью программный проект всего семейства. Специальная папка References покажет взаимные связи проектов отдельных документов.
Давайте разберемся в структуре проекта, показанного на рисунке 2.1. Этот проект связан с MasterFNew. В данном случае мы имеем дело с единственным документом, хотя в окне показаны два проекта. Дело в том, что наш документ работает с Solver ). В Office 2000 AddIn, ссылку на который надо включить в меню References, что и приводит к появлению соответствующего проекта в Проводнике. Заметьте, проект Solver закрыт паролем от просмотра, что не позволяет просмотреть его структуру и тем более код его модулей. Что же касается проекта нашего документа, то его структура раскрыта полностью. Он содержит:
Form.Main играет роль основного модуля проекта и cодержит описание глобальных переменных всего проекта.Solver.Проект данного документа довольно типичен и содержит все типы модулей за исключением модулей классов.
Проект имеет несколько терминальных свойств, которые можно задать в окне "Project Properties" (Свойства Проекта), оно выводится после щелчка правой кнопкой мыши по названию проекта или одной из его папок в окне проектов и последующего выбора из контекстного меню команды Project Properties.
(рис 2.2) Окно свойств проектаВ окне две вкладки General и Protection. На вкладке General задается имя проекта, его описание, указываются файлы справки и
Имя проекта используется не только для идентификации проекта в окне просмотра. Оно играет важную роль в тех случаях, когда документ является частью общей системы документов или используется, как компонент, вызываемый в других документах ( COM объект). В последнем случае имя проекта идентифицирует компонент в реестре Windows. Во всех случаях проект можно рассматривать как компонент, отвечающий требованиям COM модели, Согласно принятой в Public - элементы. Имя проекта играет важную роль в этом процессе, - оно является именем библиотеки типов TypeLib. Эта библиотека содержит описание объектов и интерфейсов, которые поддерживает компонент. У имени проекта есть еще одна важная роль, оно используется также при построении полного имени открытых компонентов проекта. Всякий раз, когда в проекте A надо сослаться на Public переменную или Public метод проекта B, необходимо использовать полное имя открытого компонента:
имя_проекта.имя_модуля.имя_открытого_компонента
Заметьте, при именовании используется имя проекта, а не имя документа, содержащего проект.
Вкладка Protection позволяет защитить проект от просмотра и редактирования. В большинстве случаев при передаче документа пользователям, проект должен быть защищен как от несанкционированного просмотра его модулей, так и коррекции программного текста. Включив флажок "Lock project for viewing", Вы закрываете проект, его структура будет недоступна для просмотра, если неизвестен пароль. Мы описывали такую ситуацию ранее, когда говорили о проекте Solver, связанном с
Окно проекта согласовано с окном кода. Когда Вы выбираете некоторый модуль двойным щелчком в окне проекта, его код отображается в окне кода. Но окно кода отображает не только код модуля. Важную роль играют два раскрывающихся списка, расположенные в верхней части окна кода. Они играют особую роль для модулей, связанных с объектами, реагирующими на события. Ранее, мы уже говорили, что такие объекты, как главный документ, страницы Excel, слайды презентации, являются основными, в них может быть вложено большое число объектов, также реагирующих на события. На страницах Excel, например, можно размещать командные кнопки, списки и другие элементы управления. Поэтому модуль основного объекта связан фактически с большим числом объектов, реагирующих на события. Следовательно, нужно иметь возможность отображать структуру вложенных объектов.
Левый раскрывающийся список окна кода показывает все объекты, выбранного модуля, позволяя понять, какие объекты вложены в соответствующий основной объект. На рисунке 2.1 зафиксирован момент выбора модуля, связанного с объектом Sheet2 ("ПостановкаЗадачи"). Можно видеть, что на странице расположено довольно много элементов управления. Если выбрать в левом раскрывающемся списке один из объектов, как показано на рисунке, то правый раскрывающийся список отобразит список всех возможных событий данного объекта. Если теперь в правом списке выбрать одно из возможных событий, то в окне кода автоматически будет создана заготовка обработчика этого события. Ее можно наполнить содержанием. Все выбранные таким образом события считаются активными, в правом списке событий они выделены полужирным шрифтом, при их возникновении будет послано сообщение объекту, обработчик будет выполняться, даже если он пустой, - заготовка не была наполнена содержанием.
Конечно, при создании обработчиков событий вовсе не обязательно пользоваться такой технологией работы. Можно вообще не пользоваться заготовкой и самому полностью написать обработчик события, лишь бы выдерживались требования к имени обработчика, и, конечно же, нужно также помнить, что обработчики событий могут иметь и параметры. Поэтому мы рекомендуем пользоваться заготовками.
Для стандартных модулей левый раскрывающийся список окна кода не представляет интереса, а правый содержит имена всех методов стандартного модуля и имя раздела объявлений. Выбор имени из списка позволяет немедленно перейти в соответствующее место окна кода.
Пора, пожалуй, прервать плавный ход повествования и привести хоть небольшой фрагмент кода, иллюстрирующий высказанные в этой лекции утверждения. Мы отмечали роль стандартных модулей, одно из достоинств которых состоит в возможности их многократного использования. Рассмотрим следующую, часто возникающую на практике задачу. Пусть есть форма, содержащая два списка - элементы управления типа ListBox. При открытии формы пользователь может выбирать некоторые элементы в одном списке и переносить их в другой. Возникает вопрос, как организовать работу по переносу элементов, какие должны быть обработчики событий, где их разместить, где должна быть сосредоточена вся выполняемая работа по переносу элементов из списка в список в ответ на выбор пользователя. Решение кажется естественным и единственным, - все обработчики и вся обработка должна быть сосредоточена в модуле, связанном с формой. Действительно, для обработчиков событий это единственная возможность и поэтому кажется естественным, что и вся обработка должна быть сосредоточена в этом же модуле. Но, может быть, более эффективно, чтобы формальный обработчик события, находящийся в модуле формы, вызывал обычную процедуру с параметрами, которая может находиться в стандартном модуле общего назначения. Такая процедура может многократно использоваться разными формами, где есть списки и где возникает подобная задача.
На рис. 2.3 показана одна из таких форм. В нашем тестовом примере эта форма открывается по ходу дела при работе с
(рис 2.3) Форма TwoListsForm документа ExcelВ форме есть два списка и 4 командные кнопки. Перенос элементов из одного списка в другой можно осуществлять тремя разными способами:
Направление переноса задают командные кнопки. Знак на кнопке меняется в зависимости от того, какой из списков выбран - левый или правый. Кнопка "OK" на форме переносит данные из списка на лист Excel, кнопка "Cancel" завершает работу с формой без переноса данных.
Работу обработчиков событий, возникающих при работе с объектами формы, можно было бы без сомнения полностью описать в модуле, связанном с формой. Но мы поступили по-иному и создали стандартный модуль с именем ToolsMod, в который и вынесли содержательную часть обработки. Так что у нас появилось два модуля, - в одном находятся обработчики события, в другом - стандартном сосредоточен содержательный код, выполняющий обработку. Вот код модуля TwoLists, связанного с нашей формой, и содержащий обработчики событий:
Option Explicit
Private Sub CommandButton1_Click()
'Обработчик события Click кнопки "> <"
'Выборочный обмен данными между n- колоночными списками:
'ListBox1 <--> ListBox2
If CommandButton1.Caption = ">" Then
Call MoveSelectedItems(ListBox1.ColumnCount, ListBox1, ListBox2)
Else
Call MoveSelectedItems(ListBox2.ColumnCount, ListBox2, ListBox1)
End If
End Sub
Private Sub CommandButton2_Click()
'Обработчик события Click кнопки ">> <<"
'Перенос всех данных из одного n-колоночного списка
'в конец другого, возможно, не пустого списка: ListBox1 <--> ListBox2
If CommandButton2.Caption = ">>" Then
Call MoveAllItems(ListBox1.ColumnCount, ListBox1, ListBox2)
Else
Call MoveAllItems(ListBox2.ColumnCount, ListBox2, ListBox1)
End If
End Sub
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
'Обработчик события DblClick левого списка ListBox1 (имеет параметры!)
'При двойном щелчке выбранный элемент одного n-колоночного списка
'переносится в конец другого списка
Call MoveSelectedItems(ListBox1.ColumnCount, ListBox1, ListBox2)
End Sub
Private Sub ListBox2_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
'Обработчик события DblClick правого списка ListBox2
'При двойном щелчке выбранный элемент одного n-колоночного списка
'переносится в конец другого списка
Call MoveSelectedItems(ListBox2.ColumnCount, ListBox2, ListBox1)
End Sub
Private Sub CommandButton5_Click()
'Обработчик события Click кнопки "OK"
'Перенос данных из списка на лист Excel
'В область, заданную ячейкой с именем "Dom"
Call MoveListToRange(ListBox2.ColumnCount, ListBox2, "Dom")
Me.Hide
End Sub
Private Sub CommandButton6_Click()
'Обработчик события Click кнопки "Cancel"
Me.Hide
End Sub
Private Sub ListBox1_Enter()
'Обработчик события Enter, возникающего при получении фокуса
CommandButton1.Caption = ">"
CommandButton2.Caption = ">>"
End Sub
Private Sub ListBox2_Enter()
'Обработчик события Enter, возникающего при получении фокуса
CommandButton1.Caption = "<"
CommandButton2.Caption = "<<"
End Sub
Private Sub UserForm_Initialize()
'Обработчик события Initialize формы TwoListsForm
'Заполнение списка ListBox1
Dim MyArray(1 To 5, 1 To 2) As String
MyArray(1, 1) = "Петров" : MyArray(1, 2) = "Музыкант"
MyArray(2, 1) = "Сергеев" : MyArray(2, 2) = "Учитель"
MyArray(3, 1) = "Гурина" : MyArray(3, 2) = "Актриса"
MyArray(4, 1) = "Водкин" : MyArray(4, 2) = "Художник"
MyArray(5, 1) = "Козина" : MyArray(5, 2) = "Геолог"
ListBox1.ColumnCount = 2 : ListBox2.ColumnCount = 2
ListBox1.List() = MyArray
End Sub
Модуль TwoListsForm, связанный с формой, содержит 9 обработчиков событий, возникающих при работе с самой формой - ее инициализации, так и при работе пользователя с объектами, населяющими форму. Каждый из обработчиков выполняет свои специфические задачи. Когда пользователь переключается на работу с элементами левого или правого списка, то в тот момент, когда список получает фокус, возникает событие Enter. Обработчик события Enter изменяет заголовок у соответствующих командных кнопок, подготавливая, тем самым, передачу данных в нужном направлении. Такое автоматическое изменение направления передачи позволяет уберечь пользователя от ошибочных действий. Но, обратите внимание на главное в этом примере. Для удобства пользователя ему предоставлены три разных способа передачи данных из одного списка в другой. Поскольку обмен данными может вестись в двух направлениях, то при лобовом программировании нужно было бы написать 6 различных макросов. Мы же свели задачу к двум процедурам, вызываемых с разными значениями параметров. Тексты этих процедур мы поместили в стандартный модуль с именем ToolsMod, предполагая возможность его дальнейшего использования. Вот эти тексты:
Option Explicit
Public Sub MoveSelectedItems(ByVal n As Byte, ByVal ListBox1 As Object, _
ByVal ListBox2 As Object)
'Перемещает выделенные элементы первого списка в конец второго
'с одновременным удалением данных из первого списка.
'Оба списка имеют n столбцов.
Dim RowIndex1 As Byte, RowIndex2 As Byte, i As Byte, j As Byte
'Выборочный обмен данными между списками: ListBox1 -> ListBox2
With ListBox1
RowIndex2 = ListBox2.ListCount
RowIndex1 = 0
For i = 0 To .ListCount - 1
If .Selected(RowIndex1) Then
'Создается элемент нового списка и заполняется его первый столбец
ListBox2.AddItem .List(RowIndex1)
'Заполняются остальные столбцы элемента списка
For j = 1 To n - 1
ListBox2.Column(j, RowIndex2) = .Column(j, RowIndex1)
Next j
'Перемещенный элемент удаляется из списка
.RemoveItem RowIndex1
RowIndex2 = RowIndex2 + 1
Else
RowIndex1 = RowIndex1 + 1
End If
Next i
End With
End Sub
Public Sub MoveAllItems(ByVal n As Byte, ByVal ListBox1 As Object, _
ByVal ListBox2 As Object)
' Перемещает все элементы первого списка в конец второго,
' возможно, не пустого списка с одновременным удалением данных из
' первого списка. ListBox1 -> ListBox2
Dim RowIndex1 As Integer, RowIndex2 As Integer, i As Byte
RowIndex2 = ListBox2.ListCount
For RowIndex1 = 0 To ListBox1.ListCount - 1
With ListBox1
ListBox2.AddItem .List(0)
For i = 1 To n - 1
ListBox2.Column(i, RowIndex2) = .Column(i, 0)
Next i
RowIndex2 = RowIndex2 + 1
'Перемещенный,- это всегда первый элемент,удаляется из списка
.RemoveItem 0
End With
Next RowIndex1
End Sub
Public Sub MoveListToRange(ByVal n As Byte, List1 As Object, Dom As String)
'List1 - объект типа ListBox, состоящий из n столбцов.
' Его элементы переносятся в прямоугольную область активного листа,
' Dom - задает имя ячейки, расположенной в левом верхнем углу этой
' области.
Dim myr As Range
Dim i As Byte, j As Byte
Set myr = Range(Dom)
'Цикл по числу элементов списка.
For i = 0 To List1.ListCount - 1
'Цикл по числу столбцов списка.
For j = 0 To n - 1
myr.Offset(j, i) = List1.Column(j, i)
Next j
Next i
End Sub
Public Sub ClearRange(Dom As String)
'Эта процедура очищает содержимое области листа рабочей книги,
'заданной ячейкой с именем Dom
Dim myr As Range, Row As Byte, Col As Byte
Set myr = Range(Dom)
Col = 0: Row = 0
While myr.Offset(Row, Col) <> ""
While myr.Offset(Row, Col) <> ""
'Чистка содержимого
myr.Offset(Row, Col).ClearContents
Col = Col + 1
Wend
Row = Row + 1
Col = 0
Wend
End Sub
Две последние процедуры не связаны напрямую с рассматриваемой нами задачей перемещения данных между списками. Тем не менее, мы решили их привести, поскольку они являются частью нашего тестового примера. С другой стороны они также решают общие задачи, возникающие при работе с документами Excel и потому, по праву, помещены в стандартный модуль.
Завершая разговор о переиспользовании стандартных модулей, предлагаем взглянуть на форму, взятую из совсем другого документа - документа Word.
(рис 2.4) Форма TwoLists, взятая из документа WordХотя форма и похожа на форму, связанную с MoveSelectedItems и MoveAllItems. Конечно, думая о возможности многократного использования этих процедур, мы заранее побеспокоились о возможности работы с произвольным числом столбцов в списках.
Создавая стандартные модули, предполагайте возможность их многократного использования.
Замечание:
В заключение, хотим обратить Ваше внимание на одну важную программистскую деталь. Вот как выглядит заголовок одной из наших процедур:
Public Sub MoveAllItems(ByVal n As Byte, ByVal ListBox1 As Object, _
ByVal ListBox2 As Object)
Вы видите, что тип формальных параметров для ListBox1 и ListBox2 задан как Object и, следовательно, является ListBox и при этом передача параметров производится корректно. В приложении Excel при написании процедуры также можно указать тип ListBox, но возникнет ошибка в момент передачи параметров.
В процессе тестирования Office 2000 мы указали разработчикам на это рассогласование, оно должно быть учтено в последующих версиях. Будем надеяться, что в будущем мы сможем в процедурах при объявлении формальных параметров, являющихся объектами, указывать их настоящий тип.
Напомним, проект документа мы рассматриваем, как совокупность модулей разного типа. Компонентами каждого модуля являются переменные уровня модуля, описанные в разделе объявления модуля, и методы модуля.
Первое правило видимости гласит:
"Все компоненты модуля видимы в пределах этого модуля".
Это означает, что любая переменная из раздела объявлений модуля является глобальной по отношению к методам модуля, она доступна в каждом из методов, если только внутри метода нет объявления локальной переменной с тем же именем. Общедоступность глобальных переменных облегчает передачу информации между методами модуля. Вместе с тем общедоступность и плохое, опасное качество. Каждый метод может изменить глобальную переменную. Поэтому глобальных переменных не должно быть много. За судьбой каждой из них надо внимательно следить.
Каждый метод модуля также общедоступен внутри модуля и может быть вызван другими методами модуля. Понятно, это не касается обработчиков событий. Последние вызываются системой и не могут быть вызваны никакими другими методами модуля.
Второе правило видимости гласит:
"Область видимости компонент модуля расширяется на весь проект, если компонент объявлен со спецификатором
Public".
Каждый компонент модуля - переменная или метод может быть снабжен спецификатором области видимости, который имеет два возможных значения - Public и Private. Если задан спецификатор Public, то это означает, что компонент общедоступен в пределах всего проекта. Спецификатор Private делает компонент закрытым для других модулей проекта. Он видим только в своем родном модуле.
Если при объявлении переменных модуля спецификатор области видимости опущен и указано только ключевое слово Dim, то такие переменные считаются закрытыми, - действует спецификатор Private. Для методов спецификатор области видимости можно опускать. В этом случае действует следующее правило. Все методы стандартных модулей имеют по умолчанию спецификатор Public и являются доступными во всем проекте. Методы модулей - классов и модулей, связанных с объектами, по умолчанию являются закрытыми и имеют статус Private.
Есть еще одно важное правило, касающееся общедоступных компонент. Спецификатор Public еще не гарантирует, что имя компонента будет видимо вне модуля. Чтобы компонент был видимым вне модуля, следует использовать его полное имя, которое строится по обычным правилам построения сложных имен. Оно состоит из имен, разделенных точкой, - имени компонента, имени модуля и, возможно, имени проекта. Имя проекта может потребоваться в тех случаях, когда речь идет о системе взаимосвязанных документов. Внутри одного проекта его имя можно опускать, но, заметьте, нельзя опускать имя модуля для Public переменных модулей, связанных с объектами. Для них допустимы только полные имена. Вообще разумно не иметь Public переменных для таких модулей.
Для компонент, принадлежащих стандартным модулям, внутри одного проекта разрешается опускать имя модуля. Как правило, так и поступают, но, иногда, это может привести к коллизиям, если в нескольких стандартных модулях есть компоненты с одинаковым именем. Public - компоненты модулей классов используются так, как это принято в объектно-ориентированном программировании. Далее мы об этом поговорим подробнее.
До сих пор мы говорили о проекте одного документа. Давайте немного расширим рамки и поговорим о совокупности или системе документов, о том, как связаны между собой проекты такой системы.
Оставим пока в стороне создание и использование AddIns, Com AddIns, DLL, ActiveX, Internet документов. Им будет посвящен особый разговор. Пока поговорим о более понятных случаях использования совокупности документов. Прежде всего, заметим, что корневой объект Application любого из приложения Office 2000 содержит коллекцию документов. Так что изначально предполагается возможность открытия в одном приложении нескольких документов и совместной работы с ними. Но совместная работа не ограничивается однотипными документами, принадлежащими одному приложению. Уже в первой лекции мы подробно, с примерами рассмотрели возможность создания и одновременной работы с несколькими объектами Application. Следовательно, всегда есть возможность открытия совокупности документов разного типа и совместной работы с ними, предоставляя пользователю возможность переключаться по ходу дела от одного документа к другому.
Совместная работа с несколькими документами, возможно, разного типа типична для Office 2000. Программным проектом системы документов будем называть совокупность программных проектов всех документов, входящих в систему.
Остается ответить на главный вопрос, - могут ли взаимодействовать между собой проекты отдельных документов. Можно ли из одного проекта вызывать процедуры стандартного модуля другого проекта, можно ли пользоваться объектами класса другого проекта? Можно ли иметь глобальные переменные уровня системы документов для передачи информации от одного документа к другому? На все эти вопросы существует один ответ, - Да! С программным проектом системы документов в Office 2000 достаточно просто работать, достаточно просто организовать нужные связи между программными проектами отдельных документов, нужно только уметь это делать.
Наше определение системы документов достаточно общее, - совокупность открытых документов составляет систему. О связях между документами ничего не говорится. Пользователь сам может решать, с каким документом он будет работать в следующий момент, и волен свободно переходить от одного документа к другому. Но документы в системе могут быть связаны. Рассмотрим связи двух видов:
Два документа A и B связаны по вызову, если в одном из документов, например A, вызывается документ B. Документ A называют родителем, а документ B, соответственно, потомком. Совокупность связанных по вызову документов образует дерево связанных документов. Корень дерева - это тот первичный документ, из которого и начинаются вызовы других документов. Связи в дереве направлены от корня к потомкам.
Представив совокупность связанных по вызову документов в виде дерева, мы сознательно упростили картину, фактически, она гораздо сложнее, поскольку допустимы циклы. Вызов документа означает его открытие, если он не был еще открыт и его активизацию. Вполне допустимо, что несколько открытых документов в произвольном порядке и неоднократно становятся активными. Тем не менее, зачастую, особенно при проектировании полезно рассматривать древесную структуру организации связей по вызову.
Проекты A и B связаны по ссылке, если один из проектов, например B, ссылается на проект A. Проект A называют родителем, а проект B, соответственно, потомком. Совокупность связанных по ссылке проектов образует дерево связанных проектов. Корень дерева - это тот первичный проект, на который ссылаются остальные проекты. Связи в дереве ссылок направлены от потомков к корню. Вообще говоря, проекты, связанные по ссылке, также могли бы организовывать более сложную структуру, чем дерево. Действительно, взаимные ссылки кажутся допустимыми. Но это не так. Помните, циклические ссылки проектов VBA документов не разрешены. Так что при разработке проекта системы документов Office 2000 исходите из древесной структуры организации связей между проектами. На рисунке 2.5 показаны три возможные структуры организации системы документов Office 2000.
(рис 2.5) Три структуры организации системы документовРассмотрим теперь технический вопрос, как организовать ссылку из проекта B на проект A. Для этого нужно сделать две вещи, во-первых, дать собственные имена проектам, во-вторых, - включить ссылку в проекте B на проект A. Имя нужно дать, по крайней мере, родительскому проекту A. О том, как это делается, мы уже рассказали в разделе "Свойства проекта" этой лекции. На рис. 2.2 показано соответствующее диалоговое окно.
Задание ссылки выполняется также стандартным образом. В окне Редактора Visual Basic нужно выбрать соответствующий проект, в нашем примере проект B, и из меню Tools выбрать команду References. Если документ, содержащий проект A открыт, то в окне ссылок наряду с другими возможными для ссылок компонентами появится флажок с именем проекта A, останется только его включить.
Когда речь идет о системе документов, то один из основных вопросов - организация обмена информацией между отдельными документами системы. Когда речь идет о системе проектов, то, как правило, существует сравнительно небольшая по объему общая информация, которая используется во всех проектах. Сейчас мы и поговорим о том, как организовать такой общий информационный пул, содержащий глобальную информацию уровня системы проектов. Задача состоит в том, чтобы хранимые данные могли быть доступны во всех проектах для чтения и записи. Решить задачу можно по-разному, перечислим основные способы:
Public переменных, в совокупности и определяющих общую для всей системы информацию. Систему документов спроектируем так, чтобы их проекты были связаны по ссылке, и корнем дерева был основной документ, хранящий глобальную информацию. В этом случае каждый проект будет иметь доступ к этой информации. Напомним, циклические ссылки между проектами не разрешены, отсюда следует, что нельзя часть информации поместить в проект А, часть в проект В и сделать эту информацию общедоступной для обоих проектов. Возможна только древесная организация хранения общей информации. Самая общая информация хранится в корневом проекте, в "дочерних" проектах хранится информация необходимая потомкам этих дочерей, и так до терминальных проектов.Заканчивая обсуждение этого вопроса, еще раз подчеркнем, что способ передачи информации между проектами, основанный на общем информационном пуле, как и во всех случаях работы с глобальными переменными, является опасным. Поэтому объем этой информации не должен быть большим и легко контролируемым, так что по этим соображениям первые два способа являются наиболее предпочтительными, а программисты, скорее всего, выберут первый из них. В заключение, напомним, что передача информации между проектами через общую область важный, но не единственный способ организации взаимодействия. Основным и надежным способом передачи данных для программистов является вызов Public методов проекта, - передача им аргументов и получение результатов, то, что называется аппаратом формальных - фактических параметров.
Перейдем теперь к примерам организации взаимодействия системы документов. Один из примеров на эту тему мы уже приводили в предыдущей лекции. Возможно, стоит еще раз обратиться к нему и разобрать, как из документа Word происходит переключение на работу с
(рис 2.6) Открытие рабочей книги, содержащей ссылкиПоскольку в данной рабочей книге есть ссылки на другие рабочие книги, то при открытии документа появляется сообщение о том, что, возможно, следует обновить документ, поскольку после его закрытия могли измениться документы, на которые он ссылается. На листе ChooseBook ". Эту кнопку пользователь должен нажать в тот момент, когда он стоит на распутье, и должен выбрать, с какой следующей книгой он хочет работать. Вот текст обработчика события " Click ":
Private Sub CommandButton5_Click()
ChooseBook
End Sub
Public Sub ChooseBook()
'Выбор книги, с которой будет работать пользователь
Const PathDir As String = "e:\O2000\CD2000\Ch2\"
Dim Answer As Variant
Dim CurrentBook As String
Dim Book As Workbook
Dim Found As Boolean
Answer = InputBox(prompt:= _
"Выберите книгу, с которой будете работать (1/2/3)", Default:=1)
Select Case Answer
Case 1
CurrentBook = "BookOne.xls"
Case 2
CurrentBook = "BookTwo.xls"
Case 3
CurrentBook = "BookThree.xls"
Case Else
CurrentBook = "I don't know such book"
End Select
MsgBox ("Your choose is " CurrentBook)
'Проверяем есть ли уже книга в коллекции
Found = False
For Each Book In Workbooks
If Book.Name = CurrentBook Then
Found = True
Exit For
End If
Next Book
If Found Then
'Активизируем выбранную книгу
Workbooks(CurrentBook).Activate
ElseIf CurrentBook = "I don't know such book" Then
MsgBox ("I don't know such book")
Else 'Добавляем книгу в коллекцию
Workbooks.Open PathDir CurrentBook
Workbooks(CurrentBook).Activate
End If
End Sub
Пользователю предоставляется право выбора одной из трех
Public Sub PrintGlobal()
Dim MyrOne As Range, MyrTwo As Range, MyrThree As Range
Debug.Print ("Работает процедура PrintOne")
Debug.Print ("Печать глобальных переменных")
Debug.Print (One "-" Two "-" Three)
Debug.Print ("Допустимы ссылки на ячейки различных рабочих книг!")
Set MyrOne = Range("[BookOne.xls]Sheet1!D1")
Debug.Print MyrOne
Set MyrTwo = Range("[BookTwo.xls]Sheet1!D1")
Debug.Print MyrTwo
Set MyrThree = Range("[BookThree.xls]Sheet1!D1")
Debug.Print MyrThree
MsgBox ("Everything is OK! Look at Immediate Window")
End Sub
Вот результаты отладочной печати в конце работы этой процедуры:
Работает процедура PrintOne Печать глобальных переменных My value is One -My value is One -My value is One Допустимы ссылки на ячейки различных рабочих книг! BookOne BookTwo BookThree
Всем глобальным переменным предварительно было присвоено одно и то же значение. Книги BookOne, BookTwo, BookThree.xls на первой странице в ячейке D1 хранят свои названия, их и печатает наша процедура, вызываемая в проекте одной из этих книг.
Рассмотрим теперь систему документов из тех же трех книг BookOne, BookTwo, BookThree.xls, проекты которых связаны ссылками. Наша основная цель, - продемонстрировать на примере работу трех проектов с общей информацией и общими методами, вызываемыми во всех проектах.
Вначале несколько слов об интерфейсе нашей системы документов. Мы сделали его достаточно простым. На первом рабочем листе каждой из трех книг расположены три командные кнопки: ChooseBook, ChangeGlobal, PrintGlobal.
(рис 2.7) Командные кнопки управления системой документовКогда пользователь щелкает первую из этих кнопок, соответствующий обработчик, открывает диалоговое окно, позволяет выбрать нужную книгу и активизирует ее. Вторая из кнопок отвечает за работу с общей информацией. В каждом из документов общая информация изменяется соответствующим образом. Обработчик третьей кнопки демонстрирует работу с общими методами. Перейдем теперь к деталям организации совместной работы трех документов и их проектов. Мы начали с того, что проектам дали имена, совпадающие с именами книг. Проект книги BookOne является в нашей системе основным, - он хранит глобальную информацию, общие методы и на него ссылаются остальные проекты системы. Глобальную информацию будем представлять тремя общими переменными, расположив их в стандартном модуле ModuleOne проекта BookOne:
Option Explicit 'Глобальные переменные системы документов One - Two - Three Public One As String, Two As String, Three As String
Три обработчика события Click для трех командных кнопок вызывают три общих метода ChooseBook, ChangeGlobal, PrintGlobal:
Private Sub CommandButton5_Click()
ChooseBook
End Sub
Private Sub CommandButton4_Click()
ChangeGlobal
End Sub
Private Sub CommandButton3_Click()
PrintGlobal
End Sub
Назначение процедуры ChooseBook и ее текст уже приведены. Заметим только, что эта общая процедура вызывается соответствующими обработчиками во всех трех модулях. Мы поместили ее не в стандартный модуль проекта BookOne, а в модуль, связанный с листом Sheet1 книги BookOne. Это, однако, ничуть не мешает ее вызову из других проектов. Процедура ChangeGlobal, помещена в стандартный модуль с именем ModuleOne. Она позволяет работать с глобальными переменными, и поскольку в каждом проекте работа с ними выполняется по-своему, то она не является общей процедурой. В каждом проекте есть свой вариант этой процедуры, который и вызывается обработчиком Click командной кнопки ChangeGlobal. Заметим, что когда речь идет об основном проекте, где описаны глобальные переменные, при обращении к ним не требуется использовать полные имена:
Public Sub ChangeGlobal()
One = "My value is One "
Two = "My value is One "
Three = "My value is One "
End Sub
В стандартный модуль ModuleOne мы поместили и процедуру PrintGlobal. Текст ее тоже уже приведен. Заметим, что в каждом проекте будет свой вариант этой процедуры, но в каждом из них на последнем этапе работы будет вызываться процедура PrintGlobal проекта BookOne. В стандартном модуле находится функция Plus1, которая будет вызываться из других проектов. Это очень простая функция, но она выполняет свою важную роль, демонстрируя передачу информации от проекта к проекту через аппарат формальных - фактических параметров процедур и функций. Вот ее текст:
Public Function Plus1(ByVal X As Integer) As Integer
Plus1 = X + 1
End Function
Проект BookTwo ссылается на проект BookOne, использует его общие переменные и вызывает его методы - ChooseBook, PrintGlobal, Plus1. Он является хранителем общей информации для проектов BookTwo и BookThree:
Option Explicit 'Глобальная переменная документов BookTwo, BookThree Public TwoThree As String
Поскольку все три документа устроены одинаково, то достаточно привести тексты одного обработчика и двух процедур, задающие специфику работы в проекте этого документа:
Private Sub CommandButton1_Click()
BookOne.Sheet1.ChooseBook
End Sub
Public Sub ChangeGlobal()
'Изменение значений глобальных переменных системы документов
BookOne.ModuleOne.Two = "My value is Two "
TwoThree = "My value is Two - Two "
End Sub
Public Sub PrintGlobal()
Dim Number As Integer
Debug.Print ("Работает процедура PrintTwo")
Debug.Print ("Печать глобальных переменных")
Debug.Print TwoThree
'Вызов процедур и функций проекта BookOne
Number = BookOne.ModuleOne.PLus1(1)
Debug.Print ("Это книга " Number)
ChangeGlobal
BookOne.ModuleOne.PrintGlobal
End Sub
Полагаю, что эти тексты достаточно красноречивы и не нуждаются в дальнейших пояснениях. Несколько слов теперь о третьем документе системы. Он введен только для того, чтобы показать, что при росте системы новых трудностей не возникает. Вот его тексты:
Private Sub CommandButton1_Click()
BookOne.Sheet1.ChooseBook
End Sub
Public Sub ChangeGlobal()
'Изменение значений глобальных переменных системы
BookOne.ModuleOne.Three = "My value is Three "
BookTwo.ModuleTwo.TwoThree = "My value: Two-Three"
End Sub
Public Sub PrintGlobal()
Dim Number As Integer
Debug.Print ("Работает процедура PrintThree")
Number = BookOne.ModuleOne.Plus1(2)
Debug.Print ("Это книга " Number)
ChangeGlobal
BookTwo.ModuleTwo.PrintGlobal
End Sub
Приведем результаты отладочной печати, полученные при нажатии кнопки PrintGlobal в книге BookThree:
Работает процедура PrintThree Это книга 3 Работает процедура PrintTwo Печать глобальных переменных My value: Two-Three Это книга 2 Работает процедура PrintOne Печать глобальных переменных My value is One -My value is Two -My value is Three Допустимы ссылки на ячейки различных рабочих книг! BookOne BookTwo BookThree
В заключение приведем вид окна проектов, где отображены все проекты нашей системы.
(рис 2.8) Три проекта One - Two - Three
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.