четверг, 12 марта 2015 г.

Урок 11. Создание второй надстройки, дополняющей первую.

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

Создадим в Word новый документ и сохраним его как документ с макросами (.doсm). Убедимся, что в новом документе работает надстройка, сделанная нами на прошлом уроке. Закроем документ, и откроем его через Ribbon XML Editor.

Кстати, документы можно открывать в Ribbon XML Editor как непосредственно из этого редактора, так и из контекстного меню самого документа, что очень удобно. Но для этого нужно вначале добавить в эти контекстные меню соответствующий пункт. Это легко делается со страницы настроек Ribbon XML Editor.

Откройте вкладку настроек, и обратите внимание на раздел в правой части «Добавление «Открыть в Ribbon XML Editor» в контекстное меню проводника». Отметьте галочками те документы, для которых в контекстное меню проводника должен быть добавлен соответствующий пункт. В нашем случае достаточно одной галочки в столбце «Для файлов Word» напротив «Документ с макросами», но можно отметить и все галочки. Затем нажмите кнопку «Установить».

Обязательно прочтите всплывающую подсказку на кнопке, там есть много полезной информации. В частности, то, что документ будет открываться в той копии Ribbon XML Editor, из которой была осуществлена установка. Перед удалением программы, для того, чтобы убрать ненужные уже пункты контекстных меню, не забудьте снять все установленные галочки и снова нажать кнопку «Установить» для установки отменённого состояния пунктов контекстного меню.

Напишем интерфейс, аналогичный прежней надстройке:

<?xml version="1.0" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
    <ribbon startFromScratch="false">
        <tabs>
            <tab id="Вкладка1" label="Полезные надстройки" insertBeforeMso="TabHome" keytip="Н">
                <group id="РаботаСоСтроками" label="Работа со строками">
                    <button 
                        id="ДублироватьТекущуюСтроку" 
                        onAction="ДублироватьТекущуюСтроку" 
                        label="Дублировать" 
                        keytip="Д" 
                        imageMso="QuickStylesSets" 
                        size="large" 
                        screentip="Дублировать текущую строку" 
                        supertip="Сопировать текущую строку в строку ниже"/>
                    <button 
                        id="УдалитьСдвоенныеПустыеСтроки" 
                        onAction="УдалитьСдвоенныеПустыеСтроки" 
                        label="Удалить повторные пустые строки" 
                        keytip="С" 
                        imageMso="RecordsCollapseAllSubdatasheets" 
                        size="large" 
                        screentip="Удалить повторные пустые строки" 
                        supertip="Найти и заменить все повторяющиеся пустые строки одной"/>
                    <button 
                        id="УдалитьПустыеСтроки" 
                        onAction="УдалитьПустыеСтроки" 
                        label="Удалить пустые строки" 
                        keytip="В" 
                        imageMso="GroupQuerySetup" 
                        size="large" 
                        screentip="Удалить все пустые строки" 
                        supertip="Найти и удалить все пустые строки"/>                            
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

Сгенерируем процедуры обратного вызова и сохраним их в файле. Запустим документ на выполнение. Ожидаем, что на уже существующую вкладку «Вкладка1» добавится новая группа. Попробуем запустить документ (F9). Запустили? Вот те на…

Вместо того, чтобы группа добавилась на вкладку с указанным идентификатором, мы увидели, что создалась ещё одна вкладка с тем же именем, куда и была помещена новая группа! Что же произошло не так?

По всей видимости, приложения офиса всё же различают внутри себя одинаковые идентификаторы. Очевидно, им автоматически присваиваются разные пространства имён (о которых мы говорили на первых наших уроках). Какой же из этого выход? Принудительно присвоить нашим идентификаторам одинаковое пространство имён. Для этого нам придётся произвести небольшую модификацию кода в обеих надстройках и вспомнить, что такое idQ.

Закроем Word, и удалим нашу прежнюю надстройку из папки:

    C:\Users\[ИмяПользователя]\AppData\Roaming\Microsoft\Word\STARTUP

В текущей надстройке, открытой в Ribbon XML Editor, добавим в тег customUI второй атрибут xmlns с указанием идентификатора нашего собственного пространства имён, например, МПИ (Моё Пространство Имён), и присвоим значение этому идентификатору, например, http://customui.blogspot.ru (интернет-адрес этого блога), как показано в строке ниже:

    xmlns:МПИ="http://customui.blogspot.ru"

Этим самым мы объявим новое пространство имён, в дополнение к пространству по умолчанию, которое выражалось строкой

    xmlns="http://schemas.microsoft.com/office/2006/01/customui"

Теперь в теге tab заменим атрибут id на атрибут idQ, чтобы иметь возможность включить в идентификатор вкладки префикс пространства имён, и перед идентификатором «Вкладка1» вставим этот префикс нашего нового пространства. Замечу, что как только мы добавляем новое пространство имён в тег интерфейса (customUI), оно сразу появляется в автодополнении, поэтому вставку префикса мы можем осуществить прямо из него. Итак, мы получили строку:

<tab idq="МПИ:Вкладка1" insertbeforemso="TabHome" keytip="Н" label="Полезные надстройки">

Запускаем документ на выполнение (F9) и видим нашу вкладку в интерфейсе. Она будет единственной, потому что старую нашу надстройку, которую мы ещё не модифицировали аналогичным образом, мы удалили. Переходим в редактор Бейсика (Alt+F11) и открываем в нём ранее сохранённые нами шаблоны процедур обратного вызова для этой надстройки. Затем заполняем их следующим образом:

'НайтиИЗаменить (компонент: button, атрибут: onAction), 2007
Sub НайтиИЗаменить(findString As String, replaceString As String)
    With Selection.Find
        .ClearFormatting
        .Replacement.ClearFormatting
        .Text = findString
        .Replacement.Text = replaceString
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
        .Execute Replace:=wdReplaceAll
    End With
End Sub

'ДублироватьТекущуюСтроку (компонент: button, атрибут: onAction), 2007
Sub ДублироватьТекущуюСтроку(control As IRibbonControl)
    With Selection
        .HomeKey Unit:=wdLine
        .MoveDown Unit:=wdLine, Count:=1, Extend:=wdExtend
        .Copy
        .HomeKey Unit:=wdLine
        .PasteAndFormat (wdFormatOriginalFormatting)
    End With
End Sub

'УдалитьСдвоенныеПустыеСтроки (компонент: button, атрибут: onAction), 2007
Sub УдалитьСдвоенныеПустыеСтроки(control As IRibbonControl)
Dim NumCharsBefore As Long, NumCharsAfter As Long
    Do
        NumCharsBefore = ActiveDocument.Characters.Count
        Call НайтиИЗаменить("^p^p^p", "^p^p")
        NumCharsAfter = ActiveDocument.Characters.Count
    Loop Until NumCharsBefore = NumCharsAfter
End Sub

'УдалитьПустыеСтроки (компонент: button, атрибут: onAction), 2007
Sub УдалитьПустыеСтроки(control As IRibbonControl)
Dim NumCharsBefore As Long, NumCharsAfter As Long
    Do
        NumCharsBefore = ActiveDocument.Characters.Count
        Call НайтиИЗаменить("^p^p", "^p")
        NumCharsAfter = ActiveDocument.Characters.Count
    Loop Until NumCharsBefore = NumCharsAfter
End Sub

В этих функциях мы реализовываем функционал кнопок. Мы можем увидеть здесь уже знакомую нам по первой надстройке функцию «НайтиИЗаменить», а также функции обратного вызова для кнопок. Функция дублирования строки создана методом записи макроса с последующей небольшой корректировкой, а две оставшиеся функции в цикле меняют одно количество символов абзаца (^p) на другое, в зависимости от задачи.

Замечу, что предложенная реализация функций не является эталонной. Напротив, это первое и самое простое, что пришло в голову. Напомню, что задачей этих уроков является построение интерфейса, а не программирование на VBA.

Сохраним код, перейдём в окно документа и проверим работу кнопок. Если всё работает так, как надо, то сохраняем документ как шаблон с поддержкой макросов (.dotm) в папку:

    C:\Users\[ИмяПользователя]\AppData\Roaming\Microsoft\Word\STARTUP

Эта надстройка готова. В Word пока не включаем её, чтобы не мешалась. Теперь открываем в Ribbon XML Editor документ со старой надстройкой, и правим её аналогично новой. Добавляем то же самое пространство имён, у вкладки меняем атрибут id на idQ и добавляем наш префикс перед идентификатором. Запускаем документ (F9), проверяем функционал и сохраняем его как шаблон рядом со второй надстройкой в папке

    C:\Users\[ИмяПользователя]\AppData\Roaming\Microsoft\Word\STARTUP

Закрываем сохранённый шаблон в Word и открытый документ в Ribbon XML Editor. Обе надстройки готовы к работе. Запускаем Word, лезем в настройки, и включаем обе надстройки, установив напротив них галочки. После сохранения изменений в настройках у нас появляется одна вкладка, содержащая две группы, сформированные разными надстройками! То, что нам и было нужно.

7 комментариев:

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

    ОтветитьУдалить
    Ответы
    1. Ну вообще, у групп тоже есть атрибуты insertBeforeMso и insertAfterMso.

      Удалить
    2. А также insertBeforeQ и insertAfterQ.

      Удалить
  2. Что-то не получается. А какие значения необходимо задать этим атрибутам?

    ОтветитьУдалить
    Ответы
    1. Каким именно атрибутам? В статье приведён полный код. А в xml-разметке в качестве значения атрибута просто прописывается имя функции.

      Удалить