Как написать надстройку для word

Введение

Целью создания этой статьи является сделать мини- шпаргалку для создания надстроек VSTO, здесь я попытался объединить собранные разрозненные крупицы информации по созданию надстройки VSTO на языке C# для Excel. Статья не претендует на целостное описание процесса создания надстройки, это скорее, «дорожная карта» для новичков, здесь описывается как по шагам сделать простейшую надстройку над Excel.

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

Visual Studio Tools for Office (VSTO, Visual Studio Tools для офиса) представляет собой набор средств разработки, позволяющий использовать всю функциональность .NET Framework для написания пользовательских функций офисного пакета. Надстройки, написанные в VSTO, являются полноценными скомпилированными программами, которые дополняют функционал офисного пакета и кастомизируют бизнес-логику конкретной фирмы.

Процесс написания надстройки

Для начала необходимо создать проект и выбрать тип «Надстройка VSTO для Exсel» в нашей любимой, а может не очень Visual Studio 2022, при необходимости выполнить установку соответствующих компонентов. Далее на следующей странице ввести имя проекта и выбрать платформу .NET Framework 4.8, как самую последнею. Увы платформа ограничивает максимально доступную версию используемого языка С# не выше 7.3, и некий «синтаксический сахар» из последних версий будет недоступен, что немного огорчает.

Сразу же при создании проекта открывается файл ThisAddIn.cs, представляющий собой точку доступа к функциям листа Excel, один недостаток, эти функции доступны только в этом файле, и помочь в этом нам сможет следующий код:

//Возвращает объект приложение Excel
public Excel.Application GetApplication()
{
	return Application;
}
//Возвращает объект активная рабочаяя книга
public Excel.Workbook GetActiveWorkBook()
{
	return (Excel.Workbook)Application.ActiveWorkbook;
}
//Возвращает объект активный рабочий лист
public Excel.Worksheet GetActiveWorksheet()
{
	return (Excel.Worksheet)Application.ActiveSheet;
}
//Возвращает объект активная ячейка
public Excel.Range GetActiveCell()
{
	return (Excel.Range)Application.Selection;
}

И теперь прописав в любом месте в приложения ниже идущий код мы можем получить доступ к функциям Excel:

Application application = Globals.ThisAddIn.GetApplication();
Worksheet worksheet = Globals.ThisAddIn.GetActiveWorksheet();
Range cell = Globals.ThisAddIn.GetActiveCell();
Workbook workBook = Globals.ThisAddIn.GetActiveWorkBook();

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

protected abstract class AbstractFunctions
{
	protected readonly Application application = Globals.ThisAddIn.GetApplication();
	protected readonly Worksheet worksheet = Globals.ThisAddIn.GetActiveWorksheet();
	protected readonly Range cell = Globals.ThisAddIn.GetActiveCell();
	protected readonly Workbook workBook = Globals.ThisAddIn.GetActiveWorkBook();
	public abstract void Start();  
}

Например, функция удаления формул на выделенном участке листа Excel будет выглядеть таким образом:

internal class DeleteFormula : AbstractFunctions
{
	public override void Start()
	{
		cell.Value = cell.Value;                            //Удаляем формул
		worksheet.get_Range("A1", Type.Missing).Select();   //Фокус на ячейку А1   
	}
}

Далее необходимо добавить ленту Ribbon (Visual Designer) на которой будут располагаться кнопки запуска наших приложений, тут тоже хитрого ничего нет, и добавляется на точно также, как и все элементы в проект:

Далее, в коде нашей ленты мы создаем обработчики нажатий на кнопки, я предпочитаю разносить функции по классам и использовать генерацию события нажатия на кнопку в методе Load(). Это значительно упрощает код визуально и облегчает внедрение зависимостей (Dependency injection):

public partial class Ribbon1
{     
	private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
	{             
		button1.Click += (s, a) =>
		{
			DeleteFormyla deleteFormyla= new DeleteFormyla ();
			DeleteFormyla.Start();
		}
	}
}

Однако, можно создать обработчик кнопок и нажатием двойным кликом, но как говорилось раньше это не самое красивое решение.

Теперь кульминация, запуск и.. Не работает, необходимо создать сертификат надстройки, заходим в Projects-Propertis-Signing, создаем тестовый ключ Create Test Certificate. И теперь Ваша надстройка успешно работает.

Таким образом, за минимум действий можно создать рабочую надстройку Excel.

Содержание

  • 1 Microsoft Word 2003
  • 2 Microsoft Word 2007
  • 3 Microsoft Word 2010
  • 4 Microsoft Word 2013
  • 5 Microsoft Word 2016
  • 6 Автозагрузка надстроек в Microsoft Word
    • 6.1 Другие материалы по теме:
    • 6.2 Как добавить Google Drive в Microsoft Office
    • 6.3 Как добавить «Википедию» в Microsoft Office
    • 6.4 Как добавить случайные значения в Excel
    • 6.5 Как наполнить Word случайным текстом
    • 6.6 Как добавить документы-вкладки в Word
    • 6.7 Как вернуть классическое меню Microsoft Office 2003
    • 6.8 Как сделать наглядную временную шкалу в PowerPoint
    • 6.9 Как добавить карту в презентацию PowerPoint

Надстройки — это подключаемые модули, которые расширяют возможности приложения, в данном случае Microsoft Word. Существуют разные виды надстроек, остановимся пока на надстройках, основанных на шаблонах. Что такое шаблон в Word? Шаблон — это специальный файл, в котором содержатся различные сведения о стилях, структуре, параметрах страниц, содержащихся макросах и так далее, на основе которого можно создавать новые документы.

Существует два вида шаблонов — общие и шаблоны конкретных документов. Файлы шаблонов имеют расширение «.dot» или «.dotm». Общие шаблоны, в том числе и шаблон Normal.dot, в который по умолчанию сохраняются макросы записываемые макрорекордером, содержат настройки, доступные всем документам MS Word. Шаблон конкретного документа содержит настройки, доступные только для документов, основанных на этом шаблоне. В Word имеется ряд встроенных шаблонов документов, например шаблоны писем, контрактов, резюме и прочее. Выбрать нужный шаблон можно при создании нового документа.

как сделать надстройку для word

Кроме этого пользователи могут сами создавать шаблоны для своих документов. Один и тот же шаблон может использоваться и как общий шаблон и как шаблон документа. Теперь рассмотрим сам процесс загрузки и подключения шаблонов. Чтобы не возникало путаницы, условно разделим шаблоны на «загруженные» и «подключенные». Загруженными будем считать шаблоны, ставшие доступными приложению MS Word, а подключенными будем считать шаблоны, которые не только загружены, но и сделаны активными (в списке шаблонов и надстроек перед ними проставлены флажки). Глобальный шаблон Normal.dot по умолчанию всегда и загружен и подключен. Обратите внимание на скриншот ниже, загружено три шаблона, а подключен из них один (не считая шаблона Normal). 

как сделать надстройку для word

В зависимости от того насколько часто шаблон будет использоваться, расположить его можно в разных папках. Для того, чтобы шаблон автоматически загружался и также автоматически подключался при каждом запуске MS Word, расположить его нужно в папке по адресу Documents and Settings/Ваше имя/Application Data/Microsoft/Word/Startup.

Если шаблон разместить в папке Documents and Settings/Ваше имя/Application Data/Microsoft/Шаблоны, то загружать и подключать его нужно будет вручную (об этом чуть позже), кроме того при каждом следующем запуске приложения Word, этот шаблон будет в списке загруженных шаблонов и надстроек, но снова будет выключенным. В этом списке отображаются не только общие шаблоны из папки «Шаблоны», но и шаблоны, автоматически загружаемые при запуске MS Word. 

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

Microsoft Word 2003

Запускаем приложение, заходим в меню Сервис/Шаблоны и надстройки…

как сделать надстройку для word

В появившемся диалоговом окне «Шаблоны и надстройки» нажимаем кнопку «Добавить…» 

как сделать надстройку для word

Через открывшееся окно проводника находим папку с нужным шаблоном. После добавления этот шаблон появится в списке шаблонов и надстроек, слева от него будет установлена галочка. Шаблон (надстройка) загружен и подключен.

Microsoft Word 2007

В запущенном приложении нажимаем кнопку Office  и переходим в «Параметры Word». В диалоговом окне «Параметры Word» выбираем раздел «Надстройки». В поле «Управление» выбираем пункт «Надстройки Word» либо «Шаблоны» без разницы, после чего нажимаем кнопку «Перейти…».

как сделать надстройку для word

Появляется диалоговое окно «Шаблоны и надстройки», в нем нажимаем кнопку «Добавить…» и добавляем нужный шаблон.

как сделать надстройку для word

Это же диалоговое окно можно вызывать нажатием кнопки «Шаблон документа» на вкладке «Разработчик».

как сделать надстройку для word

Если такая вкладка в Вашем приложении отсутствует, ее необходимо предварительно отобразить, это упростит и ускорит доступ к окну управления шаблонами и надстройками. 

Microsoft Word 2010

В запущенном приложении переходим на вкладку «Файл», окрашенную в синий цвет, после чего переходим в «Параметры Word». В диалоговом окне «Параметры Word» выбираем раздел «Надстройки». В поле «Управление» выбираем пункт «Надстройки Word» либо «Шаблоны» без разницы, после чего нажимаем кнопку «Перейти…».

как сделать надстройку для word

В появившемся диалоговом окне «Шаблоны и надстройки» нажимаем кнопку «Добавить…» и выбираем шаблон, который собираемся установить.

как сделать надстройку для word

Тоже самое можно проделать еще быстрее, просто нажать кнопку «Шаблон документа» на вкладке «Разработчик».

как сделать надстройку для word

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

Microsoft Word 2013

Также как и в Word 2010, в Word 2013 надстройки (шаблоны) можно установить двумя способами:

1. Через вкладку «Файл»/Параметры/Надстройки, в поле «Управление» из выпадающего списка выбрать пункт «Надстройки Word» либо «Шаблоны» и кликнуть по кнопке «Перейти». В появившемся окне менеджера надстроек «Шаблоны и надстройки» при помощи кнопки «Добавить» выбрать файл устанавливаемой надстройки и нажать «ОК».

2. Отобразить на ленте вкладку «Разработчик» и кликнуть по кнопке «Надстройки» в группе кнопок «Надстройки» либо по кнопке «Шаблон документа» в группе кнопок «Шаблоны».

как сделать надстройку для word

После этого в появившемся окне «Шаблоны и надстройки» кликнуть по кнопке «Добавить» и выбрать файл надстройки, которую необходимо установить.

Процесс установки надстроек (шаблонов) для Word, на примере Word 2013 записан на видео и выложен в youtube (смотреть видео по установке надстроек).

Microsoft Word 2016

В Word 2016 установить надстройку/шаблон можно также, как и в предыдущих версиях, либо через вкладку «Файл»/Параметры/Надстройки с выбором пункта «Надстройки для Word» в поле «Управление», либо отобразить на ленте вкладку «Разработчик», кликнуть по кнопке «Надстройки» либо «Шаблон документа» и при помощи кнопки «Добавить» выбрать файл надстройки, которые требуется установить.

как сделать надстройку для word

Автозагрузка надстроек в Microsoft Word

Чтобы надстройка автоматически загружалась и подключалась при запуске Word, её необходимо разместить в определенной папке по адресу …/Microsoft/Word/Startup. Если в папке …/Microsoft/Word нет папки Startup, то её можно создать вручную. Та часть пути к папке, которая обозначена многоточием, может различаться в зависимости от версий Windows и Office. Универсальный способ при помощи которого можно определить весь путь к папке автозагрузки, независимо от версий операционной системы и Office — найти место расположения шаблона Normal.dot или Normal.dotm, подняться на один уровень вверх, это будет папка «…/Microsoft» найти и перейти в папку «Word». Путь к папке автозагрузки должен выглядеть примерно так:

C:UsersВашеИмяAppDataRoamingMicrosoftWordSTARTUP

Другие материалы по теме:

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

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

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

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

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

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

                                                                                                                                                                                 

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

Первая кнопка удаляет все повторяющиеся пробелы. Довольно полезная функция, учитывая то, что правильная вёрстка в 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:UsersAppDataRoamingMicrosoftWordSTARTUP 

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

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

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

Как добавить Google Drive в Microsoft Office

Программы Office 2016 тесно связаны с OneDrive. Это очень хорошо, если вы всё ещё пользуетесь собственным облачным хранилищем Microsoft. Почему «всё ещё»? Напомню, совсем недавно редмондцы нежданно-негаданно урезали объём бесплатного дискового пространства, а чуть позже позволили его вернуть. Так себе история, хотя она совсем неинтересна тем, кто доверяет решениям от Google. Если вы среди них, устанавливайте плагин «Google Диска» для Microsoft Office.

как сделать надстройку для word

С ним вам станет проще открывать и сохранять рабочие документы из удалённого хранилища Google. Соответствующие меню появятся в разделах «Открыть» и «Сохранить».

Как добавить «Википедию» в Microsoft Office

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

как сделать надстройку для word

К слову, нечто похожее уже есть в 2016-й версии офиса, но отдельная надстройка всё же полезнее. Например, утилита лучше структурирует результаты поиска и не просто растолковывает значения выделенных слов, но и вставляет их в ваш текст безо всякого копипаста.

Как добавить случайные значения в Excel

Минутка экстаза для тех, кто хоть раз испытывал эффективные приёмы Excel и наполнял для этого листы случайными значениями. Эта муторная задача в миллион раз проще с надстройкой Random Generator. Не нужно никаких функций и их аргументов!

как сделать надстройку для word

Random Generator заполняет выбранные ячейки числами, датами и значениями «истина» или «ложь». В описании утилиты указано, что она предназначена для Office 2013 и более поздних версий пакета.

Как наполнить Word случайным текстом

Предположу, что скорость печати среднестатистического пользователя Word колеблется около 200 символов в минуту. И она ускоряется до тысяч символов, когда необходимо заполнить макет или таблицу чем угодно, только чтобы оценить документ визуально. Надстройка Lorem Ipsum Generator как раз и пригодится в таких случаях.

как сделать надстройку для word

Жаль, конечно, что нет кириллицы и нельзя указать точное количество символов.

Как добавить документы-вкладки в Word

Интернет программирует наше поведение, поэтому пользователи неосознанно хотят применять свои браузерные привычки и в других программах. Например, некоторым нравится панель вкладок в самом Word вместо миниатюр на панели задач Windows. Никогда не встречали такого? Тогда попробуйте в деле Tabs for Word.

как сделать надстройку для word

Программа умеет раскрашивать вкладки в разные цвета, а также имеет несколько настроек, которые подгонят её поведение под ваши запросы. Работоспособность проверена на Office 2016. Вирусов нет.

Как вернуть классическое меню Microsoft Office 2003

Office 2016 полон действительно классных нововведений, от которых люди старой закалки отказываются лишь потому, что им претит ленточная структура панели управления, появившаяся в 2007-й версии пакета. Программа Classic Menu for Office накладывает олдскульную оболочку поверх последних технологий.

как сделать надстройку для word

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

Как сделать наглядную временную шкалу в PowerPoint

Создание временных шкал (timeline) — верный способ донести важную для исполнения информацию до всех причастных лиц. Несколько распечаток таких графиков можно приложить к основному приказу, чтобы ответственные всегда видели перед собой дату, после которой их лишат премии либо вообще попросят собрать вещи. В общем, Office Timeline — хороший инструмент для мотивации. По уверениям разработчиков, их продукт позволяет создавать временные шкалы самой разной сложности, однако для этого придётся изучить весь доступный инструментарий.

как сделать надстройку для word

Созданную в PowerPoint шкалу можно перенести в другую офисную программу, например в тот же Word.

Как добавить карту в презентацию PowerPoint

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

как сделать надстройку для word

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

А какими дополнениями к офисному пакету программ пользуетесь вы?

Конструирование интерфейсов чаще всего используется при создании надстроек. Надстройка представляет собой невидимый документ со встроенным 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[ИмяПользователя]AppDataRoamingMicrosoftWordSTARTUP

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

Introduction

Microsoft Word, along with the rest of the Office applications, has an API that can be called by a program to perform operations on the app’s document. If you’ve given this API any thought, you’ve probably considered it something only power users writing VBA scripts would use.

However, that’s not the case: in Word, the powerful API calls the app to perform just about any action the app can perform on its own. Further, you can write an add-in in C# that does everything a regular C# program does. That’s right—the entire .NET Framework is available to your add-in. You end up with a C# program that not only has all of .NET’s functionality, but also Word’s functionality too.

The first question you face when you create a Word add-in is which of two approaches to use: Visual Studio Tools for Office (VSTO) or an add-in that extends the IDTExtensibility2 interface. According to Microsoft, VSTO isn’t for commercial add-ins. (And I’m not sure of when you would want to use it. In fact, when I asked Microsoft P.R. for examples of when you would use VSTO – they could not give me examples either.) However, you should know it is an option. For more information about VSTO, visit MSDN.

In this article, I’ll show you how to create an IDTExtensibility2 Word add-in. To avoid duplicating details the Microsoft Knowledge Base articles explain at great length, I’ll refer you to the articles instead. I’ll also cover the bugs I ran across and the workarounds for them.

First, to create the Word add-in, read Knowledge Base article 302901. Note: as I demonstrate in this article, you don’t create a Microsoft Office System Projects project in Visual Studio. That’s where you go to create a VSTO project.

Now that you have your Word add-in, you can play around with it. However, you’ll soon discover you can’t turn on themes for buttons the way you do for a Windows application. But a solution exists. Knowledge Base article 830033 comes to the rescue. Once again, when you follow the instructions, turning on themes for buttons works perfectly.

Visual Studio performs a bit of magic so it can run your add-in in the debugger. There is a long explanation for this that will appear in Part II of this article.

So create your initial add-in, build it, and install it. Next, go to the Solution Explorer in Visual Studio, right-click on your project, then select Properties. In the Properties dialog, select Configuration Properties | Debugging, and go to the Start Application property. For that property, set the location of WINWORD.exe (for example, it’s C:Program FilesMicrosoft OfficeOFFICE11WINWORD.EXE on my system). Now, you can run or debug from Visual Studio.

I also recommend you never again run the installer on your development machine. Windows seems to get confused by having registry entries for both running the add-in from the debugger and running it as a standard Word add-in. Even if you uninstall the add-in, it appears to cause problems. And if you have to rename your add-in, create a new one with the new name instead, then copy the other files over. Renaming an existing add-in will break because the add-in won’t have any of the registry settings made during the creation process.

Write Your Add-In

Now you’re ready to start writing your add-in. The first step is to look at the documentation for the API so you can see what you can do. You look in the MSDN library. The documentation isn’t there. You try the Word online help. The documentation isn’t there. You search on Microsoft.com, Google, and post in the newsgroups … and the answer is: there is no documentation. Documentation exists for VBA, but not for C#.

It gets worse. The VBA documentation is only available through Word’s online help; the documentation on MSDN is incomplete. And the format for the documentation is likely one you’re not used to, making it hard for you to drill down to the info you need.

You’ll also come across properties that don’t exist. In those cases, you’ll have to call get_Property(). But again, this approach isn’t documented, so you’ll have to try it and see if it works. (Refer to the MSDN article, “Automating Word Using the Word Object Model”.) But wait—there’s more. The .NET API is a COM API designed for VB. So a method that takes two integers, such as Range(int start, int end) is actually declared as Range(ref object start, ref object end). You have to declare two object (not int) variables, assign int values to them, then pass them in. Yet, the int values are not changed by the call and only an int can be passed in.

But wait—there’s even more. I’ve only found this in one place so far but it probably holds elsewhere: there is no Application.NewDocument event (because there is an Application.NewDocument method in the Word API—and C# doesn’t support having an event and a method with the same name). However, you can cast an Application object to an ApplicationClass object, and you can then call ApplicationClass.NewDocument. Problem solved … well, actually, it’s not. The ApplicationClass solution works on some systems, but not on others. (I have no idea why – and could never get a straight answer on this from Microsoft.) But there is a solution. You can also cast the Application object to an ApplicationEvents4_Event object, and you then call the ApplicationEvents4_Event.NewDocument event (ApplicationEvents3_Event.NewDocument in Word 2002). (While this appears to work on all the systems I’ve tested thus far, you might come across systems where it doesn’t work.) So don’t cast objects to ApplicationClass; instead, cast them to ApplicationEvents4_Event objects. And the IntelliSense doesn’t work for the ApplicationEvents4_Event class, so you’ll have to type in the entire line of code, but it will compile and run fine.

Application.WindowSelectionChange is an event I haven’t found a solution for yet. It can always be set, but sometimes it doesn’t fire. And I can’t find any reason for this. I can start Word and it doesn’t fire, but when I exit and immediately restart, it works. It might not work two or three times in a row, but then work ten times in a row. Even when it works, if you enter text or press undo/redo, it doesn’t fire even though it changes the selection. So, it’s not an all selection changes event so much as a some selection changes event.

Enable and Disable Menu Items

Now, say you want to enable or disable menu items, like Word does for Edit, Copy and Edit, and Paste (only enabled if text is selected). An event is fired before the RMB pop-up menu appears. But for the main menu, there is no event before a menu is dropped down. (I know it seems like there must be an event for this, but there isn’t.)

The WindowSelectionChange event is the only method I’ve found to use. However, it’s inefficient because it either fires a lot or doesn’t fire at all, and it doesn’t fire when you enter text (for which there is no event).

So, you’ll need to do two things: first, enable or disable menu items when the selection event fires; and second, in the event handler for menu items that can be enabled or disabled, check when you first enter the event handler, and if the menu items should be disabled, call your menu update method and return. This way, after the user tries to execute that menu item, the menu is correct.

When you create menu objects, you’ll need to keep a couple of things in mind. First, you must store the returned menu objects in a location that will exist for the life of the program. The menus are COM objects, and if no persistent C# object is holding them, they are designated for garbage collection and your events will stop working the next time the garbage collector runs.

Second, the call CommandBarPopup.Controls.Add(MsoControlType.msoControlButton, Type.Missing, Type.Missing, Type.Missing, false) must have false for the final parameter. If this is set to true, as soon as any other add-in sets Application.CustomizationContext to another value, all your menus go away.

Apparently, temporary (the 5th parameter) isn’t the life of the application, but the life of the present CustomizationContext set as the CustomizationContext. If another add-in changes the CustomizationContext, your menu disappears. Given a user can normally have several add-ins, you can never set the 5th parameter to true. The downside is you’re adding your menus to the default template (normal if you don’t change it) permanently. I don’t think you can have menus exist for the life of the application, but not have them added to the template.

Give Users a Template

Another approach is to give users a template to use with your add-in. The template doesn’t have to do anything, but on startup, you look for that template and add your menus only if the template exists. You also add your menus to your template. In essence, you’re using the existence of the template as an Attribute. This is a clean way to have your add-in appear only when you want it to and have it not touch any other part of Word.

Each time your program starts, you need to determine if your menu has already been added (CommandBarControl.Tag is useful for this). If it isn’t there, add it. If it is there, either delete it and then add it, or set your events on the existing items. I delete and add it because over the course of writing the program, the menu items change at times. If you delete and add it, save the location of the menu and add it there. If a user customizes her menu by moving the location of your menu, you don’t want to force it back to the original position the next time Word runs.

When you set the menus, this changes the template, and Word normally asks the user when she exits if she wants to save the changed template. To avoid this, get the value of the default Template.Saved before making the changes, and set Template.Saved to that value after you’re done. If the template was not dirty when you first got the value, it will be set back to the clean value upon completion:

private Template TemplateOn 
{ 
  get
  {
    
    
    Templates tpltColl = ThisApplication.Templates; 

    foreach (Template tpltOn in tpltColl)
      if (tpltOn.Name == "MY_TEMPLATE.DOT")
        return tpltOn;
      return ThisApplication.NormalTemplate;
  }
}

...


Template thisTemplate = TemplateOn;


bool clean = thisTemplate.Saved;
ThisApplication.CustomizationContext = thisTemplate;
...

thisTemplate.Saved = clean;

...

One warning: don’t look at the Template.Dirty value using the debugger. The act of looking at it sets it to dirty. This is a true Heisenbug. (The Heisenberg theory is that the act of observing a particle affects the particle.)

And even after you take all these measures, there is one more issue. Sometimes, when you close all documents so you have Word running but no document open, the menu events still fire fine, but calls to CommandBarControl.Enabled throw exceptions. Once you create a new document, the problem usually goes away—unless you have two instances of Word, close the first one, then bring up a document in the second one. Then the problem remains. The solution to this is covered in Part II.

Find Text Within Range

Now for the last bug (in this article, at least, and one I’ve only seen when searching for ranges within table cells). You need to find some text within a certain range. So you set the range and call Range.Find.Execute(). However, this call sometimes returns matching text found outside the range you selected. It can find text before or after the range and return it. If it finds it before, it doesn’t mean the text doesn’t exist in your range, just that Range.Find.Execute() hasn’t gotten here yet. (And yes, it is only supposed to return text inside the passed range – but it will return text outside the range.)

To fix this, you’ll need to set range.Find.Wrap = WdFindWrap.wdFindContinue and keep calling it until you get a find in your range, or it goes past the end of your range. However, there is another problem with this approach. The Find can first return text after your range even though there is text inside your range. In this case, you need to cycle through the entire document until it wraps back to your range to find the text. While this can burn a lot of clock cycles (think of a 200-page document where you have to walk all the way around), it only happens in cases where this bug occurs and, fortunately, those cases are rare.

public Range Find (int startOffset, int endOffset, bool forward, string text) 
{
    
    
    
    int rangeStart = Math.Max(0, startOffset - 1);

    
    int rangeEnd = Math.Min(endOffset + 1, ThisDocument.Content.End);

    object start = rangeStart;
    object end = rangeEnd;
    Range range = ThisDocument.Range (ref start, ref end);
    range.Find.ClearFormatting();
    range.Find.Forward = forward;
    range.Find.Text = text;
    range.Find.Wrap = WdFindWrap.wdFindStop;

    object missingValue = Type.Missing;
    range.Find.Execute(ref missingValue, ref missingValue, 
                ref missingValue, ref missingValue, ref missingValue, 
                ref missingValue, ref missingValue, ref missingValue, 
                ref missingValue, ref missingValue, ref missingValue, 
                ref missingValue, ref missingValue, ref missingValue, 
                ref missingValue);

    
    if (range.Find.Found && (startOffset <= range.Start) 
                             && (range.End <= rangeEnd))
        return range;

    
    
    
    
    
    start = startOffset;
    end = rangeEnd;
    range = ThisDocument.Range (ref start, ref end);
    string docText = range.Text;

    if (docText == null)
        return null;

    int ind = forward ? docText.IndexOf(text) : docText.LastIndexOf(text);
    if (ind == -1)
        return null;

    
    

    int _start = forward ? startOffset : rangeEnd - text.Length;
    while ((startOffset <= _start) && (_start < rangeEnd))
    {
        start = _start;
        end = rangeEnd;
        range = ThisDocument.Range (ref start, ref end);
        docText = range.Text;

        
        

        if ((docText != null) && docText.StartsWith(text))
        {
            
            int endPos = range.Start + text.Length;

            while (endPos < endOffset) 
            {
                range.End = endPos;
                docText = range.Text;
                if (docText == text)
                    break;
                endPos ++;
            }
            return range;
        }
        _start += forward ? 1 : -1;
    }
    return null;
}

All in all, I’d say you should view the Word .NET façade as fragile. Aside from the Find bug, these problems are probably due to the façade and not Word itself. But keep in mind, you might have to experiment to find a way to talk to Word in a way that is solid.

Beyond the bugs I’ve discussed, you’ll want to keep these issues in mind for your add-in. If you need to tie data to the document, use Document.Variables. Two notes about this: first, Document.Variable only accepts strings (I uuencode my data); and second, it has a size limit between 64,000 and 65,000 bytes. Also, if you serialize your data, be aware that .NET sometimes has trouble finding the assembly of your add-in when deserializing the objects. This is a .NET issue, not a Word one.

If you’re going to call the Help methods from your add-in, they require a Control object for the parent window. Given Word isn’t a .NET program, you have no way to convert an hwnd (Word’s main window) to a Control. So for Help, you’ll need to pass a null parent window.

Occasionally (in my case, it’s about once a week), when you’re building the COM add-in shim, you’ll get the error:

stdafx.h(48): error C3506: there is no typelib registered 
            for LIBID '{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}'

When you get this, go to C:Program FilesCommonFilesDesigner and run the regsvr32 msaddndr.dll command. I have no idea why this happens (sometimes it occurs between two builds when all I’ve done is edit a CS file), but it’s easy to fix.

The add-in you create is a COM add-in, not an automation add-in. To see it, add the tool menu item called curiously enough “COM Add-Ins” to the Tools menu.

Exit All Instances of Word

One issue that can sideswipe you in a myriad of ways is that if you use Outlook, it uses Word for composing messages. Word loads add-ins when the first instance starts, and uses that set of add-ins for all other instances. The only way to reload instances is to exit all instances of Word.

Word also locks the files it’s using, such as the DLL files for add-ins. Again, the executable files for your add-in are locked. If you run the debugger, it appears to lock the PDB files too. So if things become strange—builds fail, files are locked, new changes aren’t executing—make sure you’ve exited Outlook as well as all instances of Word. This isn’t a bug; it’s a correct operation, but it’s not obvious and can be frustrating to figure out.

Winword.exe can also run as an orphan process at times. No copy of Word will be on the screen; Outlook isn’t running; somehow an instance of Word never exited. Again, in this case, you can’t build until you kill that orphan process.

You’ll also come across the standard Visual Studio issue where occasionally you’ll need to exit and restart Visual Studio. This isn’t a Word issue (my work colleagues have the same problem when they’re writing C# Windows application code), and if all else fails, reboot.

Remember that Outlook starts Word without a document. Make sure your add-in handles this. Also keep in mind that because the API was originally from VB, all arrays are 1-based. This is a major pain to remember, but Microsoft does need to keep the API consistent across all .NET languages, and VB was used first for the API, so it wins.

Another issue to watch for is that Word doesn’t display exceptions thrown in your add-in. You can catch exceptions and act on them, but if you don’t catch the exception, it’s never displayed. You have to set your debugger to break on managed exceptions so you can see them, or put a try/catch in every event handler.

An IDTExtensibility2 add-in can run on Word 2002 (VSTO is limited to Word 2003). However, MSDN’s article on installing the Office 10 Primary Interop Assemblies, or PIAs, (see Additional Resources) is wrong or incomplete. You get the PIAs added to the GAC, but you can’t find them with add references – and you cannot select a file in the GAC. So copy the files out of the GAC, add those copied files, and it will then point at the PIAs in the GAC.

What about Word 2000? Supposedly, PIAs can be built for it. I’m still trying to figure this out, and if I do figure it out, look for it to be covered in Part II or Part III. But Microsoft does not provide Word 2000 PIAs.

Okay, you’ve got your add-in working; just a few more minutes and you can ship it to the world—or so it would seem. First comes the fact that if you Authenticode sign your C# DLL, it doesn’t matter to Word. You have to create a COM shim DLL that then calls your C# DLL. The MSDN article “Using the COM Add-in Shim Solution to Deploy Managed COM Add-ins in Office XP” covers this. Note: this does not work for VSTO applications. (To vent for a second: WHY, Why, why didn’t Microsoft set it up so Word accepts signed .NET DLLs as a signed Word add-in?) Follow the article very closely. In a number of cases, if your changes to the shim are just slightly wrong, the shim won’t work. And figuring out what is wrong is not easy. In fact, the only way to fix problems is to keep trying different approaches. There is a lot to cover about the shim add-in, and it is covered in Part II.

IMHO, this is the one place Microsoft really blew it. The shim solution adds a lot of work for every developer (as opposed to Microsoft doing it once up front), makes the whole solution more complex, and there is no way to debug problems with it.

Once you have the shim working, you’ll need to strong name all .NET DLLs, then Authenticode sign all DLLs. This is necessary to run on systems where security is set to High/do not trust add-ins.

There is one final issue you must address for your code to install correctly. Your initial setup program has registry settings in the HKCU for your add-in. These need to be deleted as you are not setting up your .NET code to be the add-in, but the shim instead. In your shim, the ConnectProxy.rgs file sets its add-in registry entries in the HKCU. If you always want to install your add-in for the current user only, this is fine. But if you want to give your users the choice of installing for everyone or installing for just me (and for 98% of you, you should), place the registry settings in the setup program under User/Machine Hive and delete them from ConnectProxk.rgs. (There will be a longer explanation of this in Part II.)

The biggest problem the Word add-in C# API suffers from is neglect, and its issues reside almost entirely in the .NET/C# wrapper and its associated features (such as documentation, signing security, and the shim layer). This means, once you figure out how to work around these issues, you’re left with a solid, powerful engine.

Additional Resources:

  • Microsoft Knowledge Base article 302901: “How To Build an Office COM Add-in by Using Visual C# .NET”.
  • Microsoft Knowledge Base article 830033: “How to apply Windows SP themes to Office COM add-ins”.
  • MSDN Library: “Automating Word Using the Word Object Model”.
  • MSDN Library: “Using the COM Add-in Shim Solution to Deploy Managed COM Add-ins in Office XP” by Misha Shneerson and Siew-Moi Khor.
  • MSDN Library: “Working with the Office XP Primary Interop Assemblies” by Paul Cornell.

Время прочтения: 6 мин.

Можно создать шаблон документа в Word, используя элементы управления содержимым (Content Control), и ограничить редактирование в нем. Но в документах бывает такое содержимое, которое необходимо добавлять или удалять. Например, добавить еще одну строку в таблицу, добавить пункт распоряжения и т.п. В таком случае будет удобнее создать собственную вкладку в Word, которая позволит изменять структуру документа при необходимости. В этой статье будет показано, как создавать Надстройку VSTO для документа Word.

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

В Visual Studio создадим новый проект «ExampleRibbonWord» типа «Надстройка VSTO для Word 2013 и Word 2016»:

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

Вот такая лента у нас создалась:

Теперь в окне свойств элементов управления изменим названия нашей вкладки (Tab1) и группы (Group1).

tab1.Label=”Мои шаблоны”

group1.Label=”Создание шаблона”

Для начала давайте добавим на ленту кнопку (Button1), при нажатии на которую в документе Word будет создаваться шаблон. Свойства Label задаем= «Пример шаблона».

Теперь создадим событие Click для кнопки. Кнопка должна в документе Word сформировать указанный выше шаблон. В элементах управления содержимым мы разместим следующую информацию: дата распоряжения, номер распоряжения, название распоряжения, текст распоряжения, должность и ФИО. Пункты распоряжения мы разместим в таблице без границ. Каждый пункт распоряжения будет также размещаться в элементе управления. При нажатии на кнопку таблица должна сформироваться с одной строкой и двумя столбцами (номер пункта, текст пункта). В дальнейшем необходимо будет реализовать функционал, который позволит добавлять новые пункты в распоряжение.

Код:

public partial class Ribbon1
    {
        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {

        }

        //Создание текстового элемента управления
        //диапазон, тэг (по нему в дальнейшем можно получать содержимое), замещающий текст, когда элемент не заполнен, название 
        private void AddContentControlText(Word.Range r, string tagRTC, string PlaceholderTextRTC, string titleRTC)
        {
            Word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;
            Document extendedDocument = Globals.Factory.GetVstoObject(currentDocument);
            RichTextContentControl rTC = extendedDocument.Controls.AddRichTextContentControl(r, tagRTC);
            rTC.PlaceholderText = PlaceholderTextRTC;
            rTC.Tag = tagRTC;
            rTC.LockContentControl = true;//элемент нельзя удалить
            rTC.Title = titleRTC;
        }

        //Создание элемента управления даты 
        //диапазон, тэг (по нему в дальнейшем можно получать содержимое), замещающий текст, когда элемент не заполнен, стиль даты, название 
        private void AddContentControlDate(Word.Range r, string tagDPC, string PlaceholderTextDPC, string styleDate, string titleDPC)
        {
            Word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;
            Document extendedDocument = Globals.Factory.GetVstoObject(currentDocument);
            DatePickerContentControl dPC = extendedDocument.Controls.AddDatePickerContentControl(r, tagDPC);
            dPC.PlaceholderText = PlaceholderTextDPC;
            dPC.Tag = tagDPC;
            dPC.LockContentControl = true;//элемент нельзя удалить
            dPC.DateDisplayFormat = styleDate;
            dPC.Title = titleDPC;
        }

        //создаем собственный стиль
        private void CreateMyStyle(Word.Document doc)
        {
            Word.Style style = doc.Styles.Add("MyStyleParagraph", Word.WdStyleType.wdStyleTypeParagraph);
            style.Font.Size = 12;
            style.Font.Name = "Times New Roman";
        }

        private void button1_Click(object sender, RibbonControlEventArgs e)
        {
            //при нажатии на кнопку появится диалоговое окно, которое спросит где создать шаблон в текущем открытом word или в новом.
            //при этом, если текущий документ будет не пустым, то шаблон создастся в новом документе
            Word.Document doc;
            DialogResult dialogResult = MessageBox.Show("Создать шаблон в новом документе?", "Создание шаблона", MessageBoxButtons.YesNo);
            if (dialogResult == DialogResult.Yes) doc = new Word.Document();
            else
            {
                doc = Globals.ThisAddIn.Application.ActiveDocument;
                if (doc.Characters.Count > 1)
                {
                    MessageBox.Show("Текущий документ не пустой! Шаблон будет создан в новом документе!");
                    doc = new Word.Document();
                }
            }

            //создаем стиль в текущем документе
            CreateMyStyle(doc);

            //создаем абзац
            doc.Paragraphs.Add();
            Word.Paragraph p = doc.Paragraphs.First;
            Word.Range r = p.Range;
            r.Font.Bold = 1;// шрифт жирный
            r.Text = "РАСПОРЯЖЕНИЕ";//текст абзаца
            r.ParagraphFormat.set_Style(doc.Styles["MyStyleParagraph"]); //стиль применяем

            //создаем абзац и вставляем в него 2 элемента управления
            //для каждого типа элемента управления создаем функцию с параметрами
            doc.Paragraphs.Add();
            p = doc.Paragraphs.Last;
            r = p.Range;
            r.ParagraphFormat.set_Style(doc.Styles["MyStyleParagraph"]);
            //дата распоряжения
            AddContentControlDate(r, "DateRaspr", "Дата распоряжения", "d MMMM yyyy г.", "Дата распоряжения");
            r = doc.Range(p.Range.End - 1, p.Range.End - 1);
            r.Text = " № ";
            //номер распоряжения
            AddContentControlText(r, "NumberRaspr", "Номер распоряжения", "Номер распоряжения");

            doc.Paragraphs.Add();
            p = doc.Paragraphs.Last;
            p.SpaceAfter = 18;
            r = p.Range;
            r.ParagraphFormat.set_Style(doc.Styles["MyStyleParagraph"]);
            r.Font.Bold = 1;
            p.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;//по центру размещаем
            //название распоряжения
            AddContentControlText(r, "NameRaspr", "Название распоряжения", "Название распоряжения");

            doc.Paragraphs.Add();
            p = doc.Paragraphs.Last;
            p.SpaceAfter = 18;
            r = p.Range;
            r.ParagraphFormat.set_Style(doc.Styles["MyStyleParagraph"]);
            p.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
            //текст распоряжения
            AddContentControlText(r, "TextRaspr", "Текст распоряжения", "Текст распоряжения");

            doc.Paragraphs.Add();
            p = doc.Paragraphs.Last;
            r = p.Range;
            r.ParagraphFormat.set_Style(doc.Styles["MyStyleParagraph"]);
            //таблица для пунктов распоряжения
            Word.Table tbl = doc.Tables.Add(r, 1, 2);
            tbl.Columns[1].SetWidth(doc.Application.CentimetersToPoints(1), Word.WdRulerStyle.wdAdjustFirstColumn);
            tbl.Cell(1, 1).Range.Text = "1.";
            r = doc.Range(tbl.Cell(1, 2).Range.End - 1, tbl.Cell(1, 2).Range.End - 1);
            AddContentControlText(r, "TextPunkt", "Пункт распоряжения", "Пункт распоряжения");

            doc.Paragraphs.Add();
            p = doc.Paragraphs.Last;
            r = p.Range;
            //должность
            AddContentControlText(r, "Dolgnost", "Должность", "Должность");
            r = doc.Range(p.Range.End - 1, p.Range.End - 1);
            r.Paragraphs.TabStops.Add(350, Word.WdAlignmentTabAlignment.wdLeft);
            r.Text = "t";
            r = doc.Range(p.Range.End - 1, p.Range.End - 1);
            //ФИО
            AddContentControlText(r, "FIO", "ФИО", "ФИО");
        }
    }

Запускаем и получаем вот такой шаблон документа:

Для примера заполним дату распоряжения:

Дата заполнена:

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

Понравилась статья? Поделить с друзьями:
  • Как написать надстройку для excel
  • Как написать надстройки для excel
  • Как написать надпись по кругу в word
  • Как написать над словом маленькие буквы в word
  • Как написать над подчеркнутой строкой в word