Microsoft Office System 2007
Новый пользовательский интерфейс и возможности для разработчиков.
Часть 2
Создание проекта типа COM Add-In
Развертывание и удаление модулей расширения
Реализация интерфейса IRibbonExtensibiity
Описание интерфейса в формате XML
Использование механизма Reflection
В предыдущей части мы рассмотрели общие вопросы, связанные с настройкой пользовательского интерфейса, и один из возможных сценариев расширения интерфейса — применение формата Open XML и Visual Basic for Applications. Данный подход ориентирован на создание решений на уровне документов. Для создания решений на уровне приложений (например, Microsoft Word 2007) или группы приложений, например всех приложений, поддерживающих интерфейс на базе Ribbon, следует использовать модули дополнений — COM Add-In.
В данной части мы подробно рассмотрим базовые шаблоны, применяемые для создания модулей расширений на основе технологии COM; реализацию интерфейса IRibbonExtensibiity, который используется для создания расширений на основе новых интерфейсных элементов, реализованных в Microsoft Office 2007; способы сохранения XML-описания расширений интерфейса в модуле расширения; способы реализации обработчиков событий для интерфейсных элементов, которые мы добавляем в Ribbon.
Основные шаги по созданию модуля (Add-In), расширяющего интерфейс Office 2007:
- Используя Visual Studio 2005, создать проект типа COM Add-In.
- Добавить реализацию интерфейса IRibbonExtensibiity.
- Применить метод GetCustomUI, возвращающий XML-описание интерфейса.
- Реализовать обработчик события для интерфейсного элемента.
Создание проекта типа COM Add-In
После запуска Visual Studio 2005 выберем опцию Create –> Project, а в списке доступных шаблонов — группу Other Project Types, Extensibility и шаблон Shared Add-in (рис. 1). В свойствах проекта укажем имя нашего проекта — RibbonXDemo. Нажатие кнопки OК приведет нас к мастеру, который позволяет задать ряд характеристик нашего модуля.
Рис. 1. Шаблон проекта в Visual Studio 2005
На первом экране мастера нужно выбрать язык программирования, на котором мы будем писать код нашего расширения, — Visual C#, Visual Basic или Visual C++ с библиотекой ATL. Для нашего примера мы выберем язык C#.
Далее выберем приложение, которое будет выступать хостом для нашего расширения, — расширение может быть создано как для группы приложений, так и для одного приложения; в нашем случае это будет Microsoft Word 2007.
После этого зададим имя расширения и его описание — в нашем примере это будет RibbonXDemo и Ribbon Extensibility Demo соответственно.
Следующий экран позволяет указать, следует ли загружать расширение при запуске самого приложения (включаем эту опцию) и будет ли расширение доступно только установившему его пользователю или всем пользователям данного компьютера (также включаем эту опцию).
Последний экран показывает все заданные нами характеристики; нажатие кнопки Finish приводит к генерации кода расширения (рис. 2).
Рис. 2. Опции проекта COM Add-in
В результате действий мастера создаются два проекта: собственно проект, реализующий модуль расширения, и проект по его развертыванию.
Созданный мастером код для модуля расширения представляет собой шаблон для реализации интерфейса IDTExtensibility2, который лежит в основе всех механизмов расширения Microsoft Office.
В шаблоне мы найдем следующие части реализации этого интерфейса: конструктор Connect(), в который можно поместить инициализационный код нашего расширения; набор методов, отражающих основные этапы жизненного цикла расширения (эти методы приведены в таблице).
Метод |
Описание |
Параметры |
OnConnection() |
Получает управление при загрузке модуля расширения |
Application — корневой объект приложения-хоста; |
OnDisconnection() |
Получает управление при выгрузке модуля расширения |
DisconnectMode — способ выгрузки модуля расширения; |
OnAddInsUpdate() |
Получает управление при изменениях в коллекции модулей расширения на уровне всего хост-приложения |
Сustom — массив дополнительных параметров, специфичных для хост-приложения |
OnStartupComplete() |
Получает управление после завершения загрузки хост-приложения |
Custom — массив дополнительных параметров, специфичных для хост-приложения |
OnBeginShutdown() |
Получает управление перед началом выгрузки хост-приложения |
custom — массив дополнительных параметров, специфичных для хост-приложения |
Исходный код шаблона модуля расширения показан ниже.
namespace RibbonXDemo
{
using System;
using Extensibility;
using System.Runtime.InteropServices;
[GuidAttribute(“69B5B446-8B31-4DC1-B926-8A2D0A48EE27”),
ProgId(“RibbonXDemo.Connect”)]
public class Connect : Object, Extensibility.
IDTExtensibility2
{
public Connect()
{
}
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode,
object addInInst, ref System.Array custom)
{
applicationObject = application;
addInInstance = addInInst;
}
public void OnDisconnection(
Extensibility.ext_DisconnectMode disconnectMode,
ref System.Array custom)
{
}
public void OnAddInsUpdate(ref System.Array custom)
{
}
public void OnStartupComplete(ref System.Array
custom)
{
}
public void OnBeginShutdown(ref System.Array custom)
{
}
private object applicationObject;
private object addInInstance;
}
}
Обратите внимание на две переменные — applicationObject и addInInstance. Они имеют тип object (так как мы создаем модуль расширения на основе COM-технологий) и инициализируются в методе OnConnection. Переменная applicationObject обеспечивает доступ к корневому объекту хост-приложения — Word, Excel, PowerPoint и т.п. — и является точкой входа в объектную модель соответствующего приложения. При создании модулей расширения для более чем одного типа офисных приложений можно применять языковую конструкцию case для написания кода, специфичного для конкретного типа приложения. В нашем случае мы создаем модуль расширения только для Microsoft Word 2007, поэтому будем использовать переменную applicationObject как точку входа в объектную модель Word (к этой теме мы вернемся чуть позже).
Переменная addInInstance, также инициализируемая в методе OnConnection, является точкой входа в наш модуль расширения.
Развертывание и удаление модулей расширения
Прежде чем приступить к реализации функциональности самого шаблона модуля расширения, выберем опцию Build для второго проекта — проекта по развертыванию модуля расширения. В нашем примере он имеет название RibbonXDemoSetup. После того как этот проект собран, нам будут доступны две команды — Install и UnInstall, которые могут использоваться соответственно для установки модуля расширения в приложение и для его удаления из приложения (рис. 3).
Рис. 3. Проект развертывания модуля
расширения
Теперь соберем весь проект нашего модуля расширения (Ctrl+Shift+B) и после этого развернем его, нажав правую кнопку мыши на проекте RibbonXDemoSetup и выбрав команду Install.
Для того чтобы убедиться в том, что наш модуль расширения успешно установлен в Microsoft Office 2007, в панели управления приложением (верхняя левая кнопка) выберем опцию Word Options, затем вкладку Add-Ins и проверим, существует ли модуль расширения с именем нашего проекта — RibbonXDemo.
Теперь мы готовы к реализации функциональности нашего модуля расширения. В следующем разделе мы добавим к нашему модулю интерфейс IRibbonExtensibiity, который является основой для расширения интерфейса на основе Ribbon, создадим описание нашего интерфейса на языке XML, а также обсудим связанные с этим действия. На рис. 4 и 5 показан процесс установки модуля расширения, а также список установленных в Microsoft Word 2007 модулей расширения, в котором есть и создаваемый нами модуль.
Рис. 4. Развертывание модуля расширения
Рис. 5. Установленные модули расширения в Microsoft Word 2007
Реализация интерфейса IRibbonExtensibiity
Функциональность, связанная с расширением пользовательского интерфейса на основе Ribbon в Microsoft Office 2007, базируется на интерфейсе IRibbonExtensibility. Таким образом, прежде чем мы сможем реализовать функциональность нашего модуля расширения, необходимо добавить в код модуля реализацию указанного интерфейса. Для этого выполним следующие действия:
- Добавим ссылку на сборку Microsoft.Office.Core, в которой описан необходимый интерфейс.
- В описание COM-интерфейсов, реализуемых нашим модулем, добавим интерфейс IRibbonExtensibility:
public class Connect : Object,
Extensibility.IDTExtensibility2, IRibbonExtensibility
- Воспользуемся опцией Implement Interface для получения шаблона метода GetCustomUI, которому передается управление после метода OnConnection и который служит для возвращения XML-описания расширения пользовательского интерфейса:
string IRibbonExtensibility.GetCustomUI(string
RibbonID)
{
// Возвращает строку с XML-описанием расширений ин-
терфейса Ribbon
}
Описание интерфейса в формате XML
Метод GetCustomUI() возвращает строку с XML-описанием расширений интерфейса Ribbon. Эта строка содержит описание вкладки, которую мы будем использовать, группы внутри этой вкладки, а также интерфейсных элементов, которые мы добавим в нашу группу.
Существует как минимум два варианта создания XML-описания расширений интерфейса — статический и динамический. В первом случае мы генерируем описание непосредственно в коде метода GetCustomUI() — этот способ быстро реализуется, но при необходимости внесения изменений в интерфейс потребуется вносить изменения в код нашего модуля расширения.
Другой вариант, который мы и будем использовать, — это сохранение описания интерфейса в виде XML-файла, который мы включим в состав нашей сборки, где реализован создаваемый нами модуль расширения. Для этого выполним следующие действия:
- В проект RibbonXDemo добавим новый каталог — Add –> New Folder и дадим ему имя Resources.
- В этот каталог добавим новый элемент проекта — XML-файл (Add –> New Item –> XML File). Имя файла — CustomUI.xml.
Подключение схемы
Для того чтобы мы смогли создать XML-описание интерфейса в соответствии со схемой, описывающей формат Ribbon, и воспользоваться технологией IntelliSense, необходимо подключить схему к Visual Studio. Эта схема содержится в файле CustomUI.xsd, который можно загрузить по адресу: http://officeblogs.net/UI/customUI.xsd. Схему необходимо сохранить в каталоге, где располагаются все схемы, доступные в Visual Studio, — \Program Files\Microsoft Visual Studio 8\Xml\Schemas.
В панели свойств XML-файла выберем свойство Schemas и в списке доступных схем — схему CustomUI.xsd.
Теперь мы готовы к созданию XML-описания расширений интерфейса Ribbon. В отличие от примеров, приведенных в предыдущей части, мы не будем использовать новые вкладки, а поместим наше расширение непосредственно на вкладке Home, создав группу с названием Demo. Предположим, что наше расширение будет генерировать документ (деловое письмо) с каким-то предопределенным текстом. Описание расширения интерфейса будет выглядеть так:
<customUI
xmlns=”http://schemas.microsoft.com/office/2006/
01/customui”>
<ribbon>
<tabs>
<tab idMso=”TabHome” >
<group id=”DemoRibbon” insertAfterMso
=”GroupClipboard”
label=”Demo”>
<button id=”NewLetter” label=”New Letter”
imageMso=”New” size=”large”
onAction=”CreateLetter” />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Итак, мы добавляем новую группу на вкладку Home (идентификатор idMso — TabHome), размещаем ее сразу же после группы Clipboard (insertAfterMso) и включаем в нее кнопку большого размера со стандартной иконкой (imageMso), подписью New Letter и идентификатором NewLetter — по нажатии кнопки должен вызываться обработчик события CreateLetter.
Рис. 6. IntelliSense после подключения XSD-схемы
После того как мы сохранили наш файл с XML-описанием интерфейса, все что нам осталось сделать, — это указать в свойствах файла Build Action, чтобы изменить значение параметра на Embedded Resource, — в результате наш файл будет включен в состав ресурсов внутри сборки.
Теперь мы можем заняться написанием кода метода GetCustomUI(), который бы считывал этот файл и возвращал его содержимое в виде строки.
Использование механизма Reflection
Выше мы описали выбранный нами подход к хранению файла с XML-описанием интерфейса — непосредственно внутри сборки, в которой реализован код нашего модуля расширения. Такой подход позволяет, в частности, иметь достаточно компактное представление модуля без каких-либо дополнительных внешних файлов и вносить изменения в XML-описание интерфейса без необходимости изменять код самого приложения.
Для доступа к файлу, расположенному в сборке, воспользуемся механизмом Reflection. Добавим две ссылки для доступа к нужным нам функциям — System.Reflection и System.IO; последняя обеспечит нам доступ к потоковым функциям работы с файлами.
В теле метода GetCustomUI() напишем следующий код:
Assembly asm = Assembly.GetExecutingAssembly();
Stream stream =
asm.GetManifestResourceStream(“RibbonXDemo.
Resources.CustomUI.xml”);
StreamReader reader = new StreamReader(stream);
String customUI = reader.ReadToEnd();
return customUI;
reader.Close();
stream.Close();
В первой строке мы получаем доступ к загруженной сборке, во второй — к ресурсам, определенным в данной сборке (в нашем случае — к конкретному ресурсу, имя которого — Имя проекта –> Имя каталога –> Имя файла). Далее мы используем класс StreamReader для чтения из потока в строку и возвратим эту строку как результат работы нашего метода. Две последние строки освобождают необходимые ресурсы.
Для тестирования и отладки нашего модуля расширения необходимо выполнить несколько дополнительных настроек. Во-первых, в панели опций проекта на вкладке Debug в строке Start External Program необходимо указать Microsoft Word 2007: Program Files –> Microsoft Office –> Office 12 –> Winword.exe. Затем в панели настроек Microsoft Word в разделе General включить опцию Show add-in user interface errors — активация этой опции позволит получать сообщения об ошибках, возникающих в модулях расширения (рис. 7).
Рис. 7. Опции Microsoft Word
Теперь мы можем протестировать наше приложение. Нажатие клавиши F5 приводит к загрузке Microsoft Word 2007 с измененным интерфейсом Ribbon — в нем появилась группа Demo, расположенная на вкладке Home сразу же за группой Clipboard и содержащая кнопку New Letter (рис. 8).
Рис. 8. Добавленный нами интерфейсный элемент
Нажатие на кнопку New Letter приводит к появлению сообщения об ошибке: The callback function CreateLetter was not found by GetIDsOfNames(). Это означает, что наш модуль расширения загрузился успешно и механизм расширения Microsoft Word 2007 не обнаружил метода CreateLetter, который должен вызываться при нажатии на эту кнопку. В следующем разделе мы рассмотрим, как создать обработчик события для расширенного интерфейсного элемента.
Создание обработчика события
Как мы уже отмечали, переменная applicationObject обеспечивает доступ к объектной модели хост-приложения. При создании расширения для нескольких типов приложений достаточно использовать переменную типа object. В нашем же примере мы создаем расширение только для Microsoft Word 2007, поэтому нужно более точно определить тип переменной. Для этого включим в наш проект еще одно пространство имен — Microsoft.Office.Interop.Word — и присвоим ему значение word:
using word = Microsoft.Office.Interop.Word;
После этого изменим тип переменной applicationObject на:
private word.Application applicationObject;
и код инициализации этой переменной:
applicationObject = (word.Application) application;
Теперь создадим переменную missing типа System.Type.Missing — мы будем использовать ее вместо параметров, которые пропускаем при вызове того или иного метода объектной модели Microsoft Word (пользователи Visual Basic ставят вместо таких параметров запятые):
object missing = System.Type.Missing;
И наконец, создадим обработчик события для нашего интерфейсного элемента — процедуру CreateLetter, возвращающую значение void:
public void CreateLetter(IRibbonControl control)
{
}
Обратите внимание на то, что в качестве параметра среда исполнения передает обработчику ссылку на активированный интерфейсный элемент.
В качестве примера рассмотрим следующий сценарий. Предположим, у нас имеется шаблон бизнес-письма, в который наше расширение вставляет данные в определенные места.
Первым шагом будет создание шаблона. В пустом документе Word введем текст и создадим закладку (она показана на рис. 8 подсвеченной) — это будет место для вставки данных.
Зададим имя закладки как AppName и сохраним документ в виде шаблона с именем RX.dotx.
Добавим следующий код в обработчик события CreateLetter:
// Имя шаблона
object template = “RX.dotx”;
// Создание нового документа на основе шаблона
word.Document letter =
applicationObject.Documents.Add(ref template, ref
missing,
ref missing, ref missing);
// Имя закладки
object bmName = “AppName”;
// Доступ к закладке
word.Bookmark bm = letter.Bookmarks.get_Item(ref
bmName);
// Задание текста закладки
bm.Range.Text = “Microsoft Word 2007”;
В первой строке создадим переменную, в которой хранится имя шаблона для нашего документа. Затем используем объектную модель Word для создания нового документа на основе нашего шаблона. После этого создадим переменную, хранящую имя закладки. Используя объектную модель Word, получаем доступ к закладке с определенным именем и в последней строке изменяем содержимое закладки. Для целей нашего обзора такой функциональности вполне достаточно.
Заключение
Мы рассмотрели основные шаги по созданию модуля расширения на управляемом коде, который подключается к Microsoft Word 2007 и добавляет новые интерфейсные элементы в Ribbon. Описанные способы решения данной задачи могут пригодиться для создания различных решений на базе новой версии Microsoft Office System 2007.