Разбираем по кусочкам «ленточный» интерфейс Word 2007+. Здесь нет и не будет советов о том, как его сделать похожим на Word 2003. Только усовершенствования уже существующего.
Также приглашаю посетить мой блог, посвященный работе с макросами в Word.
Напоминаю, что все коды программ, приведенные в блоге, вы используете на свой страх и риск. Не забывайте создавать резервные копии.
четверг, 14 февраля 2013 г.
Я доработал шаблон для вставки изображений из выбранной папки. Теперь выглядит это так:
Изменения:
Указание размера вставляемого изображения. В процентах или в выбранных единицах. С сохранением пропорций или без.
Указание постоянной части подписи к рисунку, выбрав из стандартных или добавив свою.
Исправлена ошибка с сохранением выбранной папки.
Задание местоположения подписи: в тексте или в прилежащей надписи.
Переработан интерфейс.
Картинки по-прежнему не отображаются в Word 2010, но корректно отображаются и в 2007-м, и в 2013. Скачать шаблон
Вот уже месяц, как я начал настраивать поначалу недоступный интерфейс нового офиса. Как оказалось, информации на русском языке по этой теме совсем немного (я сумел найти 2 места где более или менее толково объясняется, что надо делать и куда нажимать).
Посему решил обратиться к мировому русско- украиноязычному сообществу с просьбой о совместной работе. Кстати, подвернулся и со своим , поскольку не охота начинать такую тему на форуме.
Со своей стороны я уже немного разобрался как и где нажимать. Научился подключать к кнопкам свои макросы.
Пример моих кнопок:
Кого заинтересовала эта тема, милости просим.
На Украине (именно «на» а не «в» как хочется Задорнову) есть поговорка «Гуртом і батька легше бити», поэтому нужно подналечь и разобраться и этой темой.
Предыдущая запись в блоге была сделана в начале года и она является первой вехой на этапе преобразования интерфейса. Начал я изменение интерфейса Office 2007 после того, как мне надоело листать вкладки в поисках нужного инструмента. Огромную пользу в этом оказал сайт Максима Новикова и его программа для редактирования интерфейса. Результатами моих усилий стала панель с инструментами
Вся польза этой панели заключается в том, что в 80% случаев мне не нужны другие вкладки. Все есть на этой. Кроме редактирования рисунков. Следующей заботой стала необходимость прикрутить на эту панель кнопки, которые выполняли бы мои собственные макросы. Это потребовало определенных усилий. Результат на картинке
Возникает вопрос: как все это получилось?
Само создание панели инструментов на ленте не представляет особой сложности и довольно подробно рассмотрено на уже упомянутом сайте Максима Новикова.
А как же прикрутить к нему собственные кнопки или меню, которые должны выполнять мои собственные макросы?
Начнем по порядку. Во-первых, нужно создать кнопку, или любой другой элемент, который должен запускать на выполнение определенный макрос. Во-вторых, нужно указать параметр onAction, в котором определить имя макроса, который, собственно и должен запускаться при нажатии на данную кнопку или пункт меню, что в общем-то одно и тоже. В-третьих, написать правильно сам макрос.
Вот меню, которое я использую постоянно в своей работе. Оно еще не содержит всех пунктов, которые мне нужны, но уже работает.
<menuid="__MyMacroses"label="Макросы"><menuseparatorid="__Separator1"title="Вставка в документ"/><buttonid="InsertQuotes"image="QUOTES.png"label="Взять в кавычки"onaction="MyButtonAction"supertip="Выделенный текст заключается в угловые кавычки."tag="Macroses.ВзятьВКавычки"/><buttonid="ConvertToRoman"image="roman.png"label="Сделать римской цифрой"onaction="MyButtonAction"supertip="Выделенный текст преобразуется в римскую цифру."tag="Macroses.СделатьРимскойЦифрой"/><buttonid="InsertFormula"image="function.png"label="Вставить формулу с нумерацией"onaction="MyButtonAction"supertip="Вставка формулы по центру страницы и нумерацией по правому краю"tag="Macroses.Формула_с_нумерацией"/><menuseparatorid="__Separator2"title="Правка документа"/><buttonid="ClearDoubleSpaces"label="Удалить пробелы"onaction="MyButtonAction"supertip="Удаление 2-х и более пробелов подряд."tag="Macroses.Удалить_лишние_пробелы"/></menu>
Как видно из кода макроса он запускает на выполнение другой макрос, имя которого прописано в свойстве tag нашей кнопки. Этот простой маневр позволяет использовать только один макрос, для обработки нажатий на все кнопки. Главное, не забывать, что для каждой кнопки нужно указывать свое свойство tag.
Пока все.
При работе с текстовыми документами мне часто приходится изменять междустрочный интервал. Постоянно открывать для этого соответствующее меню мне надоело. Поэтому я подумал:«А как сделать, чтобы интервал задавать числом? 2 — для двойного интервала, 1 — для одинарного, или просто конкретное значение?» Во-первых, поместить на ленту поле editbox.
Вот так, например:
Во-вторых, написать код макроса, который бы брал текст из поля и соответственно устанавливал междустрочный интервал. Довольно долгие поиски дали вот такой результат (с этого сайта):
Sub УстановитьМежстрочныйИнтервал(editbox As IRibbonControl, text As String)/>
Selection.ParagraphFormat.LineSpacing = LinesToPoints(Val(text))
End Sub
Как видно код очень даже не сложный, но очень сложно вообще откопать информацию, особенно на русском языке, по работе с новым интерфейсом. На MSDN при вводе в поле поиска фразы «ribbon vba» страница просто закрывалась. Прямо масонский заговор
Итак, весь секрет заключался в параметре text, который очень не очевиден, и просто объявляется.
Теперь задача в другом: как выставлять значение в этом поле в соответствии со строчным интервалом уже выделенного текста? И все это только средствами VBA.
Вчера разговор закончился на том, что была поставлена задача об обновлении данных в поле ввода при изменении выбора.
Решается это так. Для начала нужно научиться ловить события, которые генерирует Word. В нашем случае нужно определять, что пользователь изменил набор выбранных объектов. При этом генерируется событие WindowSelectionChange. Как его ловить? Это подробно описано в моем блоге, посвященном макросам.
Здесь же мы подробно рассмотрим вопрос, связанный с обновлением информации на ленте.
Итак. Чтобы работала обратная связь с лентой, код XML, который описывает ленту, нужно доработать. Во-первых, нужно добавить событие onLoad. Делается это так:
Во-вторых, добавить в наше поле событие getText:
Этот код отличается от того, что я приводил раньше, но не принципиально. Я сделал имена макросов более понятными, чтобы не путаться. Параметр sizeString определяет длину поля ввода в символах.
Событие getText отвечает как раз за управление содержимым поля ввода. В-третьих, нужно правильно написать обработчики событий onLoad и getText. Я это сделал так:
Public CustomRibbon As IRibbonUI 'указатель для хранения ссылки на ленту. Здесь необязателен, но рекомендуется
Dim MyEvents As New EventClassModule 'конструктор класса для отслеживания событий
Sub LoadRibbon(ribbon As IRibbonUI) 'загружаем ленту
Set CustomRibbon = ribbon 'создаем указатель на ленту
ribbon.Invalidate 'обновляем ленту
Set MyEvents.App = Word.Application 'перенаправляем события из приложения в наш класс.
End Sub
Sub SetLineSpacing_GetText(control As IRibbonControl, ByRef text)
text = PointsToLines(Selection.ParagraphFormat.LineSpacing) 'устанавливаем в поле ввода значение межстрочного интервала выделенного абзаца.
End Sub
Еще раз напомню, что узнать о том, как работать с событиями приложения можно здесь.
Оказывается, что формировать ленту на лету не только можно, но и чрезвычайно полезно.
Я решил, по примеру Антона Кокина сделать свое меню для работы с закладками, только в версии для Word2007.
Сказано — сделано.
Вот что получилось:
При добавлении закладки в документ меню обновляется и имя закладки появляется внизу списка. Щелчок на имени приводит к переходу на эту закладку. В подменю «Управление закладками» появляются пункты, позволяющие удалить нужную закладку. Например, вот так:
Для того, чтобы все это работало, следует выполнить несколько простых шагов:
Создать динамическое меню в схеме документа при помощи RibbonXMLEditor
В этом меню определить макрос, который отвечает за наполнение меню содержимым
Написать сам макрос в документе, или, что намного лучше, в шаблоне.
Следует заметить, что динамическое меню получает свое содержимое при первом щелчке на нем, что может вызывать определенные задержки при большом количестве закладок или на слабой машине. А я-то думал, почему так долго открываются стандартные меню-галереи? А ведь они обновляются при открытии.
В шаблоне, где мы делаем все манипуляции с ленточным интерфейсом, пишем код макроса DynMenuGetBookmarks_GetContent. Код этот довольно сложен для восприятия с первого раза, но, разобравшись, он не представляет трудностей.
Sub DynMenuGetBookmarks_GetContent(control As IRibbonControl,ByRef content)
'
'Формирование меню с закладками документа
'
Dim sXML As String
Dim i As Integer
Set bm = ActiveDocument.Bookmarks
sXML = "" & vbCr
'--------------------------
'Разделитель
'--------------------------
sXML = sXML & _
"" & vbCr
'--------------------------
'Кнопки с именами закладок
'--------------------------
If bm.count = 0 Then
sXML = sXML & _
""
'возвращаем значение в компонент
End Sub
Как видно первой создается кнопка «Обновить» и для нее назначается макрос RefreshDynMenu_OnAction, который обновляет меню.
Sub RefreshDynMenu_OnAction(control As IRibbonControl)
'Обновление динамического меню'
CustomRibbon.Invalidate
End Sub
При удалении закладки меню обновляется автоматически, но не без нашего участия. Чтобы эта автоматика работала, мы при создании меню «Управление закладками» создаем кнопки, которые отвечают за удаление той или иной закладки. Имя закладки пишем в свойство tag. Удаление закладки происходит при выполнении макроса delbm_onAction
Public Sub delbm_onAction(control As IRibbonControl)
'
'Удаление закладки при выборе ее в соответствующем меню
'
bm.Item(control.Tag).Delete
CustomRibbon.Invalidate
End Sub
Также при выполнении макроса GoToBookmark осуществляется переход к выбранной закладке.
<
Sub GoToBookmark(control As IRibbonControl)
'
'Переход к закладке
'
On Error Resume Next
Selection.GoTo What:=wdGoToBookmark, Name:=control.Tag
If Err.Number <> 0 Then
MsgBox "Не удалось перейти к закладке «" & control.Tag & "»", vbOKOnly
End If
End Sub
>
Для добавления закладки вызывается стандартный диалог работы с закладками
Public Sub menu_AddBookmark_onAction(control As IRibbonControl)
'
'Появление окна для добавления закладки в документ
'
Dialogs(wdDialogInsertBookmark).Show
CustomRibbon.Invalidate
End Sub
При любых действиях с закладками вызывается метод CustomRibbon.Invalidate, который обновляет содержимое ленты.
С закладками получилось все довольно просто. Теперь другая задача стоит: как сформировать динамическое меню из доступных для выполнения макросов, которые можно запустить через стандартное диалоговое окно по Alt+F8? Сразу скажу, что в черновом варианте эту задачу я уже решил. Подробности далее в публикациях.
Теперь поговорим о динамическом меню для макросов. Здесь я описал функции, которые помогают в составлении этого меню. Здесь подробнее об XML-начинке. Вот так выглядит результат: В XML-схеме шаблона меню задано так:
SubDynMenuGetMacroAuto_GetContent(control As IRibbonControl,ByRef content)''Формирование динамического меню со всеми макросами, доступными для данного документа'Dim sXML AsString'строка, в которую записывается содержимое динамического менюDim i, j, IDcounter AsInteger'счетчики циклов и идентификаторов компонентовDim asModulesNames, asFuncNames AsVariant'массивы для хранения имен функций и модулей'Начало формирования динамического меню
sXML ="<menu xmlns="""&"http://schemas.microsoft.com/office/2006/01/customui"&""">"& vbCr
'--------------------------'Кнопка «Обновить»'--------------------------
sXML = sXML & vbTab & _
"<button id=""RefreshDynMenu"" "& _
"label=""Обновить"" "& _
"screentip=""Обновить содержимое этого меню"" "& _
"supertip=""Нажмите для обновления содержимого меню. Это необходимо делать, "& _
"если вы изменяете проект Visual Basic для данного документа. Или присоединяете "& _
"новые шаблоны."" "& _
"onAction=""RefreshDynMenu_OnAction"" "& _
"imageMso=""RecurrenceEdit""/>"& vbCr & vbTab & _
"<menuSeparator id=""MenuSep1""/>"& vbCr
'----------------------------------------------------------------------'Формирование пунктов меню для каждого доступного шаблона с макросами'----------------------------------------------------------------------Dim oTemplate As Object'объект для шаблона из которого будут читаться макросыDim bLockedProjOpened AsBoolean'флаг для определения состояния шаблона: закрыт он или открыт для чтенияFor Each oTemplate In Application.templates
bLockedProjOpened =FalseIf oTemplate.VBProject.Protection = vbext_pp_locked ThenSet oTemplate = Application.Documents.Open(oTemplate.FullName, ReadOnly:=True)
bLockedProjOpened =TrueEnd If'Записываем строку, начинающую меню для каждого шаблона.'Пробелы для идентификаторов заменяем на нижнее подчеркивание.
sXML = sXML & vbTab & _
"<menu id=""ID_"&Replace(oTemplate.Name," ","_") & IDcounter &""" "& _
"label=""Макросы из «"& oTemplate.Name &"»""> "& vbCr
'Счетчик для идентификаторов
IDcounter = IDcounter +1'Попытка прочитать имена программных модулей из указанного шаблонаOn Error Resume Next
asModulesNames =fGetModulesNames(oTemplate)'читаем программные модули из шаблона и записываем их в наш массив'Если произошла ошибка при чтении модулей, то выводится сообщение и процедура прерывается.If Err.Number <>0Then
MsgBox "Не удалось прочитать список модулей из "& oTemplate.Name, vbOKOnly,"Ошибка при формировании меню"
Err.Clear
Exit SubEnd IfFor j =0ToUBound(asModulesNames)'Разделитель меню для каждого программного модуля в проекте шаблона.
sXML = sXML & vbTab & vbTab & _
"<menuSeparator id=""ID_"&Replace(oTemplate.Name," ","_") & IDcounter &""" "& _
"title="""&asModulesNames(j) &"""/> "& vbCr
IDcounter = IDcounter +1'Попытка прочитать имена функций из указанного шаблона указанного модуля.On Error Resume Next
asFuncNames =fGetFuncNames(oTemplate, oTemplate.VBProject.VBComponents(asModulesNames(j)))'Если произошла ошибка при чтении имен функций, то выводится сообщение и процедура прерывается.If Err.Number <>0Then
MsgBox "Не удалось прочитать список функций из модуля "&asModulesNames(j) & _
" шаблона "& oTemplate.Name, vbOKOnly,"Ошибка при формировании меню"
Err.Clear
Exit SubEnd IfFor i =0ToUBound(asFuncNames)'Создаем кнопку в меню, которая запускает соответствующий макрос. Путь к макросу записываем в свойство'«tag». При вызове макроса «onAction» свойство «tag» передается в качестве аргумента. Свойство «tag»'записываем в виде ‹имя_проекта›.‹имя_модуля›.‹имя_макроса›. Это позволяет запускать любые макросы из'загруженных проектов.
sXML = sXML & vbTab & vbTab & _
"<button id=""menubtn_"&asModulesNames(j) & IDcounter &""" "& _
"label="""&asFuncNames(i) &""" "& _
"tag="""& oTemplate.VBProject.Name &"."&asModulesNames(j) &"."&asFuncNames(i) &""" "& _
"onAction=""MyButtonAction"""&"/>"& vbCr
IDcounter = IDcounter +1Next i
Next j
'Заканчиваем формирование меню с макросами для данного шаблона
sXML = sXML & vbTab &"</menu>"& vbCr
'Проверяем открывался ли закрытый для просмотра документ. Если да, то закрываем его.If bLockedProjOpened Then
oTemplate.Close
End IfNext oTemplate
'Добавляем стандартные кнопки управлением средой Visual Basic через разделитель
sXML = sXML & vbTab & _
"<menuSeparator id=""MenuSep2"" title=""Visual Basic for Application""/>"& vbCr & vbTab & _
"<control idMso=""MacroPlay"" showLabel=""false""/>"& vbCr & vbTab & _
"<control idMso=""MacroRecordOrStop"" showLabel=""false""/>"& vbCr & vbTab & _
"<control idMso=""MacroRecorderPause"" showLabel=""false""/>"& vbCr & vbTab & _
"<control idMso=""VisualBasic"" showLabel=""false""/>"& vbCr
'Завершаем формирование меню
content = sXML &"</menu>"'Отладочные строки. Нужны для просмотра готового кода в редакторе XML. При нормальной работе эти строки'нужно закомментировать.'''''''''''''''''''''''' Selection.InsertAfter content'''''''''''''''''''''''' Selection.Cut
Итак, что же происходит? Сначала добавляем кнопку «Обновить». Это, я полагаю, стандартный шаг для любого динамического меню, т.к. его всегда полезно лишний раз обновить. Затем вставляем разделитель и формируем вложенные меню для каждого шаблона, доступного для документа. Стоит отметить, что этот этап может вызывать некоторую задержку при формировании, т.к. макросы могут читаться из шаблонов, код которых недоступен для просмотра, но доступен для выполнения. Чтобы добраться до макросов такого закрытого шаблона, он временно открывается для чтения, из него читаются макросы, и он тут же закрывается. В каждом вложенном меню мы создаем разделители меню с названием модуля, а под этим разделителем — макросы из этого модуля. Полностью сформированный код динамического меню выглядит так (чтобы его получить нужно раскомментировать строки 97 и 98):
<menuxmlns="http://schemas.microsoft.com/office/2006/01/customui"><buttonid="RefreshDynMenu"label="Обновить"screentip="Обновить содержимое этого меню"supertip="Нажмите для обновления содержимого меню. Это необходимо делать, если вы изменяете проект Visual Basic для данного документа. Или присоединяете новые шаблоны."onAction="RefreshDynMenu_OnAction"imageMso="RecurrenceEdit"/><menuSeparatorid="MenuSep1"/><menuid="ID_Шаблон_с_панелью_инструментов_и_макросами.dotm0"label="Макросы из «Шаблон с панелью инструментов и макросами.dotm»"><menuSeparatorid="ID_Шаблон_с_панелью_инструментов_и_макросами.dotm1"title="Macroses"/><buttonid="menubtn_Macroses2"label="ФормулаСНумерацией"tag="ПроектСМакросамиИЛентой.Macroses.ФормулаСНумерацией"onAction="MyButtonAction"/><buttonid="menubtn_Macroses3"label="ВзятьВКавычки"tag="ПроектСМакросамиИЛентой.Macroses.ВзятьВКавычки"onAction="MyButtonAction"/><buttonid="menubtn_Macroses4"label="УвеличитьШрифт"tag="ПроектСМакросамиИЛентой.Macroses.УвеличитьШрифт"onAction="MyButtonAction"/><buttonid="menubtn_Macroses5"label="УменьшитьШрифт"tag="ПроектСМакросамиИЛентой.Macroses.УменьшитьШрифт"onAction="MyButtonAction"/><buttonid="menubtn_Macroses6"label="СделатьРимскойЦифрой"tag="ПроектСМакросамиИЛентой.Macroses.СделатьРимскойЦифрой"onAction="MyButtonAction"/></menu><menuid="ID_Normal.dotm7"label="Макросы из «Normal.dotm»"><menuSeparatorid="ID_Normal.dotm8"title="ThisDocument"/><buttonid="menubtn_ThisDocument9"label="Формула_с_нумерацией"tag="Normal.ThisDocument.Формула_с_нумерацией"onAction="MyButtonAction"/><buttonid="menubtn_ThisDocument10"label="ВзятьВКавычки"tag="Normal.ThisDocument.ВзятьВКавычки"onAction="MyButtonAction"/><buttonid="menubtn_ThisDocument11"label="УвеличитьШрифт"tag="Normal.ThisDocument.УвеличитьШрифт"onAction="MyButtonAction"/><buttonid="menubtn_ThisDocument12"label="УменьшитьШрифт"tag="Normal.ThisDocument.УменьшитьШрифт"onAction="MyButtonAction"/><menuSeparatorid="ID_Normal.dotm13"title="NewMacros"/><buttonid="menubtn_NewMacros14"label="BigCardText"tag="Normal.NewMacros.BigCardText"onAction="MyButtonAction"/><buttonid="menubtn_NewMacros15"label="EndOfWord"tag="Normal.NewMacros.EndOfWord"onAction="MyButtonAction"/><buttonid="menubtn_NewMacros16"label="СделатьРимскойЦифрой"tag="Normal.NewMacros.СделатьРимскойЦифрой"onAction="MyButtonAction"/><buttonid="menubtn_NewMacros17"label="Макрос2"tag="Normal.NewMacros.Макрос2"onAction="MyButtonAction"/></menu><menuSeparatorid="MenuSep2"title="Visual Basic for Application"/><controlidMso="MacroPlay"showLabel="false"/><controlidMso="MacroRecordOrStop"showLabel="false"/><controlidMso="MacroRecorderPause"showLabel="false"/><controlidMso="VisualBasic"showLabel="false"/></menu>
Как видно из этого кода в свойстве tag прописан полный путь к макросу, что позволяет его запускать из любого открытого документа. Естественно, что для вашего случая код динамического меню будет другой.
Рад сообщить, что Новиков Максим не забросил работу над своим редактором ленточного интерфейса, а совершенствует его и дальше. В последней версии 2.2 реализован механизм генерирования процедур обратного вызова для динамических аттрибутов, открытие редактора из командной строки, настройка подсветки синтаксиса, улучшенные списки вставки статических и динамических атрибутов, проверка целостности xml-структуры, открытие редактируемого файла в соответствующем приложении офиса, много мелких доработок. Сейчас Максим работает над третьей версией редактора, в которой будет реализована полная поддержка интерфейса для офис 2010, улучшенная навигация по структуре документа, проверка правильности вложения атрибутов. По мере сил я помогаю Максиму в решении возникающих задач. По-прежнему последнюю версию редактора можно скачать с этой страницы
После тестирования этого предыдущего шаблона выяснилось, что логика работы флажков для вставки названия не совсем понятна. Учитывая это, я внёс изменения. Теперь группа выглядит так: Если установить флажок «Вставлять название», то к вставляемому рисунку будет добавляться название, как обычно. Если установить флажок «Имя файла в названии», то в название будет добавляться имя файла. Если установить флажок «Не нумеровать», то постоянная часть и номер из названия будут удалены. Кроме этих изменений, ускорена загрузка изображений в галерею и состояние элементов управления теперь запоминается. Скачать шаблон можно отсюда.