воскресенье, 27 апреля 2014 г.

Excel COM Add-in и Ribbon

Введение

В предыдущей статье был описан процесс создания COM надстройки и её регистрации в системе. Однако сама надстройка мало полезна без пользовательского интерфейса. В данной статье речь пойдет о создании пользовательского Ribbon интерфейса COM надстройки. За основу взят проект, описанный в предыдущей статье.

Создание Ribbon интерфейса

Первым делом добавим к проекту ссылку на сборку office.dll. В данном случае мы используем версию сборки 12.0.0.0, что означает что сборка используется для работы с Excel 2007.


Заметим также, что эта сборка успешно работает и с новыми версиями Excel, включая 2010 и 2013. Сборки более ранних версий не имеют возможности для создания Ribbon интерфейсов.
Ключевым элементов этой сборки является интерфейс IRibbonExtensibility, который собственно и позволяет создавать Ribbon элементы.

[ComVisible(true)]
public class ComAddin : IDTExtensibility2, IRibbonExtensibility
{
    public string GetCustomUI(string ribbonID)
    {
        throw new NotImplementedException();
    }

    ...
}

Единственным методом является GetCustomUI который возвращает строку, содержащую описание Ribbon интерфейса в виде XML. Excel, в свою очередь, при загрузке надстройки обновит Ribbon интерфейс на основе этого описания.
Visual Studio не располагает дизайнером для создания Ribbon интерфейсов (если речь не идет о VSTO), поэтому создадим описание элементов интерфейса вручную. Для этого добавим к проекту XML файл.


 Для вновь созданного файла установим свойство Build Action в значение Resource.


После чего добавим к проекту файл ресурсов.


Откроем вновь созданные файл ресурсов и перетащим в открывшееся окно наш XML файл.


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

public string GetCustomUI(string ribbonID)
{
    return Resources.Ribbon;
}

Таким образом, метод возвращает строку, представляющую собой пользовательский интерфейс в виде XML. Осталось самое главное - описать элементы интерфейса.

<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <ribbon>
    <tabs>
      <tab id="myTab" label="First COM Add-in">
        <group id="myGroup" label="Ribbon">
          <button id="buttonLarge" size="large" label="Large Button" imageMso="CustomActionsMenu" />
          <separator id="separator1" />
          <box id="box" boxStyle="vertical">
            <button id="buttonSmall" label="Small button"/>
            <toggleButton id="buttonToggle" label="Toggle" />
            <checkBox id="checkbox" label="Checkbox"/>
          </box>
          <separator id="separator2"/>
          <menu id="menuLarge" size="large" label="Large Menu" imageMso="HappyFace">
            <button id="menuButton1" label="Menu Item 1"/>
            <button id="menuButton2" label="Menu Item 2"/>
            <button id="menuButton3" label="Menu Item 3"/>
          </menu>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

Разберемся подробнее. Корневым элементом является customUI, в качестве атрибута xmlns принимающий значение "http://schemas.microsoft.com/office/2006/01/customui". Namespace указывает на причастность к Excel 2007. Как уже писалось выше, этот интерфейс будет работать и в более свежих версиях Excel. Однако для доступа к новым функциям, таким как активация Ribbon вкладки при запуске Excel или авто-масштабирование, появившимся в Excel 2010, необходимо использовать namespace "http://schemas.microsoft.com/office/2009/07/customui" и, соответственно, сборку office.dll с номером версии 14.
Элемент tab представляет новую вкладку с именем "First COM Add-in". Заметим, что идентификатор id является обязательным для всех элементов интерфейса. В случае отсутствия идентификатора у элемента, сам элемент отображен не будет. При этом никакого сообщения об ошибки не выведется.
Каждая вкладка может содержать несколько групп элементов. Группы, в свою очередь, содержат сами элементы управления. В нашем случае мы добавили одну группу с именем "Ribbon".

<tab id="myTab" label="First COM Add-in">
  <group id="myGroup" label="Ribbon">
  ...
  </group>
</tab>

Элемент button, как понятно из названия, представляет собой кнопку. Атрибут size задает размер кнопки ("large" или "normal"). Атрибут label задает текст кнопки. Для установки изображения кнопки, в данном случае используется атрибут imageMso, которые указывает на одну из встроенных иконок. Для задания собственной иконки используется атрибуты image или getImage.

<button id="buttonLarge" size="large" label="Large Button" imageMso="CustomActionsMenu" />

Элемент box представляет собой группу элементов, расположенных вертикально или горизонтально. Способ расположения элементов указывается с помощь атрибута boxStyle. Элемент toggleButton представляет собой западающую кнопку, в то время как checkbox - это обычный флажок.

<box id="box" boxStyle="vertical">
  <button id="buttonSmall" label="Small button"/>
  <toggleButton id="buttonToggle" label="Toggle" />
  <checkBox id="checkbox" label="Checkbox"/>
</box>

Элемент menu представляет собой кнопку с выпадающим меню. В данном случае меню состоит из 3-х элементов.

<menu id="menuLarge" size="large" label="Large Menu" imageMso="HappyFace">
   <button id="menuButton1" label="Menu Item 1"/>
   <button id="menuButton2" label="Menu Item 2"/>
   <button id="menuButton3" label="Menu Item 3"/>
</menu>

После регистрации обновленной надстройки, Excel добавит новую вкладку на панель.


Callback методы

Ribbon добавлен, однако обработка событий элементов управления не происходит. Для этого необходимо описать callback методы, т.е. методы класса, которые будут вызваны при возникновении того или иного события.

<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <ribbon>
    <tabs>
      <tab id="myTab" label="First COM Add-in">
        <group id="myGroup" label="Ribbon">
          <button id="buttonLarge" size="large" imageMso="CustomActionsMenu" getLabel="OnGetLabel" onAction="OnLargeButtonClick" />
          <separator id="separator1" />
          <box id="box" boxStyle="vertical">
            <button id="buttonSmall" label="Small button" onAction="OnSmallButtonClick"/>
            <toggleButton id="buttonToggle" label="Toggle" onAction="OnToggleClick" />
            <checkBox id="checkbox" label="Checkbox" onAction="OnCheckBoxClick"/>
          </box>
          <separator id="separator2"/>
          <menu id="menuLarge" size="large" label="Large Menu" imageMso="HappyFace">
            <button id="menuButton1" label="Menu Item 1" onAction="OnMenuButtonClick"/>
            <button id="menuButton2" label="Menu Item 2" onAction="OnMenuButtonClick"/>
            <button id="menuButton3" label="Menu Item 3" onAction="OnMenuButtonClick"/>
          </menu>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

Для описания callback методов используются атрибуты начинающиеся, как правило, со слова "on" или "get". При этом атрибут задает имя метода, который будет вызван при возникновении события. Требования к callback методам следующие:
  1. Идентификатор доступа должен быть public;
  2. Имя метода должно совпадать со значением, установленном в соответствующем атрибуте, включая регистр;
  3. Сигнатура метода должна соответствовать типу события.
Одно и то же событие имеет разную сигнатуру callback методов. Так, событие onAction для элемента button требует следующую сигнатуру callback метода

void OnAction(IRibbonControl control)

Для элементов checkbox и togglebutton, сигнатура метода должна быть

void OnAction(IRibbonControl control, bool pressed)

Список всех callback методов и их сигнатур доступен по ссылке.

Почти все callback методы получают объект типа IRibbonControl, которые содержит информацию об элементе, инициировавшем событие. Свойства Id, например, возвращает идентификатор элемента. Это свойство полезно, в случае, если несколько элементов используют один callback метод, таким образом можно определить какой именно элемент сгенерировал событие.

Добавим реализацию callback методов.

public string OnGetLabel(IRibbonControl control)
{
    switch (control.Id)
    {
        case "buttonLarge":
            return "Large button";
        default:
            return null;
    }
}

public void OnLargeButtonClick(IRibbonControl control)
{
    MessageBox.Show("Large Button Click");
}

public void OnSmallButtonClick(IRibbonControl control)
{
    MessageBox.Show("Small Button Click");
}

public void OnCheckBoxClick(IRibbonControl control, bool pressed)
{
    MessageBox.Show("Checkbox Click: " + (pressed ? "checked" : "unchecked"));
}

public void OnToggleClick(IRibbonControl control, bool pressed)
{
    MessageBox.Show("Toggle Button Click: " + (pressed ? "checked" : "unchecked"));
}

public void OnMenuButtonClick(IRibbonControl control)
{
    MessageBox.Show("Menu Button Click: " + control.Id);
}

Событие getLabel (callback метод OnGetLabel) срабатывает при перерисовки Ribbon интерфейса и возвращает текст для соответствующего элемента. В нашем случае мы проверяем элемент управления, инициировавший событие, и возвращаем текст для этого элемента. Этот метод полезен если необходимо добавить поддержку нескольких языков и отображать разный текст на разных языках.

public string OnGetLabel(IRibbonControl control)
{
    switch (control.Id)
    {
        case "buttonLarge":
            return "Large button";
        default:
            return null;
    }
}

Событие onAction для кнопки срабатывает при её нажатии. В качестве единственного аргумента callback метод получает объект типа IRibbonControl.

public void OnLargeButtonClick(IRibbonControl control)
{
    MessageBox.Show("Large Button Click");
}

Для элементов togglebutton и checkbox, callback метод также содержит переменную типа bool, которая указывает, какое состояние принял элемент.

public void OnCheckBoxClick(IRibbonControl control, bool pressed)
{
    MessageBox.Show("Checkbox Click: " + (pressed ? "checked" : "unchecked"));
}

Соберем проект и зарегистрируем обновленную сборку. При тестировании в Excel видим обработку событий для элементов управления.


С исходниками проекта и готовой надстройкой можно ознакомиться по ссылке.

Комментариев нет:

Отправить комментарий