пятница, 13 марта 2015 г.

Урок 12. Динамическое меню.

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

Давайте сделаем ещё одну надстройку-демонстратор динамического меню. Создадим третий документ и откроем его в Ribbon XML Editor. Построим в нём следующий код:

<?xml version="1.0" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" xmlns:МПИ="http://customui.blogspot.ru">
    <ribbon startFromScratch="false">
        <tabs>
            <tab idQ="МПИ:Вкладка1" label="Полезные надстройки" insertBeforeMso="TabHome" keytip="Н">
                <group id="ДинамическиеМеню" label="Демонстрация меню">
                    <menu id="Меню1" label="Обычное меню" itemSize="large">
                        <button 
                            id="Кнопка1" 
                            label="Пункт 1" 
                            description="Пункт обычного меню" 
                            onAction="Сообщение1" />
                        <button 
                            id="Кнопка2" 
                            label="Пункт 2" 
                            description="Пункт обычного меню" 
                            onAction="Сообщение2" />
                        <button 
                            id="Кнопка3" 
                            label="Пункт 3" 
                            description="Пункт обычного меню" 
                            onAction="Сообщение3" />
                    </menu>
                    <dynamicMenu 
                        id="ДинамическоеМеню1" 
                        label="Динкамическое меню" 
                        getContent="ВернутьДинамическоеМеню" />
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

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

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

Attribute VB_Name = "RibbonCallbacks"

Мы можем поменять эту строку на:

Attribute VB_Name = "CustomUICallbacks"

и при сохранении файла нам будет предлагаться уже новое имя.

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

Заполним шаблоны функций следующим кодом:

'Кнопка1 (компонент: button, атрибут: onAction), 2007
Sub Сообщение1(control As IRibbonControl)
    MsgBox "Был выбран пункт 1"
End Sub

'Кнопка2 (компонент: button, атрибут: onAction), 2007
Sub Сообщение2(control As IRibbonControl)
    MsgBox "Был выбран пункт 2"
End Sub

'Кнопка3 (компонент: button, атрибут: onAction), 2007
Sub Сообщение3(control As IRibbonControl)
    MsgBox "Был выбран пункт 3"
End Sub

'ДинамическоеМеню1 (компонент: dynamicMenu, атрибут: getContent), 2007
Sub ВернутьДинамическоеМеню(control As IRibbonControl, ByRef content)
Dim sXML As String
    sXML = "<menu itemSize=""large"" xmlns=""http://schemas.microsoft.com/office/2006/01/customui"">" & vbCrLf
    sXML = sXML & "<button id=""Кнопка1"" label=""Пункт 1"" description=""Пункт динамического меню"" onAction = ""Сообщение1""/>" & vbCrLf
    sXML = sXML & "<button id=""Кнопка2"" label=""Пункт 2"" description=""Пункт динамического меню"" onAction = ""Сообщение2""/>" & vbCrLf
    sXML = sXML & "<button id=""Кнопка3"" label=""Пункт 3"" description=""Пункт динамического меню"" onAction = ""Сообщение3""/>" & vbCrLf
    content = sXML & "</menu>" 
End Sub

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

Формируемые пункты возвращаются в динамическое меню не списком, а XML-структурой, объединенённой корневым элементом menu, имеющим тип CT_MenuRoot. Элемента этого типа нет в иерархии статических элементов. Но в справке Ribbon XML Editor среди приложений мы можем найти ссылку «Динамическое меню», нажав на которую, мы откроем страничку с описанием этого типа и дополнительными сведениями по нему.

Согласно справке, корневой элемент menu должен содержать атрибут xmlns с объявлением пространства имён, используемое нами в основном коде. Замечу, что несмотря на то, что ранее мы использовали этот атрибут только в корневом элементе customUI, теоретически его можно использовать в любом элементе интерфейса, хотя его и не будет среди предлагаемых вариантов в автодополнении ввиду редкости такого использования. В данном случае мы применяем его в элементе menu.

В функции «ВернутьДинамическоеМеню» мы присваиваем строковой переменной sXML сначала открывающий тег menu с атрибутом размера пунктов меню и объявлением пространства имён, затем добавляем туда коды перевода строки (vbCrLf), затем теги элементов пунктов меню с желаемыми атрибутами и, в завершение, присваиваем возвращаемой переменной содержимое полученной строки с добавлением закрывающего тега menu. Внутри строковых переменных знак кавычек делается сдвоенным, чтобы не было конфликта с синтаксисом Бейсика, использующего тот же знак для обозначения строк.

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

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

  1. Динамическое меню получает свой контент не при открытии документа, а при своём открытии. Именно с этим связана небольшая задержка при открытии насыщенных стандартных меню, вроде галереи стилей, номеров страниц и т.п.
    Очень хороший материал у вас. По-доброму завидую :)

    ОтветитьУдалить
    Ответы
    1. Спасибо, поправил.
      Благодарю за положительный отзыв! )

      Удалить
  2. Анонимный08 апреля, 2015 19:38

    Спасибо за полезную информацию. Есть у меня такой вопрос. Судя по всему функция ВернутьДинамическоеМеню вызывается только один раз при первом обращении. Как сделать, чтобы она вызывалась каждый раз, когда открывают динамическое меню? Это было бы полезно, когда меню зависит от каких-нибудь условий.

    ОтветитьУдалить
    Ответы
    1. Я не знаю... спросите у Александра Витера (предыдущий комментарий), он, судя по всему, знает про динамическое меню побольше моего.

      Удалить
    2. Нужно обновлять ленту методом Invalidate или отдельный элемент управления Invalidate(control.Id)
      Почитайте мой блог, посвящённый настройке интерфейса. Я как раз подвёл итог работе с динамическими меню: Элемент управления dynamicMenu

      Удалить
  3. Скажите, а как называется и настраивается элемент интерфейса в виде текстового поля с кнопками увеличить и уменьшить значение?
    Как стандартный элемент для задания интервала между строками, например

    ОтветитьУдалить
    Ответы
    1. Это который на вкладке "разметка страницы? Интересный вопрос... В распоряжении пользователя такого элемента нет. Как и, по моему, некоторых других составных элементов, имеющихся на стандартных лентах. Сами применяют, а нам не дают (((

      Удалить
    2. Нагуглил такое решение проблемы:
      https://social.msdn.microsoft.com/Forums/office/en-US/04dd2304-26e8-44dc-b2c8-2fc541738e86/how-to-add-a-spinner-spin-box-control-to-a-custom-ribbon-in-excel-2007?forum=exceldev

      Удалить
    3. Ну да, только если так - текстовое поле и справа две стандартные кнопки, расположенные горизонтально.
      Спасибо за ссылку!

      Удалить