четверг, 9 апреля 2015 г.

Элемент управления dynamicMenu


Я уже рассказывал о динамических меню ранее:
  1. Динамические меню
  2. Динамические меню (продолжение)
  3. Динамические меню (продолжение 2)
  4. Динамические меню (продолжение 3)
Сегодня я подведу итог и представлю окончательный результат.
Для более полной картины нижеописанного рекомендую ознакомиться с прекрасными материалами на эту тему: Урок 12. Динамическое менюУрок 8. Динамические атрибуты. Тем более, что, судя по содержанию, толчком для этих материалов, в какой-то мере, был и мой блог.

пятница, 3 апреля 2015 г.

Новая версия RXE. Исправления в блоге.

Редактор ленточного интерфейса Ribbon Xml Editor не остановился в своём развитии, недавно вышла очередная его версия. Из новых возможностей стоит отметить: контекстно-зависимую подсказку при вводе атрибутов и встроенный просмотрщик стандартных офисных изображений, которые можно ставить на свои элементы управления с атрибутом idMso. Полный список изменений.
В блоге проведена работа по приведению его в должный вид: подсветка кода, выделение кода мышкой без номеров строк, спойлеры.

воскресенье, 16 февраля 2014 г.

Новая версия Ribbon XML Editor. Неполадки в работе блога

Приношу извинения за временное некорректное отображение блога. В скором времени проблема будет устранена.
Тем временем, хочу предложить вниманию всех новую версию редактора "ленточного" интерфейса Ribbon XML Editor. Не буду повторять изложенное на сайте Максима, но подчеркну особо важные для меня новые функции:

  1. Открытие документа в приложении офиса и (sic!) повторное открытие в RXE после закрытия документа в офисе. Очень, очень полезная функция в процессе разработки, когда нужно часто переключаться, чтобы проверить внесённые изменения.
  2. "Допечатывание" кавычек после знака "=".
  3. Сохранения всех настроек, которые только можно сохранить. Теперь ничего не пропадёт :).
  4. Прекрасная справка по использованию программы.

четверг, 14 февраля 2013 г.

Я доработал шаблон для вставки изображений из выбранной папки. Теперь выглядит это так:

Изменения:
  1. Указание размера вставляемого изображения. В процентах или в выбранных единицах. С сохранением пропорций или без.
  2. Указание постоянной части подписи к рисунку, выбрав из стандартных или добавив свою.
  3. Исправлена ошибка с сохранением выбранной папки.
  4. Задание местоположения подписи: в тексте или в прилежащей надписи.
  5. Переработан интерфейс.
Картинки по-прежнему не отображаются в Word 2010, но корректно отображаются и в 2007-м, и в 2013.
Скачать шаблон

Работаем над Office 2007

Вот уже месяц, как я начал настраивать поначалу недоступный интерфейс нового офиса. Как оказалось, информации на русском языке по этой теме совсем немного (я сумел найти 2 места где более или менее толково объясняется, что надо делать и куда нажимать).

Посему решил обратиться к мировому русско- украиноязычному сообществу с просьбой о совместной работе. Кстати, подвернулся и  со своим , поскольку не охота начинать такую тему на форуме.

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

Пример моих кнопок:


Кого заинтересовала эта тема, милости просим.

На Украине (именно «на» а не «в» как хочется Задорнову) есть поговорка «Гуртом і батька легше бити», поэтому нужно подналечь и разобраться и этой темой.

Предыдущая запись в блоге была сделана в начале года и она является первой вехой на этапе преобразования интерфейса.
Начал я изменение интерфейса Office 2007 после того, как мне надоело листать вкладки в поисках нужного инструмента. Огромную пользу в этом оказал сайт Максима Новикова и его программа для редактирования интерфейса.
Результатами моих усилий стала панель с инструментами


Вся польза этой панели заключается в том, что в 80% случаев мне не нужны другие вкладки. Все есть на этой. Кроме редактирования рисунков.
Следующей заботой стала необходимость прикрутить на эту панель кнопки, которые выполняли бы мои собственные макросы.
Это потребовало определенных усилий. Результат на картинке
Возникает вопрос: как все это получилось?
Само создание панели инструментов на ленте не представляет особой сложности и довольно подробно рассмотрено на уже упомянутом сайте Максима Новикова.
А как же прикрутить к нему собственные кнопки или меню, которые должны выполнять мои собственные макросы?
Начнем по порядку.
Во-первых, нужно создать кнопку, или любой другой элемент, который должен запускать на выполнение определенный макрос.
Во-вторых, нужно указать параметр onAction, в котором определить имя макроса, который, собственно и должен запускаться при нажатии на данную кнопку или пункт меню, что в общем-то одно и тоже.
В-третьих, написать правильно сам макрос.
Вот меню, которое я использую постоянно в своей работе. Оно еще не содержит всех пунктов, которые мне нужны, но уже работает.
Собственное меню для запуска макросов
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <menu id="__MyMacroses" label="Макросы"> <menuseparator id="__Separator1" title="Вставка в документ"/> <button id="InsertQuotes" image="QUOTES.png" label="Взять в кавычки" onaction="MyButtonAction" supertip="Выделенный текст заключается в угловые кавычки." tag="Macroses.ВзятьВКавычки"/> <button id="ConvertToRoman" image="roman.png" label="Сделать римской цифрой" onaction="MyButtonAction" supertip="Выделенный текст преобразуется в римскую цифру." tag="Macroses.СделатьРимскойЦифрой"/> <button id="InsertFormula" image="function.png" label="Вставить формулу с нумерацией" onaction="MyButtonAction" supertip="Вставка формулы по центру страницы и нумерацией по правому краю" tag="Macroses.Формула_с_нумерацией"/> <menuseparator id="__Separator2" title="Правка документа"/> <button id="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.
Сказано — сделано.
Вот что получилось:

При добавлении закладки в документ меню обновляется и имя закладки появляется внизу списка. Щелчок на имени приводит к переходу на эту закладку. В подменю «Управление закладками» появляются пункты, позволяющие удалить нужную закладку. Например, вот так:

Для того, чтобы все это работало, следует выполнить несколько простых шагов:

  1. Создать динамическое меню в схеме документа при помощи RibbonXMLEditor

  2. В этом меню определить макрос, который отвечает за наполнение меню содержимым

  3. Написать сам макрос в документе, или, что намного лучше, в шаблоне.

Следует заметить, что динамическое меню получает свое содержимое при первом щелчке на нем, что может вызывать определенные задержки при большом количестве закладок или на слабой машине. А я-то думал, почему так долго открываются стандартные меню-галереи? А ведь они обновляются при открытии.

В шаблоне, где мы делаем все манипуляции с ленточным интерфейсом, пишем код макроса 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
 '--------------------------
 'Разделитель
 '--------------------------
 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? Сразу скажу, что в черновом варианте эту задачу я уже решил. Подробности далее в публикациях.

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

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

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

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

Обновление редактора Ribbon XML Editor

Рад сообщить, что Новиков Максим не забросил работу над своим редактором ленточного интерфейса, а совершенствует его и дальше.
В последней версии 2.2 реализован механизм генерирования процедур обратного вызова для динамических аттрибутов, открытие редактора из командной строки, настройка подсветки синтаксиса, улучшенные списки вставки статических и динамических атрибутов, проверка целостности xml-структуры, открытие редактируемого файла в соответствующем приложении офиса, много мелких доработок.
Сейчас Максим работает над третьей версией редактора, в которой будет реализована полная поддержка интерфейса для офис 2010, улучшенная навигация по структуре документа, проверка правильности вложения атрибутов.
По мере сил я помогаю Максиму в решении возникающих задач.
По-прежнему последнюю версию редактора можно скачать с этой страницы

Вставка картинок в документ из выбранной папки (исправления)

После тестирования этого предыдущего шаблона выяснилось, что логика работы флажков для вставки названия не совсем понятна. Учитывая это, я внёс изменения. Теперь группа выглядит так:

Если установить флажок «Вставлять название», то к вставляемому рисунку будет добавляться название, как обычно. Если установить флажок «Имя файла в названии», то в название будет добавляться имя файла. Если установить флажок «Не нумеровать», то постоянная часть и номер из названия будут удалены.
Кроме этих изменений, ускорена загрузка изображений в галерею и состояние элементов управления теперь запоминается.
Скачать шаблон можно отсюда.

понедельник, 28 июня 2010 г.

Элемент dropDown — выпадающий список

Как раз раздумывал над очередной темой, как её подсказала сама жизнь: просьба рассказать об элементе comboBox и сетования одного из посетителей WordExpert на неудобную работу с автотекстом. Постараюсь убить двух зайцев сразу.
Итак, задача: сформировать список из элементов автотекста, содержащихся в шаблоне, на котором основан документ. При выборе элемента из списка, вставлять его в документ.
Как всегда, начнём с XML-схемы. На ленту поместим группу "Автотекст" с выпадающим списком и кнопкой для обновления этого списка, если при работе с документом мы добавим элемент автотекста шаблон. В качестве списка я буду использовать не comboBox, а dropDown. Они абсолютно идентичны, но comboBox позволяет принимать текст, введённый пользователем. Нам это не нужно, поэтому используем dropDown.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="RibbonLoading">
 <ribbon startFromScratch="false">
  <tabs>
   <tab idMso="TabHome">
    <group id="grAutoText" label="Автотекст">
     <dropDown id="ddAutoText"
          onAction="ddAutoText_onAction"
          getItemLabel="ddAutoText_getItemLabel"
          getItemSupertip="ddAutoText_getItemSupertip"
          getItemCount="ddAutoText_getItemCount"
          getItemID="ddAutoText_getItemID"
          getSelectedItemIndex="ddAutoText_getSelectedItemIndex" >

     </dropDown>
     <button id="btnRefresh"
         label="Обновить"
         imageMso="RecurrenceEdit"
         onAction="btnRefresh_onAction" />
    </group>
   </tab>
  </tabs>
 </ribbon>
</customUI>

Пояснения к атрибутам элемента dropDown.

  • onAction — процедура, вызываемая при нажатии на пункте списка

  • getItemLabel — процедура для задания текста элемента списка

  • getItemSupertip — процедура для задания текста расширенной подсказки элемента списка

  • getItemCount — процедура для задания количества элементов списка

  • getItemID — процедура для задания идентификаторов элементво списка (необязательная)

  • getSelectedItemIndex — процедура для выбора определённого элемента списка


Кнопка совершенно обычная и никаких трудностей у вас не должна вызвать, если вы дошли до создания списка. Если же возникли трудности, то ознакомьтесь с этой заметкой. Сохраняем шаблон и открываем его в Word, не забыв перед этим получить заготовку для кода VBA. Для этого в программе Ribbon XML Editor, которую я настоятельно рекомендую к использованию, предусмотрена специальная функция : вы можете автоматически получить заготовки для всех динамических атрибутов, указанных в XML-схеме и сохранить их в виде модуля *.bas для последующего импорта в документ.
Теперь код VBA. Открываем шаблон в Word, импортируем сохранённый модуль с процедурами ленты и доводим его до такого состояния:
Option Explicit
Dim tmp As Template
Dim bb As BuildingBlocks
Dim MyRibbon As IRibbonUI

' (компонент: customUI, атрибут: onLoad), 2007
Sub RibbonLoading(ribbon As IRibbonUI)
  Set tmp = ActiveDocument.AttachedTemplate
  Set bb = tmp.BuildingBlockTypes(wdTypeAutoText).Categories("Общие").BuildingBlocks
  Set MyRibbon = ribbon
End Sub

'ddAutoText (компонент: dropDown, атрибут: onAction), 2007
Sub ddAutoText_onAction(control As IRibbonControl, selectedId As String, selectedIndex As Integer)
  bb(selectedIndex).Insert Where:=Selection.Range, RichText:=True
End Sub

'ddAutoText (компонент: dropDown, атрибут: getItemLabel), 2007
Sub ddAutoText_getItemLabel(control As IRibbonControl, index As Integer, ByRef label)
  label = bb(index + 1).Name
End Sub

'ddAutoText (компонент: dropDown, атрибут: getItemSupertip), 2007
Sub ddAutoText_getItemSupertip(control As IRibbonControl, index As Integer, ByRef superTip)
  superTip = bb(index + 1).Value
End Sub

'ddAutoText (компонент: dropDown, атрибут: getItemCount), 2007
Sub ddAutoText_getItemCount(control As IRibbonControl, ByRef count)
'  Dim tmp As Template
  count = bb.count
End Sub

'ddAutoText (компонент: dropDown, атрибут: getItemID), 2007
Sub ddAutoText_getItemID(control As IRibbonControl, index As Integer, ByRef id)
  id = bb(index + 1).Name
End Sub

'ddAutoText (компонент: dropDown, атрибут: getSelectedItemIndex), 2007
Sub ddAutoText_getSelectedItemIndex(control As IRibbonControl, ByRef index)
  index = 0
End Sub

'btnRefresh (компонент: button, атрибут: onAction), 2007
Sub btnRefresh_onAction(control As IRibbonControl)
  MyRibbon.InvalidateControl "ddAutoText"
End Sub

В результате получим вот такой симпатичный список

В конце, несколько замечаний. Как видно из кода, для выборки элементов автотекста используется только одна категория: "Общие". Если автотекст распределён по нескольким категориям, они не будут видны. Если нужно распределить по категориям, то либо создавать столько списков, сколько в шаблоне категорий автотекста, либо использовать не список, а динамическое меню. Естественно это потребует переделки кода.
Пример шаблона