среда, 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, задающего макрос, срабатывающий при нажатии на элемент, в котором этот атрибут прописан.

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

  1. С установкой текста при загрузке приложения - понятно. А как получить значение, например editBox, в VBA? Как работает OnChange?

    ОтветитьУдалить
    Ответы
    1. OnChange так и работает - выполняется при изменении поля. Его и используем для получения значения:

      В XML у editBox пишите onChange="onChangeText"

      В VBA получаете процедуру обратного вызова:

      Sub onChangeText(control As IRibbonControl, text As String)
      myText = text
      End Sub

      Вначале кода объявляете глобальную переменную:

      Dim myText As String

      Вот и всё!

      Удалить
    2. Спасибо!

      Удалить
  2. Создал динамический атрибут getEnabled для кнопки. При загрузке все срабатывает, но при изменениях в документе уже не хочет. Можно как-то сделать, чтобы динамический атрибут реагировал на изменения в документе?
    К примеру для Excel-я: если в ячейке A1 значение 1, то кнопка активна, если 2, то не активна. Как-то так. Заранее благодарен за ответ.

    ОтветитьУдалить
    Ответы
    1. Может, запускать макрос из события Worksheet_Change, отслеживая изменения ячейки?
      (https://support.microsoft.com/ru-ru/kb/213612)

      Удалить
    2. Анонимный28 июля, 2017 01:14

      Если ещё актуально...
      Работает.

      http://www.excel-vba.ru/forum/index.php?topic=4300.0

      http://www.xtremevbtalk.com/excel/312119-ribbonx-update-editbox-text-worksheet-range-button-custom-tab.html

      Удалить
    3. Спасибо. Думаю, для кого-то в любом случае будет актуально.

      Удалить