Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© 2000, Андрей Колесов, Ольга ПавловаКак известно, пользователь MS Office 97/2000 может выполнять различные операции по настройке меню и панелей инструментов среды — модифицировать, удалять и добавлять команды и панели. Это можно делать как с помощью команды Настройка (Customize), так и программным образом (VBA). Но где хранятся эти настройки, как они связаны с обрабатываемыми документами? Это полезно знать, особенно если вы хотите перенести эти настройки на другой компьютер. Тем более что реализация механизма сохранения настроек для всех офисных приложений различная (это к вопросу о том, что MS Office представляет собой как бы единое семейство продуктов).
Word
Это приложение обладает наиболее удобной и гибкой системой настроек. Как известно, VBA-проекты хранятся в документах или шаблонах Word. В них же хранятся и настройки панелей инструментов, и пользователь может сам выбирать, в каком именно файле они будут записаны.
Таким образом можно реализовать самые разнообразные варианты подключения расширений пользовательской среды. Если записать эти настройки в глобальный шаблон (например, в Normal.dot), то данные команды будут доступны при работе со всеми документами в данной сессии. При сохранении установок в конкретный документ они будут применяться только в нем. Если же настройки включены в присоединенный шаблон, они будут появляться во всех документах, которые его используют.
Excel и PowerPoint
В этих двух приложениях макрокод хранится в документах (рабочих книгах и презентациях), а сами настройки панелей инструментов — в фиксированных файлах. Для версии 97 это соответственно файлы <username>.xlb и <username>.pcb в каталоге Windows, для версии 2000 — файлы Excel\Excel.xlb и PowerPoint\ppt.pcb, которые находятся в каталоге Windows\Application Data\Microsoft\. Таким образом, получается, что пользователь может работать только с одним фиксированным вариантом настроек панелей инструментов.
Но в этих приложения механизм работы команд несколько различается. Для примера напишите такую макрокоманду:
Sub Hello MsgBox "Hello!" End Sub
которую запишите в книгу Hello.xls и презентацию Hello.ppt.
Соответственно в Excel и PowerPoint сделайте кнопку на панели инструментов для обращения к этим макрокомандам. Затем перезагрузите Excel: на панели инструментов будет видна ваша кнопка, хотя программный код для нее не загружен. Нажмите кнопку — произойдет загрузка Hello.xls и выполнится макрокоманда. Теперь повторите те же действия для PowerPoint — макрокоманда выполнится, но презентация не загрузится в среду приложения.
В Excel можно скопировать созданные пользователем панели инструментов в рабочую книгу (со встроенными это проделать нельзя!). Это делается с помощью диалогового окна Attach Toolbars (вызывается командой Attach на вкладке ToolBars окна Customize) — программным образом с применением VBA это сделать нельзя.
После копирования можно удалить пользовательские панели из среды самого Excel с помощью команды Delete окна Customize или метода Delete коллекции CommandBars. Удалить панель из рабочей книги можно только с помощью диалогового окна.
Важное замечание. Когда вы открываете рабочую книгу, производится автоматическое копирование пользовательских панелей из книги в среду Excel (но только в том случае, если панелей с такими именами не было в самом приложении!). При этом следует иметь в виду, что при закрытии рабочей книги эти панели остаются в Excel. Поэтому, если вы хотите, чтобы подобные компоненты появлялись только в сеансе работы с данной рабочей книгой, вы можете программным образом сделать удаление панели при закрытии книги. Если же вы не удалите пользовательскую панель из Excel и будете корректировать ее, то в последующих сеансах вы будете работать именно с ней (копирование из рабочей книги не будет выполняться).
Outlook и FrontPage
В этих двух приложениях все сделано еще проще: программный код хранится в строго фиксированных проектах — Outlook\VbaProject.OTM и FrontPage\Macros\Microsoft FrontPage.fpm, а настройки среды — в столь же фиксированных Outlook\outcmd.dat и FrontPage\State\cmpUI.prt. Все это, в свою очередь, находится в подкаталоге Windows\Application Data\Microsoft\.
Примечание. Для приложений Excel, PowerPoint, Outlook и FrontPage каталог Windows\Application Data\Microsoft\ используется только в случае однопользовательского режима. Если же в операционной системе установлен многопользовательский режим с использованием различных профилей, то в этом случае используется каталог Windows\Profiles\Username\Application Data\Microsoft\.
Access
В документах Microsoft пользовательские настройки встроенных панелей инструментов среды Access хранятся в "специальной базе данных". Наши исследования показали, что эта база — Реестр Windows, хранящийся по адресу: HKCU\Software\Microsoft\Office\9.0\Access\Settings\Commandbars\. Но что и как туда пишется — нам разобраться не удалось. Панели же инструментов, создаваемые с помощью Access, хранятся в файлах этой базы данных.
Как известно, перемещение фокуса от одного элемента управления к другому можно выполнять с помощью клавиатуры: Tab (вперед) или Shift+Tab (назад). Порядок передвижения определяется значением свойства TabIndex элементов управления, который начинается с нуля и для каждого размещаемого на форме элемента управления увеличивается на единицу. Свойство TabIndex, конечно же, имеется только у видимых компонентов (у Timer его нет). Пассивные визуальные компоненты типа Label имеют свойство TabIndex, но фактически не используют его — при перемещении по форме такие элементы автоматически пропускаются.
Для изменения последовательности обхода визуальных компонентов (она редко совпадает с тем порядком, в котором мы создавали диалоговый интерфейс) нужно менять свойства TabIndex. Это можно делать вручную, напрямую корректируя параметры в окне Properties. Обратите внимание, что ошибка использования одного и того же значения TabOrder для разных компонентов здесь исключена за счет автоматической перенумерации.
Пусть, например, у вас имеются четыре компонента с номерами 0, 1, 2, 3. Для последнего вы решили установить TabOrder=1. В этом случае автоматически изменятся значения свойств TabOrder для двух элементов управления: 1->2, 2->3.
Однако гораздо удобнее использовать специальные визуальные средства управления перемещения по элементам формы. Как мы отмечали в совете 301, конструктор MS Forms 2.0 (в среде VB и VBA) для этого использует специальную встроенную команду TabOrder. Чтобы воспользоваться ею, щелкните правой кнопкой мыши форму и в появившемся контекстом меню выберите команду Tab Order. В появившемся одноименном диалоговом окне выделите нужные компоненты и с помощью кнопок Move Up и Move Down передвиньте их в нужную позицию списка, который определяет порядок перемещения (рис. 306-1).
Рис. 306-1
Для обычных VB-форм такой встроенной команды нет, и по этому поводу мы уже выказывали свое недовольство в адрес разработчиков из Microsoft. Однако мы были не правы — такие визуальные средства имеются в VB 5 и VB 6 в виде автономного дополнения TabOrder (исходный код этого проекта находится в подкаталоге ...\MSDN98\98VS\1033\Samples\VB98\TabOrder\ для VB6 или в ...\Samples\CompTool\AddIns\TabOrder\).
Чтобы сделать Add-In (на примере VB 6.0), загрузите проект TabOrder.VBP и выполните команду File|Make TabOrder.DLL. В результате вы не только создадите исполняемый модуль, но и зарегистрируете новое дополнение, которое появится под названием Tab Order Sample Addin в окне Add-Ins|Add-In Manager среды Visual Basic. Для работы с TabOrder необходимо обычным образом установить режим загрузки в этом окне, и тогда на панели инструментов VB появится кнопка TabOrder Window для запуска этого дополнения (рис. 306-2).
Рис. 306-2
Примечание. При работе в VB 5 для включения TabOrder (созданного в этой же среде!) в окно Add-In Manager необходимо добавить в секцию [Add-Ins32] файла VBADDIN.INI строку TabOrder.Connect=0.
Теперь создайте в VB какой-нибудь тестовый пример с формой, на которой разместите несколько разных элементов управления, и щелкните кнопку TabOrder Window. В появившемся окне (рис. 306-3) можно корректировать последовательность перемещения по элементам управления формы.
Рис. 306-3
Однако здесь имеются дополнительные функции, которых нет в аналогичной утилите MS Forms 2.0. Обратите внимание, что на нашей тестовой форме все элементы управления расположены на разных расстояниях от верхней и правой границ формы. С помощью соответствующих кнопок на панели инструментов можно упорядочить список по вертикальному (рис. 306-4) или горизонтальному (рис. 306-5) расположению визуальных компонентов. Кроме того, программист без перезагрузки этой утилиты может восстановить исходную последовательность списка.
Рис. 306-4
Рис. 306-5
Вообще, мы рекомендуем внимательно изучить проект TabOrder, так как он представляет собой интересный пример создания Add-In, использования User-документов и ресурсных файлов. Но один вопрос все же остается — зачем нужно было делать для VB-форм и MS Forms 2.0 две похожие, но тем не менее разные утилиты?
VB содержит довольно много встроенных функций и операторов для управления файлами и каталогов. Совершенно очевидно, что эти средства работают через Win API, и понятно, что последние обладают более широкими возможностями. Чаще всего встроенные функции VB реализуют усеченный вариант соответствующей API-функции (например, оператор MkDir представляет собой частный случай функции CreatDirectory). Многие же возможности API вообще не представлены в виде VB-операторов. В частности, это относится к операции удаления каталогов, которая может быть выполнена таким образом:
Private Declare Function RemoveDirectory& Lib "kernel32" Alias _ "RemoveDirectoryA" (ByVal lpPathName As String) ' Удаление каталога (пустого!) PathName$ = "D:\t\" ' в конце имени "\" необязательна ' code& = RemoveDirectory(PathName) If code& = 0 Then ' операция удаления не была выполнена Else ' каталог удален End If
Здесь нужно обратить внимание на несколько моментов:
Но, как удалить каталог, если в нем находятся файлы и вложенные подкаталоги? Для этого потребуется написать процедуру DeleteDir, которая будет перед удалением каталога просматривать и удалять его содержимое. Обращение к ней может выглядеть следующим образом:
Dim DirCount%, FileCount% ' число удаленных каталогов и файлов DirName$ = "d:\t\" ' удаляемый каталог DirCount = 0: FileCount = 0 ' начальная установка Result& = DeleteDir&(DirName$, DirCount%, FileCount%) ' Result = 0 - удален
А сама процедура будет содержать такой код:
Public Function DeleteDir&(DirName$, DirCount%, FileCount%) ' ' Удаление каталога со всем его внутренним содержанием ' ВХОД: ' DirName$ — имя удаляемого каталога ' ВЫХОД: ' DeleteDir - код завершения (0 - удален, <>0 - не удален ' DirCount, FileCount — текущее число удаленных каталогов и файлов ' (перед первым обращении эти переменные должны быть обнулены) ' Dim Result& ' ' 1. Удалить все внутри каталога Call DeleteDirAll(DirName$, DirCount%, FileCount%, Result&) If Result = 0 Then ' без ошибок ' 2. удалить сам каталог Result = RemoveDirectory(DirName$) If Result = 0 Then Result = -1 Else Result = 0: DirCount = DirCount + 1 End If End If DeleteDir = Result ' код завершения End Function
Но очевидно, что ключевой задачей здесь является написание процедуры DeleteDirAll, которая будет удалять внутреннее содержание каталога — файлы и подкаталоги. Тут не обойтись без использования рекурсии, и поэтому полезно вспомнить наши советы 229-231. Более того, нам понадобится готовый код подпрограммы CurrentDirCounter ("как есть"), а также продпрограмма FileNameCase, которую можно использовать в качестве заготовки и создать DeleteDirAll:
Public Sub DeleteDirAll(DirName$, DirCount%, FileCount%, Result&) ' ' Удаление всех файлов и подкаталогов заданного каталога ' ReDim arrFile$(100) ' для имен элементов оглавления Dim CountFile%, NewPathName$, i% ' On Error GoTo ErrorDeleteFile ' формируем список файлов каталога Call CurrentDirCounter(DirName$, "*.*", CountFile%, arrFile$(), 0) ' удаление файлов If CountFile > 0 Then ' есть файлы в подкаталоге For i = 1 To CountFile Kill DirName$ & arrFile$(i) ' удалить файл FileCount = FileCount + 1 ' счетчик удаленных файлов Next End If 'формирование списка подкаталогов Call CurrentDirCounter(DirName$, "", CountFile%, arrFile$(), vbDirectory) If CountFile > 0 Then ' есть подкаталоги For i = 1 To CountFile ' формирование имени вложенного подкаталога NewPathName$ = DirName$ + arrFile$(i) + "\" '!! рекурсивное обращение к функции удаления каталога ' с новым именем, но старыми значениями счетчиков Result = DeleteDir&(NewPathName$, DirCount%, FileCount%) If Result <> 0 Then Exit Sub ' была ошибка Next End If Exit Sub ErrorDeleteFile: Result = Err ' ошибка при удалении файла End Sub