четверг, 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 марта 2015 г.

Урок 10. Создание простейшей надстройки.

Конструирование интерфейсов чаще всего используется при создании надстроек. Надстройка представляет собой невидимый документ со встроенным VBA-кодом и с собственными дополнениями к интерфейсу, автоматически открываемый приложением при запуске. При запуске он не показывает своё тело (т.е. содержимое документа текст, рисунки и пр.), а проявляется только своими изменениями в интерфейсе и подключенными макросами. Таким образом, приложение Microsoft Office дополняет свой функционал, и позволяет редактировать другие документы, используя вновь полученные функции.

Документ надстройки для Word имеет расширение .dotm. Фактически, надстройка для Word является обычным шаблоном с поддержкой макросов. Для надстроек Excel и PowerPoint имеются отдельные расширения — .xlam и .ppam соответственно.

Замечу, что надстройки .xlam и .ppam просто так не открываются в приложениях в качестве документа для редактирования, поэтому пока такая надстройка не готова, её сохраняют как обычный документ с поддержкой макросов. А вот шаблон .dotm можно открыть в Word именно как шаблон (по крайней мере, Ribbon XML Editor это делает), поэтому его можно сохранять шаблоном и в процессе разработки надстройки. Однако, я всё же рекомендую действовать единообразно, и исходники надстроек всегда держать в формате обычных документов с поддержкой макросов, и переводить их в шаблоны или надстройки только после полного окончания их разработки.

Итак, давайте попробуем построить надстройку Word, которая будет выглядеть, как отдельная вкладка, и содержать группу с кнопками, выполняющими некоторые действия. Пусть это будут некие действия с пробелами. Замечу, что процесс создания надстройки Excel ничем не отличается от создания надстройки Word и, умея создавать одно, вы будете уметь создавать другое.

Откроем Ribbon XML Editor, откроем в нём наш подопытный документ, и в окно для 2007-го интерфейса скопируем текст интерфейса нашей надстройки:

<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="WordArtSpacingMenu" 
                        size="large" 
                        screentip="Удалить повторяющиеся пробелы" 
                        supertip="Найти и заменить все повторяющиеся пробелы одним"/>
                    <button 
                        id="ПробелыВПереносыСтрок" 
                        onAction="ПробелыВПереносыСтрок" 
                        label="Пробелы в переносы строк" 
                        keytip="ПС" 
                        imageMso="PivotExpandField" 
                        size="large" 
                        screentip="Пробелы в переносы строк" 
                        supertip="Найти и заменить все пробелы переносом строки"/>
                    <button 
                        id="ПереносыСтрокВПробелы" 
                        onAction="ПереносыСтрокВПробелы" 
                        label="Переносы строк в пробелы" 
                        keytip="СП" 
                        imageMso="PivotCollapseField" 
                        size="large" 
                        screentip="Переносы строк в пробелы" 
                        supertip="Найти и заменить все переносы строк пробелами"/>                        
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

Ознакомьтесь с кодом интерфейса. Тут всё просто. Надстройка добавляет новую вкладку, и располагает на ней группу работы с пробелами, содержащую три кнопки. Для упрощения примера, в качестве иконок для кнопок я использовал максимально подходящие встроенные изображения. Вы можете использовать вместо них свои собственные, вы это уже умеете.

Первая кнопка удаляет все повторяющиеся пробелы. Довольно полезная функция, учитывая то, что правильная вёрстка в Word вообще не предполагает идущих подряд пробелов. Вторая кнопка преобразует все пробелы в переносы строк, а третья — обратное действие, т.е. переносы строк в пробелы. Тоже нужная функция для работы с последовательностями различных лексем.

Сгенерируем функции обратного вызова (Alt+F11). Скопируем шаблоны в буфер обмена (обращаем внимание на раскладку клавиатуры во избежание появления кракозябр). Закроем окно шаблонов и запустим документ. Перейдём в редактор Бейсика (Alt+F11) и вставим взятые в буфер обмена шаблоны функций вместо наших старых функций модуля RibbonCallbacks.

Теперь осталось написать на Бейсике нужные команды. Очевидно, нам понадобится функция поиска и замены. С помощью штатной функции записи макросов я выяснил, каким кодом осуществляется поиск и замена, и организовал это в виде отдельной функции, которую мы будем использовать внутри наших функций обратного вызова.

Итак, вставляем в код новую функцию:

'НайтиИЗаменить (компонент: 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)
Dim NumCharsBefore As Long, NumCharsAfter As Long
    Do
        NumCharsBefore = ActiveDocument.Characters.Count
        Call НайтиИЗаменить("  ", " ")
        NumCharsAfter = ActiveDocument.Characters.Count
    Loop Until NumCharsBefore = NumCharsAfter
End Sub

'ПробелыВПереносыСтрок (компонент: button, атрибут: onAction), 2007
Sub ПробелыВПереносыСтрок(control As IRibbonControl)
    Call НайтиИЗаменить(" ","^p")
End Sub

'ПереносыСтрокВПробелы (компонент: button, атрибут: onAction), 2007
Sub ПереносыСтрокВПробелы(control As IRibbonControl)
    Call НайтиИЗаменить("^p", " ")
    Call НайтиИЗаменить("^w^p", "")
End Sub

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

Две последние функции ещё более элементарны. Там просто вызывается наша функция по поиску и замене, которая меняет пробел на символ абзаца (^p) или наоборот. В последнем случае мы ещё и удаляем появившийся в конце текста из-за неубирающегося символа абзаца лишний пробел (^w — чистое пространство, например, пробелы или табуляция).

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

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

Теперь добавим нашу надстройку. Закрываем всё, открываем Word, лезем в Файл -> Параметры -> Надстройки -> Управление, выбираем «Надстройки Word» и нажимаем кнопку «Перейти». В открывшемся окне на первой же вкладке нажимаем «Добавить…» и выбираем наш файл. Нажимаем «ОК», и наша надстройка начинает действовать.

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

Урок 9. Выполнение действия по нажатию кнопки.

На прошлом уроке мы познакомились с написанием функций для динамического возвращения значений атрибутов. Теперь мы напишем функцию, которая будет выполняться при нажатии на кнопку. Если мы рассмотрим все предлагаемые для button атрибуты, то увидим атрибут onAction. Именно в этом атрибуте и указывается функция, которая будет выполняться при нажатии на эту кнопку.

Добавим кнопке атрибут:

onAction="ОтобразитьПриветствие"

Сгенерируем шаблоны функций (Alt+F11), проконтролируем текущую раскладку, чтобы была русская, и скопируем в буфер шаблон для функции «ОтобразитьПриветствие». Затем закроем окно, запустим документ на выполнение, нажмём в Word Alt+F11, и вставим скопированное рядом нашей предыдущей с функцией. Внутри функции пишем команду вывода сообщения:

MsgBox "Приветствую тебя, мой повелитель!"

Сохраняем, всё закрываем, затем снова запускаем документ. Нажимаем на кнопку и получаем удовольствие.

Вместо вывода сообщения можно вызвать любую имеющуюся в Word команду. Выше я упоминал, что штатной кнопке нельзя присвоить внешнее изображение. Но можно создать свою кнопку, присвоить ей внешнее изображение, а в onAction прописать команду, выполняющуюся при нажатии штатной кнопки.

Давайте повесим на нашу кнопку команду открытия редактора Бейсика. Запускаем документ, переходим в редактор Бейсика, и правим наши функции. Сначала в функции «Поприветствовать» меняем название кнопки на, например, «Открыть редактор Бейсика» (можем изменить и имя самой функции, но тогда надо не забыть поменять его также и в атрибуте onAction). Затем в функции «ОтобразитьПриветствие» комментируем апострофам строку вывода приветствия, и пишем вместо неё строку:

ShowVisualBasicEditor = True

Сохраняем, закрываем редактор Бейсика, нажимаем на кнопку и… снова оказываемся в редакторе! Всё, как и задумывалось!

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

Замечу, что обучение записи макросов и программированию на VBA не входит в задачи наших уроков, но всё это очень легко осваивается самостоятельно. Особенно запись макросов. Для неё даже предусмотрена группа «Макросы» на вкладке «Вид» или группа «Код» с кнопкой «Запись макроса» на вкладке «Разработчик».

На сегодня закончим. В следующем уроке мы перейдём к важной теме - созданию надстроек.

среда, 4 марта 2015 г.

Урок 8. Динамические атрибуты.

Помимо статических атрибутов существуют т.н. динамические атрибуты. Они позволяют задавать в своём значении имя внешней функции, которая сама предоставит нужное значение атрибута. Функции пишутся на языке VBA (Visual Basic for Applications), что переводится, как «Визуальный Бейсик для приложений». Это именно тот язык программирования, на котором в приложениях Microsoft Office записываются макросы, о которых вы уже, несомненно, слышали. Код функций хранится внутри документа и редактируется встроенным в приложение редактором Бейсика.

Функции, имена которых передаются элементу интерфейса, как в нашем случае, называются функциями обратного вызова. Смысл названия заключается в том, что вызов функции осуществляется не из кода программы, а как бы с обратной стороны, из интерфейса. То есть, основной код программы всего лишь передаёт имя функции в интерфейс в качестве параметра, а интерфейс вызывает её только тогда, когда ему самому это нужно.

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

Как правило, динамические атрибуты начинаются со слова get (получить). Фактически, эти атрибуты являются парными для статических, то есть, задают один и тот же параметр. Только в одном случае он задаётся сразу в XML-тексте, т.е. статически, а в другом возвращается из функции в качестве результата её работы, непосредственно в момент запуска документа, то есть, динамически.

Давайте рассмотрим один пример. Откроем Word, создадим документ, и сохраним его, как документ с макросами, т.е. с расширением .docm. Именно этот тип документа позволит нам сохранять в нём код VBA-функций. Закроем Word, откроем Ribbon XML Editor, и откроем в нём только что созданный нами файл. Напишем код аналогичный тому, что мы создавали раньше: создадим новую вкладку, в неё поместим группу, а в группу — кнопку:

<customui xmlns="http://schemas.microsoft.com/office/2006/01/customui">
    <ribbon startFromScratch="false">
        <tabs>
            <tab id="Вкладка1" insertBeforeMso="TabHome" label="Моя вкладка">
                <group id="Группа1" label="Моя группа">
                    <button id="Кнопка1" image="Рука" keytip="ХАЙ" label="Поприветствовать" screentip="Вывести окно приветствия" size="large" supertip="Вывести окно приветствия для получения удовольствия"/>
                </group>
            </tab>
        </tabs>
    </ribbon>
</customui>

Запустим документ на выполнение, и проверим, что код работает правильно. Теперь мы попробуем заменить статический атрибут label у кнопки на его динамический аналог — getLabel. Но вначале изменим кое-какую настройку Ribbon XML Editor.

После запуска документа на выполнение мы будем писать код на VBA через редактор Бейсика, встроенный в Word, и чтобы этот код мог сохранится в документе, нам следует перед его сохранением закрывать документ в Ribbon XML Editor. Иначе, очевидно, что последний его перезатрёт уже при своём сохранении, ведь в Ribbon XML Editor документ был открыт раньше, чем в Word.

Чтобы делать всё автоматически, откроем страницу настроек Ribbon XML Editor и в разделе «Документы» установим галочку на пункте «Закрывать документ перед запуском его в MS Office». Удостоверимся, что следующая галочка, «Открывать документ при закрытии его в MSOffice», тоже установлена, и вернёмся на страницу кода.

Посмотрим, как это работает. Запустим документ на выполнение (F9). Документ запустится. Переключимся на приложение Ribbon XML Editor, и убедимся, что документ там закрылся. Снова вернёмся к Word. Закроем документ или весь Word целиком. Тотчас Ribbon XML Editor выйдет на передний план и автоматически откроет документ. Таким образом, мы можем попеременно редактировать и XML и VBA, не боясь, что сохранение документа в одном редакторе перезатрёт изменения, сделанные в другом редакторе.

Теперь находим атрибут label и установив на него курсор, и нажав Ctrl+Пробел сменим его на getLabel. В качестве значения впишем имя функции (можем оставить в качестве такового старое значение, пусть теперь это будет именем функции, оно вполне подходит для этого).

Теперь нам надо написать соответствующую функцию на VBA. Чтобы упростить программистам жизнь, Ribbon XML Editor может сгенерировать шаблон для этой функции, чтобы потом только осталось её заполнить. Для этого нажмём Alt+F11 или кнопку в правой части верхней панели:



Откроется окно с шаблоном нашей функции. Его можно заполнить прямо здесь, но лучше воспользоваться специализированным редактором Бейсика. Для этого вначале сохраним шаблон функции в файл: нажмём кнопку с изображением дискетки в верхней панели окна шаблонов Ribbon XML Editor. Предложенное имя файла RibbonCallbacks.bas оставим без изменений.

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

Переходим в редактор Бейсика. Для этого нажимаем ту же комбинацию Alt+F11, но уже в Word. В качестве альтернативы можно включить в настройках Word вкладку разработчика, переключиться на неё и нажать кнопку «Visual Basic» в группе «Код».

В открывшемся редакторе Бейсика нажимаем Ctrl+M, или выбираем в меню «File -> Import Faile…». Находим сохранённый из Ribbon XML Editor файл с шаблонами (RibbonCallbacks.bas) и открываем его. В документ в раздел Modules добавляется новый модуль RibbonCallbacks (смотрите его слева, в структуре проекта). Открываем папку модулей, делаем двойной щелчок на модуле RibbonCallbacks и видим текст нашего шаблона.

Теперь вписываем внутрь функции команду возврата имени кнопки:

'Кнопка1 (компонент: button, атрибут: getLabel), 2007
Sub Поприветствовать(control As IRibbonControl, ByRef label)    
    label = "Динамическая надпись"
End Sub

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

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

Сначала идёт комментарий, который на VBA начинается с апострофа. Затем идёт сама функция, начинающаяся со слова Sub, и заканчивающаяся End Sub. Сразу после Sub идёт имя функции, которое мы задали в динамическом атрибуте. В скобках за именем указаны имена атрибутов, через которые функция принимает и возвращает параметры.

Первый параметр — control (ожидается тип IRibbonControl). Сюда передаётся объект нашей кнопки. Второй параметр — label. Перед ним указан способ передачи параметра — ByRef, что означает «по ссылке». То есть, в функцию передаётся не копия параметра, а ссылка непосредственно на сам объект. Объектом служит некая строка, возвращаемая функцией. То есть, именно в эту строку необходимо помещать текст, который должен возвращаться функцией.

Обычно результат зависит от каких-либо условий, но в нашем случае, чтобы не усложнять пример, мы вернули текст без всяких условий.

На этом мы закончим текущий урок. Теперь вы сами можете поупражняться в динамическом возвращении других атрибутов. Замечу, что в дальнейшем не надо сохранять шаблоны функций в файл. Когда добавляется новая функция, то просто надо найти её шаблон и cкопировать её в редактор Бейсика. При этом важно помнить, что редактор Бейсика не поддерживает Unicode, поэтому при копировании следите за тем, чтобы при взятии текста в буфер обмена была включена та раскладка клавиатуры, на которой вы пишите комментарии. Иначе система не сможет распознать кодовую страницу забираемого в буфер текста комментариев, и вставит кракозябры. То же относится и к именам функций, если они написаны не на латинице.

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

понедельник, 2 марта 2015 г.

Урок 7. Надписи и всплывающие подсказки. Пример стандартного меню.

Давайте добавим к созданной нами на прошлом уроке кнопке полноценную всплывающую подсказку. Для этого добавим в тег button два статических атрибута: screentip="Вывести окно приветствия" и supertip="Вывести окно приветствия для получения удовольствия". Запустим документ, и посмотрим, что получилось. Теперь, при наведении мыши на кнопку, отображается всплывающая подсказка с текстом, сформированном из значений обоих атрибутов.

Что ещё можно задать в статических атрибутах для кнопки? Например, быстрые клавиши (keytip), подсказки о которых появляются в интерфейсе после кратковременного нажатия на клавишу Alt. Для одного элемента интерфейса можно задавать последовательности до трёх клавиш, хотя в большинстве случаев достаточно одной, максимум — двух, если количество элементов на вкладке достаточно велико.

Отдельно упомяну про атрибут description. В любом случае не будет ошибкой, если он тоже будет заполнен текстом, но срабатывать он будет только тогда, когда элемент с этим атрибутом будет помещён внутрь меню в качестве пункта этого меню, а атрибут itemSize самого меню будет содержать значение large. Другими словами, description актуален только внутри меню с большими пунктами. Поэтому в нашем случае заполнять его пока не будем.

Теперь я расскажу об одной интересной находке. Она будет интересна приверженцам старого классического меню. Неожиданно оригинал кода интерфейса находится (в составе предлагаемого шаблона документа) на личном сайте начальника отдела артиллерийско-технической службы подводной лодки военно-морских сил США в отставке:

http://gregmaxey.mvps.org/word_tip_pages/ribbon_menu_control.html.

Ниже можно скачать отдельно код интерфейса, переведённого мною на русский язык:
word2003UI.exportedUI

Для тестирования сохраните скачанный файл на диске, откройте в Ribbon XML Editor какой-нибудь новый документ Word и нажмите кнопку «Импортировать файл настроек ленты»:



Выберите скачанный файл. Его текст вставится в текущее окно. Первая строчка будет закомментирована в целях совместимости с типом файла «.exportedUI». Раскомментируйте её (для этого просто выделите весь комментарий целиком и нажмите кнопку «Комментарий» в строке над окном, четвёртую слева). Всё, теперь можете запустить документ на выполнение и насладиться видом классического меню.

В качестве альтернативного варианта можно просто открыть скачанный файл в блокноте, и скопировать его содержимое непосредственно в окно Ribbon XML Editor, но зачем делать лишние телодвижения?

Самостоятельно рассмотрите код. Он весьма прост. В нём создаются пользовательские меню, помещённые в горизонтальный контейнер (box) в которые посредством атрибута idMso вставляются стандартные элементы. Ниже контейнера в два ряда располагаются группы кнопок, имитирующие классические панели инструментов. Ничего более сложного в коде не производится.

Замечу, что на вышеупомянутом сайте американского офицера можно найти очень много полезной и интересной информации для новичков, владеющих английским. Всё очень толково и грамотно, с иллюстрациями, разложено по полочкам.

воскресенье, 1 марта 2015 г.

Урок 6. Работа с внешними изображениями

На прошлом уроке я упоминал о том, что на встроенных (штатных) кнопках можно использовать только встроенные изображения, уже имеющиеся в офисе. Если же мы создадим свою кнопку, то сможем разместить на ней как встроенное, так и собственное, внешнее изображение. Давайте попробуем это сделать, добавить в интерфейс внешнее изображение.

Откроем проект прошлого урока и добавим в нашу группу вторую кнопку. Пусть кнопка должна будет в итоге выводить на экран окно с приветствием. Поскольку кнопка будет самодельная, вместо idMso для задания идентификатора используем id. В качестве идентификатора укажем произвольное слово, например, «Приветствие»:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
    <ribbon startFromScratch="false">
        <tabs>
            <tab id="Вкладка1" label="Моя вкладка" insertBeforeMso="TabHome">
                <group id="Группа1" label="Моя группа">
                    <button idMso="DeleteWord"/>
                    <button id="Приветствие" label="Поприветствовать" />
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

Теперь запустим документ на выполнение и посмотрим, что получилось. Мы увидим, что справа от нашей большой кнопки, которую мы поместили туда ранее, появилась маленькая, со словом «Поприветствовать». Поскольку изображение ей мы ещё не присвоили, кнопка будет содержать только надпись.

Теперь займёмся изображением. В идеале мы должны создать иконку 32x32, которая бы хорошо смотрелась не только в своём истинном размере, но и при автоматической интерполяции до 16x16. Предпочтительный формат файла изображения, конечно, «png», поскольку он поддерживает градацию прозрачности, что позволит нам избежать рваных краёв у рисунка при его отображении на кнопке.

Само создание иконки в графическом редакторе выходит за рамки нашей темы. В качестве альтернативы, можно поискать нужные иконки в Интернете. Чтобы не нагружать читателя лишними заботами, я выложу пример иконки здесь. Скачайте её и сохраните на диске:

Чтобы вставить изображение в код, нужно, чтобы оно уже находилось внутри структуры документа. На втором уроке я упоминал о паре мест внутри документа, которые нас будут интересовать при использовании внешних изображений. Эти места отображаются на вкладках «Ссылки на изображения» и «Типы файлов».

Поскольку изображений в документе ещё нет, редактор вкладки «Ссылки на изображения» будет пуст. Он будет отображать содержимое файла CustomUI.xml.rels с сылками на импортированные изображения, который будет располагаться внутри документа в папке customUI\_rels. Но пока этого файла тоже нет. Всю эту структуру, а также папку images для хранения изображений внутри документа, Ribbon XML Editor создаст автоматически, в процессе импорта внешнего изображения.

Давайте импортируем изображение в документ. Для работы с внешними изображениями в интерфейсе редактора предусмотрен специальный блок, состоящий из кнопки импорта файла изображения, комбобокса выбора изображения для вставки его идентификатора в текст, кнопки вставки идентификатора и кнопки удаления файла изображения из документа:



На рисунке блок работы с изображениями выделен красной рамкой. Нажмём первую кнопку блока для выбора изображения для импорта, и выберем скачанный и сохранённый ранее файл Рука.png. Его изображение и идентификатор тотчас добавится в комбобокс. Изображение уже в документе.

Что изменилось на вкладках «Ссылки на изображения» и «Типы файлов». На первой из этих вкладок появился следующий текст:

<?xml version="1.0"?>
<relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <relationship id="Рука" target="images/xTCxUBxU2xTS.png" type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image">
</relationship></relationships>

Внутри корневого тега Relationships содержится тег Relationship с сылкой на только что импортированное изображение. Бегло пройдёмся по атрибутам этого тега. C атрибутом id всё понятно — это идентификатор изображения, который будет применяться в тексте. А вот дальше следует атрибут target, указывающий расположение файла внутри папки интерфейса, но вместо имени файла мы видим странный набор символов. Объясняется это просто — офис не поддерживает кириллицу в именах файлов изображений, и поэтому Ribbon XML Editor автоматически кодирует кириллистические символы 36-ричными кодами, содержащими только цифры и латиницу.

Последним атрибутом ссылки является type — тип ссылки. Тип записывается наподобие пространства имён — интернет-адресом, по которому, на момент написания урока, ничего нет. Из его записи мы можем лишь понять, что рассматриваемый тег является ссылкой на изображение, что мы уже и так знаем.

На второй из рассматриваемых вкладок отображается файл [Content_Types].xml, который тоже подвергается некоторым изменениям во время импорта изображений. Файл находится в корне структуры документа и содержит перечисление типов объектов, помещённых в документ. Он уже содержит ряд объектов, а при добавлении изображения в него помещается строка с указанием типа файла изображения в привязке к его расширению. В нашем случае мы добавляли файл «png», поэтому в список добавилась соответствующая строка:

<default contenttype="image/png" extension="png"/>

При добавлении следующего изображения с тем же расширением редактор обнаружит наличие этой строки, и не будет уже добавлять новую. А вот если добавить файл другого формата, то в текст вставится новая строка, соответствующая новому формату файла.

Тут надо упомянуть про форматы изображений, поддерживаемых офисом. Процитирую справку Ribbon XML Editor с перечислением форматов, которые поддерживаются этим редактором для импорта:
  1. png (тип image/png) — поддерживает градацию прозрачности.
  2. gif (тип image/gif) — поддерживает единственный уровень прозрачности — полную прозрачность.
  3. bmp (конвертируя в png) — может поддерживать градацию прозрачности (32-битный формат)
  4. ico, icon (тип image/x-icon) — поддерживает градацию прозрачности.
  5. jpg, jpeg, jpe, pjpeg (тип image/jpeg) — не поддерживает прозрачность.
  6. tif, tiff (тип image/tiff) — поддерживает градацию прозрачности.
  7. wmf (тип image/x-wmf) — векторный формат, поддерживает прозрачность.
  8. emf (тип image/x-emf) — усовершенствованный wmf, поддерживает прозрачность.
Практически интерес представляют только первые четыре формата. Остальные, как правило, для интерфейсных изображений не применяются. Отдельно скажу про формат «bmp», опять же процитировав справку:

«Несмотря на то, что интерфейс офисных программ понимает файлы формата .bmp, он отображает их без учёта прозрачности (в случае 32-битного bmp) и без принудительного вырезания фона, поэтому Ribbon XML Editor при импорте конвертирует такие файлы в формат .png либо копируя канал прозрачности в случае 32-битного .bmp, либо вырезая фон, определяя его, как пиксели, цвет которых совпадает с цветом левого нижнего пикселя.»

Так что, если у вас есть коллекция иконок в формате «bmp», никто не запрещает их применять. Но я всё же рекомендую использовать именно формат «png», как наиболее приспособленный для этого дела.

Итак, изображение у нас в документе. Дело осталось за малым — сослаться на него в коде интерфейса. Для этого ставим курсор в конец атрибутов тега кнопки, и нажимаем кнопку «Вставить ссылку на изображение в текущую позицию курсора». В тег вставляется атрибут image со значением идентификатора картинки. Происходит это так же, как и при вставке встроенного изображения из галереи в атрибут imageMso.

Запускаем документ на выполнение, и любуемся красивой кнопкой. При желании можем тоже сделать её большой, добавив атрибут size с указанием в нём значения large.

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

пятница, 27 февраля 2015 г.

Урок 5. Работа со встроенными изображениями

Давайте создадим новый проект. Откроем в Ribbon XML Editor новый документ и вставим вкладку и группу, как мы уже делали на предыдущем уроке. Затем вставим в группу кнопку (button):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
    <ribbon startFromScratch="false">
        <tabs>
            <tab id="Вкладка1" label="Моя вкладка" insertBeforeMso="TabHome">
                <group id="Группа1" label="Моя группа">
                    <button idMso="DeleteWord"/>
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

Пусть кнопка будет удалять текущее слово с позиции курсора до конца. Идентификатор этой кнопки мы можем посмотреть в настройках ленты в приложении Word. Запустим документ, посмотрим на результат. По умолчанию, эта кнопка не содержит изображения, и потому выглядит в интерфейсе не очень симпатично. Давайте добавим кнопке изображение.

Microsoft Office уже содержит в себе несколько тысяч изображений. Надо только узнать идентификатор наиболее подходящего, и вставить его в атрибут imageMso. Довольно нетривиальная задача, не правда ли? К счастью, Ribbon XML Editor имеет в своём распоряжении галерею всех встроенных в офис иконок, которая позволяет осуществить выбор и вставку понравившегося изображения.

Ставим курсор после последнего атрибута кнопки перед закрывающими тег символами, и нажимаем кнопку галереи:



Откроется галерея иконок, содержащая несколько вкладок. На каждой вкладке по 500 изображений. При наведении мыши на каждое изображение появляется всплывающая подсказка с его идентификатором:



Выберем понравившееся изображение, и нажмём на него. Окно закроется, а в позицию курсора вставится атрибут imageMso со значением идентификатора выбранного изображения. Проверим результат, нажав F9 для запуска документа. Я использовал идентификатор WatermarkRemove. Кнопка стала выглядеть намного лучше.

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

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

Теперь давайте изменим размер кнопки. Для этого пропишем атрибут size:

<button idMso="DeleteWord" imageMso="WatermarkRemove" size="large">

Атрибут size может принимать только два значения, которые нам и предлагает автодополнение: normal и large. По умолчанию кнопка DeleteWord имеет размер normal. После изменения на large и запуска документа на выполнение мы увидим, что увеличилась не только сама кнопка, но и изображение на ней. Теперь вместо иконки 16х16 автоматически используется иконка 32x32. При этом интерполяции не происходит — как правило, офис содержит в себе оба размера иконок.

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

среда, 25 февраля 2015 г.

Урок 4. Ещё несколько полезных атрибутов.

Ранее мы уже использовали такие атрибуты для элементов интерфейса, как id, idMso и label. Всё это были статические атрибуты, то есть атрибуты, значения которым мы присваиваем сразу, а не получаем их в процессе работы офисного приложения, как это было бы в случае использования динамических атрибутов, о которых мы поговорим позже. Давайте рассмотрим другие полезные статические атрибуты, которые мы можем использовать для должного оформления интерфейса.

Давайте переместим нашу вкладку с конца ленты в её начало, поставив её перед самой первой вкладкой «Главная». Для этого вставим в элемент новый атрибут insertBeforeMso (вставить перед…) и присвоим ему идентификатор вкладки «Главная».

Как узнать идентификатор вкладки «Главная»? Обычно для определения идентификаторов различных элементов в офисном приложении открывают настройки ленты, наводят курсор на нужный элемент в списке, и читают всплывающую подсказку. Но вот на именах вкладках и групп подсказок почему-то нет. Как же быть? Элементарно! Открываем встроенную справку программы Ribbon XML Editor, и на её главной странице в списке приложений к справке находим пункт «Word — идентификаторы всех вкладок и групп». Открываем и вуаля! Замечу, что более полного списка идентификаторов вкладок и групп я не встречал больше нигде.

Установим курсор в любое свободное место внутри тега tab в области атрибутов, чтобы слева и справа были пробелы. Нажмём Ctrl+Пробел и выберем в списке атрибут insertBeforeMso. Выбранный атрибут вставится в текст. Затем наберём знак «=». Вы увидите, как тут же после знака автоматически добавятся кавычки, и курсор установится внутри них. Снова нажмём Ctrl+Пробел и выберем из предлагаемых идентификаторов вкладок найденный в справке идентификатор TabHome, который соответствует вкладке «Главная».

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

Замечу, что количество набранных букв до показа списка автодополнения можно регулировать в настройках программы. На первой вкладке настроек в разделе «Автодополнение» находится три маленькие странички настроек. Первая — это общие настройки для всех списков, а следующие две относятся к длинным и коротким спискам. Короткие списки — это списки тегов, атрибутов или их значений, если эти значения — не идентификаторы. Такие списки удобно видеть целиком, без сокращения по мере набора. Длинные списки — это списки идентификаторов, которые обычно очень большие, и их полезно сокращать, принимая во внимание уже набранную часть идентификатора.

Проверим, что у нас получилось. Запустив документ на выполнение, мы увидим, что наша вкладка переместилась в самое начало, и расположилась перед вкладкой «Главная». Аналогично работает атрибут insertAfterMso, позволяя расположить наш элемент после существующего.

Мы можем автоматически скрывать все стандартные вкладки ленты при загрузке документа, и оставлять только созданные нами. Для этого достаточно в элементе ribbon изменить значение атрибута startFromScratch (начать с чистого листа) с false на true. Не знаю, имеет ли это большой смысл, но просто запомним, что такая возможность есть.

Кроме того, можно скрывать каждую вкладку и по-отдельности, используя атрибут visible:

    <tab idMso="TabHome" visible="false"/>
    <tab idMso="TabInsert" visible="false"/>
    <tab idMso="TabPageLayoutWord" visible="false"/>
    <tab idMso="TabReferences" visible="false"/>
    <tab idMso="TabMailings" visible="false"/>

Здесь мы с помощью атрибута idMso ссылаемся на стандартные вкладки и присваиваем атрибуту visible (видимость) значение false.

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