Разбираем по кусочкам новый «ленточный» интерфейс Word 2007. Здесь нет и не будет советов о том, как его сделать похожим на Word 2003. Только усовершенствования уже существующего. Также приглашаю посетить мой блог, посвященный работе с макросами в Word.

Напоминаю, что все коды программ, приведенных в блоге, вы используете на свой страх и риск. Не забывайте создавать резервные копии.

вторник, 6 января 2009 г.

Динамическое меню (продолжение 2)

Теперь поговорим о динамическом меню для макросов. Здесь я описал функции, которые помогают в составлении этого меню. Здесь подробнее об XML-начинке.
Вот так выглядит результат:

В XML-схеме шаблона меню задано так:
Динамическое меню для макросов   Копировать код без номеров строк
1 <dynamicMenu id="DynMenuGetMacroAuto"
2 label="Авто Макросы"
3 getContent="DynMenuGetMacroAuto_GetContent"
4 screentip="Макросы, доступные для выполнения"
5 supertip="Динамическое меню, которое составляется автоматически.&#13;Если документ еще ни разу не сохранялся, то перед открытием меню документ нужно обязательно сохранить. Иначе это вызовет ошибку."/>

И наконец, само формирование меню (getContent)
Формирование динамического меню с макросами   Копировать код без номеров строк
1 Sub DynMenuGetMacroAuto_GetContent(control As IRibbonControl, ByRef content)
2 '
3 'Формирование динамического меню со всеми макросами, доступными для данного документа
4 '
5 Dim sXML As String 'строка, в которую записывается содержимое динамического меню
6 Dim i, j, IDcounter As Integer 'счетчики циклов и идентификаторов компонентов
7 Dim asModulesNames, asFuncNames As Variant 'массивы для хранения имен функций и модулей
8 'Начало формирования динамического меню
9 sXML = "<menu xmlns=""" & "http://schemas.microsoft.com/office/2006/01/customui" & """>" & vbCr
10 '--------------------------
11 'Кнопка «Обновить»
12 '--------------------------
13 sXML = sXML & vbTab & _
14 "<button id=""RefreshDynMenu"" " & _
15 "label=""Обновить"" " & _
16 "screentip=""Обновить содержимое этого меню"" " & _
17 "supertip=""Нажмите для обновления содержимого меню. &#13;Это необходимо делать, " & _
18 "если вы изменяете проект Visual Basic для данного документа. Или присоединяете " & _
19 "новые шаблоны."" " & _
20 "onAction=""RefreshDynMenu_OnAction"" " & _
21 "imageMso=""RecurrenceEdit""/>" & vbCr & vbTab & _
22 "<menuSeparator id=""MenuSep1""/>" & vbCr
23 '----------------------------------------------------------------------
24 'Формирование пунктов меню для каждого доступного шаблона с макросами
25 '----------------------------------------------------------------------
26 Dim oTemplate As Object 'объект для шаблона из которого будут читаться макросы
27 Dim bLockedProjOpened As Boolean 'флаг для определения состояния шаблона: закрыт он или открыт для чтения
28 For Each oTemplate In Application.templates
29 bLockedProjOpened = False
30 If oTemplate.VBProject.Protection = vbext_pp_locked Then
31 Set oTemplate = Application.Documents.Open(oTemplate.FullName, ReadOnly:=True)
32 bLockedProjOpened = True
33 End If
34 'Записываем строку, начинающую меню для каждого шаблона.
35 'Пробелы для идентификаторов заменяем на нижнее подчеркивание.
36 sXML = sXML & vbTab & _
37 "<menu id=""ID_" & Replace(oTemplate.Name, " ", "_") & IDcounter & """ " & _
38 "label=""Макросы из «" & oTemplate.Name & "»""> " & vbCr
39 'Счетчик для идентификаторов
40 IDcounter = IDcounter + 1
41 'Попытка прочитать имена программных модулей из указанного шаблона
42 On Error Resume Next
43 asModulesNames = fGetModulesNames(oTemplate) 'читаем программные модули из шаблона и записываем их в наш массив
44 'Если произошла ошибка при чтении модулей, то выводится сообщение и процедура прерывается.
45 If Err.Number <> 0 Then
46 MsgBox "Не удалось прочитать список модулей из " & oTemplate.Name, vbOKOnly, "Ошибка при формировании меню"
47 Err.Clear
48 Exit Sub
49 End If
50 For j = 0 To UBound(asModulesNames)
51 'Разделитель меню для каждого программного модуля в проекте шаблона.
52 sXML = sXML & vbTab & vbTab & _
53 "<menuSeparator id=""ID_" & Replace(oTemplate.Name, " ", "_") & IDcounter & """ " & _
54 "title=""" & asModulesNames(j) & """/> " & vbCr
55 IDcounter = IDcounter + 1
56 'Попытка прочитать имена функций из указанного шаблона указанного модуля.
57 On Error Resume Next
58 asFuncNames = fGetFuncNames(oTemplate, oTemplate.VBProject.VBComponents(asModulesNames(j)))
59 'Если произошла ошибка при чтении имен функций, то выводится сообщение и процедура прерывается.
60 If Err.Number <> 0 Then
61 MsgBox "Не удалось прочитать список функций из модуля " & asModulesNames(j) & _
62 " шаблона " & oTemplate.Name, vbOKOnly, "Ошибка при формировании меню"
63 Err.Clear
64 Exit Sub
65 End If
66 For i = 0 To UBound(asFuncNames)
67 'Создаем кнопку в меню, которая запускает соответствующий макрос. Путь к макросу записываем в свойство
68 '«tag». При вызове макроса «onAction» свойство «tag» передается в качестве аргумента. Свойство «tag»
69 'записываем в виде ‹имя_проекта›.‹имя_модуля›.‹имя_макроса›. Это позволяет запускать любые макросы из
70 'загруженных проектов.
71 sXML = sXML & vbTab & vbTab & _
72 "<button id=""menubtn_" & asModulesNames(j) & IDcounter & """ " & _
73 "label=""" & asFuncNames(i) & """ " & _
74 "tag=""" & oTemplate.VBProject.Name & "." & asModulesNames(j) & "." & asFuncNames(i) & """ " & _
75 "onAction=""MyButtonAction""" & "/>" & vbCr
76 IDcounter = IDcounter + 1
77 Next i
78 Next j
79 'Заканчиваем формирование меню с макросами для данного шаблона
80 sXML = sXML & vbTab & "</menu>" & vbCr
81 'Проверяем открывался ли закрытый для просмотра документ. Если да, то закрываем его.
82 If bLockedProjOpened Then
83 oTemplate.Close
84 End If
85 Next oTemplate
86 'Добавляем стандартные кнопки управлением средой Visual Basic через разделитель
87 sXML = sXML & vbTab & _
88 "<menuSeparator id=""MenuSep2"" title=""Visual Basic for Application""/>" & vbCr & vbTab & _
89 "<control idMso=""MacroPlay"" showLabel=""false""/>" & vbCr & vbTab & _
90 "<control idMso=""MacroRecordOrStop"" showLabel=""false""/>" & vbCr & vbTab & _
91 "<control idMso=""MacroRecorderPause"" showLabel=""false""/>" & vbCr & vbTab & _
92 "<control idMso=""VisualBasic"" showLabel=""false""/>" & vbCr
93 'Завершаем формирование меню
94 content = sXML & "</menu>"
95 'Отладочные строки. Нужны для просмотра готового кода в редакторе XML. При нормальной работе эти строки
96 'нужно закомментировать.
97 '''''''''''''''''''''''' Selection.InsertAfter content
98 '''''''''''''''''''''''' Selection.Cut

Итак, что же происходит?
Сначала добавляем кнопку «Обновить». Это, я полагаю, стандартный шаг для любого динамического меню, т.к. его всегда полезно лишний раз обновить.
Затем вставляем разделитель и формируем вложенные меню для каждого шаблона, доступного для документа. Стоит отметить, что этот этап может вызывать некоторую задержку при формировании, т.к. макросы могут читаться из шаблонов, код которых недоступен для просмотра, но доступен для выполнения. Чтобы добраться до макросов такого закрытого шаблона, он временно открывается для чтения, из него читаются макросы, и он тут же закрывается.
В каждом вложенном меню мы создаем разделители меню с названием модуля, а под этим разделителем — макросы из этого модуля.
Полностью сформированный код динамического меню выглядит так (чтобы его получить нужно раскомментировать строки 97 и 98):
Формирование динамического меню с макросами   Сформированное динамическое меню
1 <menu xmlns="http://schemas.microsoft.com/office/2006/01/customui">
2 <button id="RefreshDynMenu" label="Обновить" screentip="Обновить содержимое этого меню" supertip="Нажмите для обновления содержимого меню. &#13;Это необходимо делать, если вы изменяете проект Visual Basic для данного документа. Или присоединяете новые шаблоны." onAction="RefreshDynMenu_OnAction" imageMso="RecurrenceEdit"/>
3 <menuSeparator id="MenuSep1"/>
4 <menu id="ID_Шаблон_с_панелью_инструментов_и_макросами.dotm0" label="Макросы из «Шаблон с панелью инструментов и макросами.dotm»">
5 <menuSeparator id="ID_Шаблон_с_панелью_инструментов_и_макросами.dotm1" title="Macroses"/>
6 <button id="menubtn_Macroses2" label="ФормулаСНумерацией" tag="ПроектСМакросамиИЛентой.Macroses.ФормулаСНумерацией" onAction="MyButtonAction"/>
7 <button id="menubtn_Macroses3" label="ВзятьВКавычки" tag="ПроектСМакросамиИЛентой.Macroses.ВзятьВКавычки" onAction="MyButtonAction"/>
8 <button id="menubtn_Macroses4" label="УвеличитьШрифт" tag="ПроектСМакросамиИЛентой.Macroses.УвеличитьШрифт" onAction="MyButtonAction"/>
9 <button id="menubtn_Macroses5" label="УменьшитьШрифт" tag="ПроектСМакросамиИЛентой.Macroses.УменьшитьШрифт" onAction="MyButtonAction"/>
10 <button id="menubtn_Macroses6" label="СделатьРимскойЦифрой" tag="ПроектСМакросамиИЛентой.Macroses.СделатьРимскойЦифрой" onAction="MyButtonAction"/>
11 </menu>
12 <menu id="ID_Normal.dotm7" label="Макросы из «Normal.dotm»">
13 <menuSeparator id="ID_Normal.dotm8" title="ThisDocument"/>
14 <button id="menubtn_ThisDocument9" label="Формула_с_нумерацией" tag="Normal.ThisDocument.Формула_с_нумерацией" onAction="MyButtonAction"/>
15 <button id="menubtn_ThisDocument10" label="ВзятьВКавычки" tag="Normal.ThisDocument.ВзятьВКавычки" onAction="MyButtonAction"/>
16 <button id="menubtn_ThisDocument11" label="УвеличитьШрифт" tag="Normal.ThisDocument.УвеличитьШрифт" onAction="MyButtonAction"/>
17 <button id="menubtn_ThisDocument12" label="УменьшитьШрифт" tag="Normal.ThisDocument.УменьшитьШрифт" onAction="MyButtonAction"/>
18 <menuSeparator id="ID_Normal.dotm13" title="NewMacros"/>
19 <button id="menubtn_NewMacros14" label="BigCardText" tag="Normal.NewMacros.BigCardText" onAction="MyButtonAction"/>
20 <button id="menubtn_NewMacros15" label="EndOfWord" tag="Normal.NewMacros.EndOfWord" onAction="MyButtonAction"/>
21 <button id="menubtn_NewMacros16" label="СделатьРимскойЦифрой" tag="Normal.NewMacros.СделатьРимскойЦифрой" onAction="MyButtonAction"/>
22 <button id="menubtn_NewMacros17" label="Макрос2" tag="Normal.NewMacros.Макрос2" onAction="MyButtonAction"/>
23 </menu>
24 <menuSeparator id="MenuSep2" title="Visual Basic for Application"/>
25 <control idMso="MacroPlay" showLabel="false"/>
26 <control idMso="MacroRecordOrStop" showLabel="false"/>
27 <control idMso="MacroRecorderPause" showLabel="false"/>
28 <control idMso="VisualBasic" showLabel="false"/>
29 </menu>

Как видно из этого кода в свойстве tag прописан полный путь к макросу, что позволяет его запускать из любого открытого документа.
Естественно, что для вашего случая код динамического меню будет другой.