Microsoft office interop word range

title description ms.date ms.topic dev_langs helpviewer_keywords author ms.author manager ms.technology ms.workload

How to: Programmatically define and select ranges in documents

Learn how you can programmatically define and select ranges in Microsoft Word documents by using the Range object.

02/02/2017

how-to

VB

CSharp

documents [Office development in Visual Studio], selecting sentences

documents [Office development in Visual Studio], ranges

sentences, selecting in documents

ranges, selecting in documents

ranges, defining in documents

John-Hart

johnhart

jmartens

office-development

office

How to: Programmatically define and select ranges in documents

[!INCLUDE Visual Studio]
You can define a range in a Microsoft Office Word document by using a xref:Microsoft.Office.Interop.Word.Range object. You can select the entire document in a number of ways, for example, by using the xref:Microsoft.Office.Interop.Word.Range.Select%2A method of the xref:Microsoft.Office.Interop.Word.Range object, or by using the Content property of the xref:Microsoft.Office.Tools.Word.Document class (in a document-level customization) or the xref:Microsoft.Office.Interop.Word.Document class (in a VSTO Add-in).

[!INCLUDEappliesto_wdalldocapp]

Define a range

The following example shows how to create a new xref:Microsoft.Office.Interop.Word.Range object that includes the first seven characters in the active document, including non-printing characters. It then selects the text within the range.

To define a range in a document-level customization

  1. Add the range to the document by passing a start and end character to the xref:Microsoft.Office.Tools.Word.Document.Range%2A method of the xref:Microsoft.Office.Tools.Word.Document class. To use this code example, run it from the ThisDocument class in your project.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationCS/ThisDocument.cs» id=»Snippet18″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationVB/ThisDocument.vb» id=»Snippet18″:::

To define a range by using a VSTO Add-in

  1. Add the range to the document by passing a start and end character to the xref:Microsoft.Office.Interop.Word._Document.Range%2A method of the xref:Microsoft.Office.Interop.Word.Document class. The following code example adds a range to the active document. To use this code example, run it from the ThisAddIn class in your project.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationAddIn/ThisAddIn.cs» id=»Snippet18″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationAddIn/ThisAddIn.vb» id=»Snippet18″:::

Select a range in a document-level customization

The following examples show how to select the entire document by using the xref:Microsoft.Office.Interop.Word.Range.Select%2A method of a xref:Microsoft.Office.Interop.Word.Range object, or by using the xref:Microsoft.Office.Tools.Word.Document.Content%2A property of the xref:Microsoft.Office.Tools.Word.Document class.

To select the entire document as a range by using the Select method

  1. Use the xref:Microsoft.Office.Interop.Word.Range.Select%2A method of a xref:Microsoft.Office.Interop.Word.Range that contains the entire document. To use the following code example, run it from the ThisDocument class in your project.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationCS/ThisDocument.cs» id=»Snippet19″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationVB/ThisDocument.vb» id=»Snippet19″:::

To select the entire document as a range by using the Content property

  1. Use the xref:Microsoft.Office.Tools.Word.Document.Content%2A property to define a range that encompasses the entire document.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationCS/ThisDocument.cs» id=»Snippet20″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationVB/ThisDocument.vb» id=»Snippet20″:::

    You can also use the methods and properties of other objects to define a range.

To select a sentence in the active document

  1. Set the range by using the xref:Microsoft.Office.Interop.Word.Sentences collection. Use the index of the sentence you want to select.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationCS/ThisDocument.cs» id=»Snippet21″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationVB/ThisDocument.vb» id=»Snippet21″:::

    Another way to select a sentence is to manually set the start and end values for the range.

To select a sentence by manually setting the start and end values

  1. Create a range variable.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationCS/ThisDocument.cs» id=»Snippet23″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationVB/ThisDocument.vb» id=»Snippet23″:::

  2. Check to see if there are at least two sentences in the document, set the Start and End arguments of the range, and then select the range.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationCS/ThisDocument.cs» id=»Snippet24″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationVB/ThisDocument.vb» id=»Snippet24″:::

Select a range by using a VSTO Add-in

The following examples show how to select the entire document by using the xref:Microsoft.Office.Interop.Word.Range.Select%2A method of a xref:Microsoft.Office.Interop.Word.Range object, or by using the xref:Microsoft.Office.Interop.Word._Document.Content%2A property of the xref:Microsoft.Office.Interop.Word.Document class.

To select the entire document as a range by using the Select method

  1. Use the xref:Microsoft.Office.Interop.Word.Range.Select%2A method of a xref:Microsoft.Office.Interop.Word.Range that contains the entire document. The following code example selects the contents of the active document. To use this code example, run it from the ThisAddIn class in your project.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationAddIn/ThisAddIn.cs» id=»Snippet19″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationAddIn/ThisAddIn.vb» id=»Snippet19″:::

To select the entire document as a range by using the Content property

  1. Use the xref:Microsoft.Office.Interop.Word._Document.Content%2A property to define a range that encompasses the entire document.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationAddIn/ThisAddIn.cs» id=»Snippet20″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationAddIn/ThisAddIn.vb» id=»Snippet20″:::

    You can also use the methods and properties of other objects to define a range.

To select a sentence in the active document

  1. Set the range by using the xref:Microsoft.Office.Interop.Word.Sentences collection. Use the index of the sentence you want to select.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationAddIn/ThisAddIn.cs» id=»Snippet21″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationAddIn/ThisAddIn.vb» id=»Snippet21″:::

    Another way to select a sentence is to manually set the start and end values for the range.

To select a sentence by manually setting the start and end values

  1. Create a range variable.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationAddIn/ThisAddIn.cs» id=»Snippet23″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationAddIn/ThisAddIn.vb» id=»Snippet23″:::

  2. Check to see if there are at least two sentences in the document, set the Start and End arguments of the range, and then select the range.

    C#

    :::code language=»csharp» source=»../vsto/codesnippet/CSharp/Trin_VstcoreWordAutomationAddIn/ThisAddIn.cs» id=»Snippet24″:::

    VB

    :::code language=»vb» source=»../vsto/codesnippet/VisualBasic/Trin_VstcoreWordAutomationAddIn/ThisAddIn.vb» id=»Snippet24″:::

See also

  • Word object model overview
  • How to: Programmatically extend ranges in documents
  • How to: Programmatically retrieve start and end characters in ranges
  • How to: Programmatically extend ranges in documents
  • How to: Programmatically reset ranges in Word documents
  • How to: Programmatically collapse ranges or selections in documents
  • How to: Programmatically exclude paragraph marks when creating ranges

      Большинству операций, выполняемых программным способом, есть эквиваленты в пользовательском интерфейсе (UI), доступные как команды в меню и на панелях инструментов. Также существует нижележащая архитектура, обеспечивающая поддержку команд, выбираемых из UI. Всякий раз, когда вы создаете новый документ Word, он базируется на каком-либо шаблоне; расширение файлов шаблонов «.dot», а файлы документов – «.doc». Шаблон Word может содержать текст, код, стили, панели инструментов, элементы автотекста, комбинации клавиш для быстрого доступа к командам. Новый документ связывается с шаблоном и получает полный доступ к его элементам. Если вы не указываете конкретный шаблон, новый документ создается на основе стандартного шаблона «Normal.dot», который устанавливается при установке Word).

      Шаблон Normal.dot является глобальным, он доступен любому документу, который вы создаете. Вы могли бы при желании поместить весь свой код в Normal.dot и создавать все документы в своей среде на основе собственного шаблона Normal (Обычный). Но тогда его файл мог бы стать чрезмерно большим, поэтому более эффективное решение для многих разработчиков — создание собственных шаблонов для конкретных приложений. В документах, создаваемых на основе вашего шаблона, код из стандартного шаблона Normal по-прежнему будет доступен. При необходимости можно связывать документ с несколькими шаблонами в дополнение к шаблону Normal.

      Для работы с приложением Microsoft Word в .NET, используется объект Application, который является предком всех остальных объектов. Получив на него ссылку, вы можете работать с его методами и свойствами. Этот объект предоставляет большой набор методов и свойств, позволяющих программным путем управлять Microsoft Word. Код инициализации нового объекта Application, представлен ниже.

Microsoft.Office.Interop.Word.Application winword = 
  new Microsoft.Office.Interop.Word.Application();

Чтобы открыть существующий документ или создать новый, необходимо создать новый объект Document.

object missing = System.Reflection.Missing.Value;
Microsoft.Office.Interop.Word.Document document =
     winword.Documents.Add(ref missing, ref missing, ref missing, ref missing);

      Выполняя какую-либо операцию в пользовательском интерфейсе Word (например, добавляя верхний колонтитул), вы выделяете соответствующую секцию, используя объект «Selection», определяющий текущую позицию «невидимого» курсора и применяете к ней новый параметр форматирования с использованием объекта «Range». Данный объект представляет область в документе и может включать в себя все что угодно — от пары символов, до таблиц, закладок и много другого. Вы не ограничены одним объектом «Range» — в одном документе можно определить сразу несколько таких объектов.

//Добавление верхнего колонтитула
foreach (Microsoft.Office.Interop.Word.Section section in document.Sections)
{                   
    Microsoft.Office.Interop.Word.Range headerRange = 
    section.Headers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
    headerRange.Fields.Add(headerRange, Microsoft.Office.Interop.Word.WdFieldType.wdFieldPage);
    headerRange.ParagraphFormat.Alignment = 
    Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphCenter;
    headerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdBlue;
    headerRange.Font.Size = 10;
    headerRange.Text = "Верхний колонтитул" + Environment.NewLine + "www.CSharpCoderR.com";
}

//Добавление нижнего колонтитула
foreach (Microsoft.Office.Interop.Word.Section wordSection in document.Sections)
{                   
    Microsoft.Office.Interop.Word.Range footerRange =
   wordSection.Footers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
    
    footerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdDarkRed;
    footerRange.Font.Size = 10;
    footerRange.ParagraphFormat.Alignment = 
    Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphCenter;
    footerRange.Text = "Нижний колонтитул" + Environment.NewLine + "www.CSharpCoderR.com";
}

Чтобы добавить текст в документ, необходимо указать позицию для вставки и сам текст.

//Добавление текста в документ
document.Content.SetRange(0, 0);
document.Content.Text = "www.CSharpCoderR.com" + Environment.NewLine;

Так же вы можете применить к тексту определенный стиль.

//Добавление текста со стилем Заголовок 1
Microsoft.Office.Interop.Word.Paragraph para1 = document.Content.Paragraphs.Add(ref missing);
object styleHeading1 = "Заголовок 1";
para1.Range.set_Style(styleHeading1);
para1.Range.Text = "Исходники по языку программирования CSharp";
para1.Range.InsertParagraphAfter();

      В классе Microsoft.Office.Interop.Word.Document, присутствует коллекция «Tables», которая позволяет добавить таблицу в документ с использованием метода Add.

//Создание таблицы 5х5
Table firstTable = document.Tables.Add(para1.Range, 5, 5, ref missing, ref missing);

firstTable.Borders.Enable = 1;
foreach (Row row in firstTable.Rows)
{
    foreach (Cell cell in row.Cells)
    {
        //Заголовок таблицы
        if (cell.RowIndex == 1)
        {
            cell.Range.Text = "Колонка " + cell.ColumnIndex.ToString();
            cell.Range.Font.Bold = 1;
            //Задаем шрифт и размер текста
            cell.Range.Font.Name = "verdana";
            cell.Range.Font.Size = 10;                                                     
            cell.Shading.BackgroundPatternColor = WdColor.wdColorGray25;
            //Выравнивание текста в заголовках столбцов по центру
            cell.VerticalAlignment = 
                 WdCellVerticalAlignment.wdCellAlignVerticalCenter;
            cell.Range.ParagraphFormat.Alignment = 
                 WdParagraphAlignment.wdAlignParagraphCenter;
        }
        //Значения ячеек
        else
        {
            cell.Range.Text = (cell.RowIndex - 2 + cell.ColumnIndex).ToString();
        }
    }
}

       Для функционирования описанного выше кода, необходимо добавить к текущему проекту объектную библиотеку MS Word. Перейдите в меню «Проект» и выберете команду «Добавить ссылку» или в обозревателе решений, найдите пункт «Ссылки» и сделайте клик правой клавишей мыши по нему, из появившегося контекстного меню выберете соответствующий пункт.

      В открывшемся окне «Добавить ссылку», перейдите на вкладку «COM» и выберете «Microsoft Word 14.0 Object Library» из предложенного списка библиотек.

В обозревателе решений у вас появится ссылка на данную библиотеку.

      Перейдите в конструктор главной формы и добавьте из панели элементов, командную кнопку «Button». Данный элемент необходим для запуска процесса создания документа и вызов MS Word для отображения.

      Сделайте двойной клик левой клавишей мыши по элементу «Button» и вы перейдете в автоматически созданный метод события «button1_Click». Добавьте в него приведенный ниже код.

try
{               
    Microsoft.Office.Interop.Word.Application winword = 
        new Microsoft.Office.Interop.Word.Application();
    
    winword.Visible = false;

    //Заголовок документа
    winword.Documents.Application.Caption = "www.CSharpCoderR.com";

    object missing = System.Reflection.Missing.Value;

    //Создание нового документа
    Microsoft.Office.Interop.Word.Document document =
        winword.Documents.Add(ref missing, ref missing, ref missing, ref missing);

    //добавление новой страницы
    //winword.Selection.InsertNewPage();

    //Добавление верхнего колонтитула
    foreach (Microsoft.Office.Interop.Word.Section section in document.Sections)
    {                   
        Microsoft.Office.Interop.Word.Range headerRange = section.Headers[
        Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
        headerRange.Fields.Add(
       headerRange, Microsoft.Office.Interop.Word.WdFieldType.wdFieldPage);
        headerRange.ParagraphFormat.Alignment = 
       Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphCenter;
        headerRange.Font.ColorIndex = 
       Microsoft.Office.Interop.Word.WdColorIndex.wdBlue;
        headerRange.Font.Size = 10;
        headerRange.Text = "Верхний колонтитул" + Environment.NewLine + "www.CSharpCoderR.com";
    }

    //Добавление нижнего колонтитула
    foreach (Microsoft.Office.Interop.Word.Section wordSection in document.Sections)
    { 
        //
        Microsoft.Office.Interop.Word.Range footerRange =
wordSection.Footers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;
        //Установка цвета текста
        footerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdDarkRed;
        //Размер
        footerRange.Font.Size = 10;
        //Установка расположения по центру
        footerRange.ParagraphFormat.Alignment = 
            Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphCenter;
        //Установка текста для вывода в нижнем колонтитуле
        footerRange.Text = "Нижний колонтитул" + Environment.NewLine + "www.CSharpCoderR.com";
    }

    //Добавление текста в документ
    document.Content.SetRange(0, 0);
    document.Content.Text = "www.CSharpCoderR.com" + Environment.NewLine;

    //Добавление текста со стилем Заголовок 1
    Microsoft.Office.Interop.Word.Paragraph para1 = document.Content.Paragraphs.Add(ref missing);
    object styleHeading1 = "Заголовок 1";
    para1.Range.set_Style(styleHeading1);
    para1.Range.Text = "Исходники по языку программирования CSharp";
    para1.Range.InsertParagraphAfter();

    //Создание таблицы 5х5
    Table firstTable = document.Tables.Add(para1.Range, 5, 5, ref missing, ref missing);

    firstTable.Borders.Enable = 1;
    foreach (Row row in firstTable.Rows)
    {
        foreach (Cell cell in row.Cells)
        {
            //Заголовок таблицы
            if (cell.RowIndex == 1)
            {
                cell.Range.Text = "Колонка " + cell.ColumnIndex.ToString();
                cell.Range.Font.Bold = 1;
                //Задаем шрифт и размер текста
                cell.Range.Font.Name = "verdana";
                cell.Range.Font.Size = 10;                                                     
                cell.Shading.BackgroundPatternColor = WdColor.wdColorGray25;
                //Выравнивание текста в заголовках столбцов по центру
                cell.VerticalAlignment =
                WdCellVerticalAlignment.wdCellAlignVerticalCenter;
                cell.Range.ParagraphFormat.Alignment = 
                WdParagraphAlignment.wdAlignParagraphCenter;
            }
            //Значения ячеек
            else
            {
                cell.Range.Text = (cell.RowIndex - 2 + cell.ColumnIndex).ToString();
            }
        }
    }
    winword.Visible = true;
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

      Запустите ваш проект, нажав на клавиатуре, клавишу «F5». Нажмите на единственную кнопку, расположенную на главной форме вашего проекта. У вас автоматически откроется документ Microsoft Word с заполненным верхним и нижним колонтитулом, обычным и с применением стиля текстом, а так же заполненной таблицей.

      Для сохранения документа в определенной директории, добавьте приведенный ниже код, после строки «winword.Visible = true;».

//Сохранение документа
object filename = @"d:temp1.docx";
document.SaveAs(ref filename);
//Закрытие текущего документа
document.Close(ref missing, ref missing, ref missing);
document = null;
//Закрытие приложения Word
winword.Quit(ref missing, ref missing, ref missing);
winword = null;

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

С выходом .NET5 дальнейшее развитие некоторых проектов оказалось под вопросом из-за сложности портирования. Если от небольших устаревших библиотек можно отказаться или найти им замену, то от зависимости Microsoft.Office.Interop.Word.dll очень сложно отказаться. Microsoft не планирует добавлять совместимость с .NET Core/5+, поэтому в этой статье мы рассмотрим, как создавать документы Word с помощью Open XML SDK.

Введение

Office Open XML, также известный как OpenXML или OOXML, представляет собой формат на основе XML для офисных документов, включая текстовые документы, электронные таблицы, презентации, а также диаграммы, фигуры и другой графический материал. В июне 2014 года Microsoft выпустила исходный код Open XML SDK на GitHub для работы с таким форматом.

У этой библиотеки есть серьёзные преимущества:

  • совместима с .NET 5+,

  • не требует установки Microsoft Office,

  • высокая скорость работы,

  • открытый исходный код.

Без минусов тоже не обошлось:

  • сложный API,

  • скудная документация.

Эти минусы определённо дополняют друг друга. Собственно, это и стало причиной создания этого материала.

А вот открытый исходный код является большим плюсом. Если бы код COM-библиотек был открыт, сообщество разработчиков помогло бы с портированием на .NET Core/5+. Кроме привлечения сторонних разработчиков, публичный код даёт каждому возможность находить и исправлять ошибки и уязвимости или хотя бы сообщать о них. Качество публичных библиотек очень важно для всех проектов, которые могут их использовать. Например, мы проводили небольшой аудит кода Open XML SDK при первом знакомстве с этой библиотекой.

Боль разработчиков Office

Для продуктов Office было разработано очень много софта сторонними разработчиками. Это плагины для Word, Excel, Outlook. Многие компании наделали себе удобных плагинов и генераторов отчётов в формате Word. А 3 июля 2021 года произошло страшное – все тикеты про поддержку .NET 5+ в VSTO / COM, разбросанные по разным ресурсам, были в одночасье закрыты с комментарием представителей Microsoft подобного рода:

…The VSTO/COM Add-Ins platform is very important to Microsoft, and we plan to continue to support it in Office with .NET Framework 4.8 as the last major version…VSTO/COM Add-Ins cannot be created with .NET Core and .NET 5+. This is because .NET Core/.NET 5+ cannot work together with .NET Framework in the same process and may lead to add-in load failures. Microsoft will not be updating VSTO or the COM Add-in platform to use .NET Core or .NET 5+…

По их информации, поддержка .NET 5+ не предвидится. Вот одна из таких дискуссий, которая ещё долго не прекращалась после этого объявления: «Please port Visual Studio Tools For Office (VSTO) to .NET 5/7, to enable VSTO add-in development in C# in .Net 5/7».

Если у разработчиков плагинов совсем всё плохо – им предложили перейти на Office JavaScript API (совсем другой язык, API не позволяет делать и малую часть того, что было), то для создания документов из C# кода можно попробовать перейти на библиотеку Open XML SDK (nuget).

Основы

Прежде чем приступить к разбору примеров, стоит понять, с чем вообще работают эти две библиотеки и в чём состоит разница их подходов.

Word документ — это набор запакованных xml-документов. Все элементы структурированы под тегами.

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

<w:p w:rsidR="007D2247" w:rsidRDefault="009A4B44"
         xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:r>
    <w:t>тест</w:t>
  </w:r>
  <w:bookmarkStart w:name="_GoBack" w:id="0" />
  <w:bookmarkEnd w:id="0" />
</w:p>

Сборка Interop.Word немного абстрагируется от этой структуры и часто работает с некоторым участком – Range – документа. А Open XML SDK идёт по пути отражения внутренней структуры документа в самом коде. Параграфы <w:p>, участки текста <w:t> и всё остальное становятся объектами в самом коде. Если вы не создадите тело документа, параграф и других обязательных «родителей», то и добавлять текст будет некуда.

На скриншоте как раз изображена внутренняя структура основного файла для документа Word – document.xml. Этот файл содержит само наполнение документа.

Скриншот сделан в очень нужной для работы с Open XML утилите Open XML SDK 2.5 Productivity Tool. К моменту написания статьи эта утилита была удалена с сайта Microsoft, а в репозитории Open-XML-SDK добавлена ссылка на некий DocxToSource, который должен стать заменой устаревшего Productivity Tool. Однако эта замена всё ещё является прототипом, поэтому пока лучше постараться найти старый добрый Productivity Tool. Старая утилита позволяет просмотреть строение документа, познакомиться с автогенерированным кодом.

Также она позволяет сравнить два разных документа (и код для их создания, и их внутреннее строение).

Примеры

Для Interop.Word во всей статье примем такой псевдоним для удобства чтения:

using MicrosoftWord = Microsoft.Office.Interop.Word;

Также для упрощения будем называть Open XML SDK просто Open XML.

Создание документа

Interop.Word:

MicrosoftWord.Application wordApp = new MicrosoftWord.Application();
MicrosoftWord.Document wordDoc = wordApp.Documents.Add();
MicrosoftWord.Range docRange = wordDoc.Range();
.... // тут при необходимости работаем с документом
wordDoc.SaveAs2(pathToDocFile);
wordApp.Quit();

Тут всё достаточно просто, но всё равно есть свои подводные камни. При работе с Interop мы взаимодействуем не просто с некоторым объектом в памяти, а с COM-объектом. Поэтому возникает необходимость завершать все процессы после окончания работы программы. Эта проблема не раз поднималась на Stack Overflow (1, 2), и ей предложено множество разных решений.

Есть решение с участием Marshal Class, являющимся частью InteropServices.

finally
{
  if (Marshal.IsComObject(wordDoc))
    try
    {
      Marshal.FinalReleaseComObject(wordDoc);
    }
    catch { throw; }
 
  if (Marshal.IsComObject(wordApp))
    try
    {
      Marshal.FinalReleaseComObject(wordApp);
    }
    catch { throw; }
}

Однако в таком случае можно упустить какие-нибудь процессы.

Есть более надёжный вариант с обращением к GC:

GC.Collect();
GC.WaitForPendingFinalizers();

Эти методы надо вызвать после того, как вся работа с COM-объектами будет завершена.

Если не завершить процессы, то при активном дебаге можно устроить себе такую ситуацию:

Но даже если в коде присутствует закрытие процессов после окончания работы, при прерывании программы вручную или её падении процесс останется запущенным. Такого недостатка нет при создании и работе с документом через Open XML.

Open XML:

using (WordprocessingDocument doc = 
         WordprocessingDocument.Create(pathToDocFile,
                                       WordprocessingDocumentType.Document,
                                       true))
{
  MainDocumentPart mainPart = doc.AddMainDocumentPart();
  mainPart.Document = new Document();
  Body body = mainPart.Document.AppendChild(new Body());
  SectionProperties props = new SectionProperties();
  body.AppendChild(props);
}

Обратите внимание на добавление SectionProperties, они понадобятся нам позже.

Добавление параграфа

Interop.Word

public static void InsertWordText(MicrosoftWord.Document doc,
                                      string text)
{
  MicrosoftWord.Paragraph paragraph = doc.Paragraphs.Add(Missing.Value);
  paragraph.Range.Text = text;
  paragraph.Range.InsertParagraphAfter();
}

Текст также можно сделать жирным или курсивным через параметр Font:

paragraph.Range.Font.Bold = 1;
paragraph.Range.Font.Italic = 1;

Изменить размер шрифта можно через:

paragraph.Range.Font.Size = 14;

Выравнивание текста выполняется через ParagraphFormat.Alignment:

paragraph.Range.ParagraphFormat.Alignment = MicrosoftWord.WdParagraphAlignment
                                                        .wdAlignParagraphCenter;

Open XML:

public static void AddText(WordprocessingDocument doc, string text)
{
  MainDocumentPart mainPart = doc.MainDocumentPart;
  Body body = mainPart.Document.Body;
  Paragraph paragraph = body.AppendChild(new Paragraph());

  Run run = paragraph.AppendChild(new Run());
  run.AppendChild(new Text(text));
  run.PrependChild(new RunProperties());
}

В случае с Open XML жирным или курсивным текст можно сделать через:

run.RunProperties.AddChild(new Bold());
run.RunProperties.AddChild(new Italic());

Изменение размера шрифта в этом случае немного неинтуитивно, но согласуется с общей логикой работы с Open XML:

run.RunProperties.AddChild(new FontSize(){ Val = "14"});

Выравнивание текста:

paragraph.ParagraphProperties.AddChild(new Justification()
                                       {
                                         Val = JustificationValues.Center
                                       });

Важно перед этим не забыть добавить к параграфу свойства:

paragraph.AppendChild(new ParagraphProperties());

Вставка заголовка

Предположим, что нам нужно вписать в документ заголовок. В случае Interop.Word нужно всего лишь небольшое дополнение к вставке текста, чтобы получить заголовок:

Interop.Word:

public static void InsertWordHeading1(MicrosoftWord.Document doc,
                                      string headingText)
{
  MicrosoftWord.Paragraph paragraph = doc.Paragraphs.Add(Missing.Value);
  paragraph.Range.Text = headingText;
  paragraph.Range.set_Style("Heading 1");
  paragraph.Range.InsertParagraphAfter();
}

В этом случае сначала задаём Range для записи нового текста и присваиваем ему стиль Heading 1.

Open XML:

public static void InsertWordHeading1(WordprocessingDocument doc,
                                      string headingText)
{
  MainDocumentPart mainPart = doc.MainDocumentPart;
  Paragraph para = mainPart.Document.Body.AppendChild(new Paragraph());
  Run run = para.AppendChild(new Run());
  run.AppendChild(new Text(headingText));
  para.ParagraphProperties = new ParagraphProperties(
                               new ParagraphStyleId() { Val = "Heading1" });
}

Тут, казалось бы, всё очень похоже. Аналогично добавляем параграф и в случае с Open XML организуем нужную иерархию объектов.

Однако на самом деле в случае с Open XML коварным оказывается добавление стиля. Interop.Word работает с реальным полноценным документом, как если бы вы запустили Word и нажали создать. А вот Open XML работает только с тем, что было создано. И если вы добавляете текст документу, созданному через Open XML, а не через Interop.Word, то в нём будут отсутствовать, например, стили. Соответственно, никакого стиля Heading1 в таком документе не будет. Его нужно сначала добавить.

Удобнее всего добавлять нужный стиль при создании документа. Есть два варианта: перенести стили из готового Word-документа или добавить эти стили вручную.

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

Для второго варианта нам поможет Productivity Tool для Open XML, упоминавшийся ранее. Чтобы получить код, нужный для добавления желаемого стиля, создаём чистый документ Word, используем в нём нужный стиль и «скармливаем» этот документ утилите. Далее через использование кнопки Reflect Code на /word/styles.xml в структуре документа мы получим реализацию метода GeneratePartContent. В нём мы ищем реализацию нужного стиля и всё, что с ним связано, включая StyleParagraphProperties, StyleRunProperties и т.д.

Для стиля Heading 1 нужный нам автосгенерированный код будет выглядеть примерно так:

Style style2 = new Style() { Type = StyleValues.Paragraph,
                             StyleId = "Heading1" };
StyleName styleName2 = new StyleName(){ Val = "heading 1" };
....
style2.Append(styleRunProperties1);

Чтобы добавить перенесённый стиль к генерируемому документу, нужно создать набор стилей Styles и добавить стиль к набору. Далее к документу нужно добавить StyleDefinitionsPart и присвоить группу стилей. Выглядеть это будет вот так:

var styles = new Styles();
styles.Append(style2);
wordDocument.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
wordDocument.MainDocumentPart.StyleDefinitionsPart.Styles = styles;

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

Смена ориентации страницы

Для нашего отчёта нам нужна была именно ландшафтная ориентация страницы.

Interop.Word:

MicrosoftWord.Document wordDoc = wordApp.Documents.Add();
MicrosoftWord.Range docRange = wordDoc.Range();
docRange.PageSetup.Orientation = MicrosoftWord.WdOrientation
                                              .wdOrientLandscape;

У документа получаем нужный Range (страниц или всего документа) и задаём ландшафтную ориентацию.

Open XML:

var sectionProperties = mainPart.Document
                                .Body
                                .GetFirstChild<SectionProperties>();
sectionProperties.AddChild(new PageSize()
{
  Width = (UInt32Value)15840U,
  Height = (UInt32Value)12240U,
  Orient = PageOrientationValues.Landscape
});

C Open XML в этом случае всё не настолько абстрактно, как хотелось бы. Если вы инициализируете в PageSize только поле Orient, то ничего не изменится. Width и Height тоже нужно менять.

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

sectionProperties.AddChild(new PageMargin()
{
  Top = 720,
  Right = Convert.ToUInt32(1440.0),
  Bottom = 360,
  Left = Convert.ToUInt32(1440.0),
  Header = (UInt32Value)450U,
  Footer = (UInt32Value)720U,
  Gutter = (UInt32Value)0U
});

Гиперссылки

Interop.Word:

public static void AddHyperlinkedText(MicrosoftWord.Document doc,
                                      string text,
                                      string url)
{
  MicrosoftWord.Range wrdRng = doc.Bookmarks
                                  .get_Item("\endofdoc")
                                  .Range;
  doc.Hyperlinks.Add(wrdRng, url, TextToDisplay: text);
}

Тут всё просто: как обычно, получаем нужный Range и добавляем гиперссылку. У метода Add много параметров, и можно сконструировать более сложную ссылку.

Open XML:

public static void AddHyperlinkedText(WordprocessingDocument doc,
                                      string text,
                                      string url)
{
  MainDocumentPart mainPart = doc.MainDocumentPart;
  Body body = mainPart.Document.Body;
  Paragraph paragraph = body.AppendChild(new Paragraph());

  var rel = mainPart.AddHyperlinkRelationship(new Uri(url), true);

  Hyperlink hyperlink = new Hyperlink(new Run(
                                    new RunProperties(
                                      new RunStyle 
                                      {
                                        Val = "Hyperlink",
                                      },
                                      new Underline
                                      {
                                        Val = UnderlineValues.Single
                                      },
                                      new Color
                                      {
                                        ThemeColor = ThemeColorValues.Hyperlink
                                      }),
                                      new Text
                                      {
                                        Text = text
                                      })) 
                    {
                      Id = rel.Id 
                    };

  paragraph.AppendChild(hyperlink);
}

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

Картинки

Interop.Word:

public static void InsertWordPicture(MicrosoftWord.Document doc,
                                     string picturePath)
{
  MicrosoftWord.Range wrdRng = doc.Bookmarks.get_Item("\endofdoc")
                                            .Range;
  wrdRng.InlineShapes.AddPicture(picturePath);
}

Тут всё достаточно просто, а с Open XML всё оказалось крайне сложно.

Open XML:

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

new DW.Extent() { Cx = 990000L, Cy = 792000L }

и тут

new A.Extents() { Cx = 990000L, Cy = 792000L }

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

Дело в том, что масштаб отображения картинки здесь завязан на такую вещь, как EMU (English Metric Units).

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

double englishMetricUnitsPerInch = 914400;
double pixelsPerInch = 96;
double englishMetricUnitsPerPixel = englishMetricUnitsPerInch / pixelsPerInch;

double emuWidth = width * englishMetricUnitsPerPixel;
double emuHeight = height * englishMetricUnitsPerPixel;

Тут мы получаем количество EMU на пиксель, приняв значение PPI за 96, и умножаем полученное значение на нужное количество пикселей для ширины и высоты. В итоге у наc есть нужная нам ширина и высота в EMU. Их мы и передаём как Cx и Cy для Extent и Extents:

Cx = (Int64Value)emuWidth, Cy = (Int64Value)emuHeight

Таблицы

Interop.Word:

Генерация таблицы через Interop.Word достаточно прямолинейна. Разберём пример, как можно было бы вставить таблицу из квадратной матрицы строк.

public static void InsertWordTable(MicrosoftWord.Document doc,
                                   string[,] table)
{
  MicrosoftWord.Table oTable;
  MicrosoftWord.Range wrdRng = doc.Bookmarks
                                  .get_Item("\endofdoc")
                                  .Range;

  int rowCount = table.GetLength(0);
  int columnCount = table.GetLength(1);

  oTable = doc.Tables.Add(wrdRng,
                    rowCount,
                    columnCount,
                    DefaultTableBehavior: MicrosoftWord.WdDefaultTableBehavior
                                                       .wdWord9TableBehavior,
                    AutoFitBehavior: MicrosoftWord.WdAutoFitBehavior
                                                  .wdAutoFitWindow);

  for (int i = 0; i < rowCount; i++)
    for (int j = 0; j < columnCount; j++)
      oTable.Cell(i + 1, j + 1).Range.Text = table[i,j];
}

Параметры метода AddDefaultTableBehavior и AutoFitBehavior — как видно из их названия, отвечают за поведение таблицы при необходимости изменения размера под содержимое ячеек. Им присваиваются значения перечислений WdDefaultTableBehavior и WdAutoFitBehavior соответственно. Сам метод Add создаёт в документе таблицу с нужными нам параметрами.

Стиль к таблице можно применить следующим образом:

oTable.set_Style("Grid Table 4 - Accent 1");

Также для красивого выделения первого столбика, если он является заголовочным, можно присвоить true полю oTable.ApplyStyleFirstColumn.

Расстояние между параграфами текста контролируется через oTable.Range.ParagraphFormat.SpaceAfter. Для компактного отображения таблицы можно использовать

oTable.Range.ParagraphFormat.SpaceAfter = 0;

Также можно устанавливать тип написания текста к строкам или колонкам:

oTable.Rows[1].Range.Font.Bold = 1;
oTable.Column[1].Range.Font.Italic = 1;

Используя эти возможности, можно получить вот такую таблицу:

Open XML:

public static void InsertWordTable(WordprocessingDocument doc,
                                   string[,] table)
{
  DocumentFormat.OpenXml.Wordprocessing.Table dTable =
    new DocumentFormat.OpenXml.Wordprocessing.Table();

  TableProperties props = new TableProperties();

  dTable.AppendChild<TableProperties>(props);

  for (int i = 0; i < table.GetLength(0); i++)
  {
    var tr = new TableRow();

    for (int j = 0; j < table.GetLength(1); j++)
    {
      var tc = new TableCell();
      tc.Append(new Paragraph(new Run(new Text(table[i, j]))));

      tc.Append(new TableCellProperties());

      tr.Append(tc);
    }
    dTable.Append(tr);
  }
  doc.MainDocumentPart.Document.Body.Append(dTable);
}

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

Поэтому при проходе по матрице мы для каждой строки создаём TableRow, а потом для каждого элемента в строке создаём TableCell, куда добавляем новые Paragraph, Run и Text с соответствующим значением из матрицы. TableCellProperties лучше также добавить сразу, чем потом при дальнейшей работе с таблицей наткнуться на System.NullReferenceException при попытке добавить свойство ячейке.

Если мы не зададим в TableProperties ни стиля, ни Borders, то таблица будет выглядеть вот так

Границы таблицы формируются через TableBorders.

var borderValues = new EnumValue<BorderValues>(BorderValues.Single);
var tableBorders = new TableBorders( 
                     new TopBorder { Val = borderValues, Size = 4 },
                     new BottomBorder {  Val = borderValues,  Size = 4 },
                     new LeftBorder { Val = borderValues, Size = 4 },
                     new RightBorder { Val = borderValues, Size = 4 },
                     new InsideHorizontalBorder { Val= borderValues, Size = 4 },
                     new InsideVerticalBorder { Val= borderValues, Size = 4 }));

Перечисление BorderValues здесь задаёт стиль границ.

TableBorders нужно добавить к TableProperties через

props.Append(tableBorders);

Границы таблицы можно не задавать, если ей будет присвоен какой-нибудь стиль. Главное не забыть, что стиль сначала нужно добавить к документу.

Задаётся стиль достаточно просто:

TableStyle tableStyle = new TableStyle()
                        {
                          Val = "GridTable4-Accent5"
                        };

Его так же, как и границы, нужно добавить к TableProperties:

props.Append(tableStyle);

Для того чтобы таблица заняла всю ширину страницы можно использовать TableWidth заданную следующим образом:

var tableWidth = new TableWidth()
                 {
                   Width = "5000",
                   Type = TableWidthUnitValues.Pct
                 };

Значение 5000 тут взято «не из воздуха». Тип единицы ширины здесь мы задаём TableWidthUnitValues.Pct – единицы ширины в одну пятидесятую процента страницы или 0,02%. В итоге пять тысяч Pct это 100% ширины страницы.

Этот параметр добавляется к TableProperties аналогичным образом:

props.Append(tableWidth);

Важный момент: TableProperties должны быть добавлены к таблице до самих данных для того, чтобы они работали корректно. Их можно добавить и после других объектов, но тогда уже стоит использовать

dTable.PrependChild<TableProperties>(props);

Раскраска таблиц

Для формирования нашего отчёта нам нужно было раскрасить ячейки в некоторых таблицах документа.

Interop.Word:

oTable.Cell(i, j).Range.Shading.BackgroundPatternColor = MicrosoftWord.WdColor
                                                                    .wdColorRed;

где oTable – это созданная нами ранее таблица, i и j — это индексы нужной ячейки. Присваиваемое значение – перечисление WdColor.

Open XML:

tc.Append(new TableCellProperties(
            new Shading { Fill = "FF0000" }));

где tc – это TableCell, с которой идёт работа. Полю Fill присваивается строка с Hex-значением цвета.

Разрыв страницы

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

Interop.Word:

public static void InsertWordBreak(MicrosoftWord.Document doc)
{
  MicrosoftWord.Range wrdRng = doc.Bookmarks.get_Item("\endofdoc")
                                            .Range;
  wrdRng.InsertBreak();
}

Open XML:

public static void InsertWordBreak(WordprocessingDocument doc)
{
  MainDocumentPart mainPart = doc.MainDocumentPart;
  mainPart.Document.Body.InsertAfter(new Paragraph(
                                       new Run(
                                         new Break()
                                         { 
                                           Type = BreakValues.Page
                                         })),
                                     mainPart.Document.Body.LastChild);
}

Тип разрыва меняется через перечисление BreakValues.

Footer/Header

Также нам нужны были футеры/хедеры в документе.

Interop.Word:

public static void InsertWordFooter(
  MicrosoftWord.Document doc,
  string headerText)
{
  MicrosoftWord.Range headerRange = doc.Sections
                                 .Last
                                 .Headers[MicrosoftWord.WdHeaderFooterIndex
                                                       .wdHeaderFooterPrimary]
                                 .Range;

  headerRange.Fields.Add(headerRange, MicrosoftWord.WdFieldType.wdFieldPage);
  headerRange.Text = headerText;
}

Через headerRange.Font можно поменять параметры текста, например размер, шрифт, цвет и т.д. А headerRange.ParagraphFormat.Alignment, как следует из названия, задаёт выравнивание текста. Это поле принимает значения WdParagraphAlignment.

Open XML:

Тут сложность состоит в том, что футер/хэдер сам по себе хранится в отдельном .xml файлике. Поэтому нам нужно связать хэдер/футер с содержанием документа через SectionProperties.

static void InsertWordHeader(HeaderPart part,
                             string headerText)
{
  MainDocumentPart mainPart = doc.MainDocumentPart;

  if (mainPart.HeaderParts.Any())
    return;

  HeaderPart headerPart = mainPart.AddNewPart<HeaderPart>();

  string headerPartId = mainPart.GetIdOfPart(headerPart);

  part.Header = new Header(
                  new Paragraph(
                    new ParagraphProperties(
                      new ParagraphStyleId() { Val = "Header" }),
                      new Run( new Text() { Text = headerText })));

  var sectionProperties = mainPart.Document
                                  .Body
                                  .GetFirstChild<SectionProperties>();
  sectionProperties.PrependChild<HeaderReference>(new HeaderReference()
                                                  {
                                                    Id = headerPartId
                                                  });
}

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

if (mainPart.HeaderParts.Any())
  return;

можно использовать

mainDocumentPart.DeleteParts(mainDocumentPart.HeaderParts);

Для футера нужно будет передать mainDocumentPart.FooterParts.

Заключение

Описанные методы работы с Open XML SDK можно собрать в библиотеку классов для внутреннего использования в компании, что мы и сделали. Создание Word документов стало даже удобнее, чем было с Word Interop API.

Здесь может возникнуть закономерный вопрос, есть ли готовые библиотеки на основе Open XML SDK для упрощённой работы с документами? Ответ – однозначно да. Но, к сожалению, поддержка таких библиотек быстро прекращается. Истории создания таких проектов все одинаковые: программисты начинают работать с Word, осознают неудобство существующей инфраструктуры, дорабатывают её — и некоторые библиотеки публикуются на GitHub. Даже если удастся найти относительно свежую версию подобной библиотеки, то, скорее всего, она была реализована под задачи конкретного проекта, и в вашем проекте всё равно будет неудобной в использовании. Плюс появится риск остаться с неподдерживаемой библиотекой.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Victoria Khanieva, Svyatoslav Razmyslov. Tutorial: how to port a project from Interop Word API to Open XML SDK.

This article shows how to create a Word document using C# and Office 2013.

Step 1: Create a simple Windows application and place a button control in it.

Step 2: Double-click the button control and go to the code widow.

Step 3: Add a reference for «Microsoft.Office.Interop.Word» as shown in the following image.
 

Clipboard02.jpg

Step 4: Copy and paste the following code to generate the Word document.
 

The code is self-explanatory and the required comments are added wherever they are required.

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     CreateDocument();  
  4. }  
  5.   
  6.   
  7. private void CreateDocument()  
  8. {  
  9.     try  
  10.     {  
  11.           
  12.         Microsoft.Office.Interop.Word.Application winword = new Microsoft.Office.Interop.Word.Application();  
  13.   
  14.           
  15.         winword.ShowAnimation = false;  
  16.   
  17.           
  18.         winword.Visible = false;  
  19.             
  20.           
  21.         object missing = System.Reflection.Missing.Value;  
  22.   
  23.           
  24.         Microsoft.Office.Interop.Word.Document document = winword.Documents.Add(ref missing, ref missing, ref missing, ref missing);  
  25.             
  26.           
  27.         foreach (Microsoft.Office.Interop.Word.Section section in document.Sections)  
  28.         {  
  29.               
  30.             Microsoft.Office.Interop.Word.Range headerRange = section.Headers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;  
  31.             headerRange.Fields.Add(headerRange, Microsoft.Office.Interop.Word.WdFieldType.wdFieldPage);  
  32.             headerRange.ParagraphFormat.Alignment = Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphCenter;  
  33.             headerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdBlue;  
  34.             headerRange.Font.Size = 10;  
  35.             headerRange.Text = «Header text goes here»;  
  36.         }  
  37.   
  38.           
  39.         foreach (Microsoft.Office.Interop.Word.Section wordSection in document.Sections)  
  40.         {  
  41.               
  42.             Microsoft.Office.Interop.Word.Range footerRange = wordSection.Footers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range;  
  43.             footerRange.Font.ColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdDarkRed;  
  44.             footerRange.Font.Size =10;  
  45.             footerRange.ParagraphFormat.Alignment = Microsoft.Office.Interop.Word.WdParagraphAlignment.wdAlignParagraphCenter;  
  46.             footerRange.Text = «Footer text goes here»;  
  47.           }  
  48.   
  49.           
  50.         document.Content.SetRange(0, 0);  
  51.         document.Content.Text = «This is test document «+ Environment.NewLine;  
  52.             
  53.           
  54.         Microsoft.Office.Interop.Word.Paragraph para1 = document.Content.Paragraphs.Add(ref missing);                  
  55.         object styleHeading1 = «Heading 1»;  
  56.         para1.Range.set_Style(ref styleHeading1);                  
  57.         para1.Range.Text = «Para 1 text»;  
  58.         para1.Range.InsertParagraphAfter();  
  59.   
  60.           
  61.         Microsoft.Office.Interop.Word.Paragraph para2 = document.Content.Paragraphs.Add(ref missing);  
  62.         object styleHeading2 = «Heading 2»;  
  63.         para2.Range.set_Style(ref styleHeading2);  
  64.         para2.Range.Text = «Para 2 text»;  
  65.         para2.Range.InsertParagraphAfter();  
  66.   
  67.           
  68.         Table firstTable = document.Tables.Add(para1.Range, 5, 5, ref missing, ref missing);  
  69.             
  70.         firstTable.Borders.Enable = 1;  
  71.         foreach (Row row in firstTable.Rows)  
  72.         {  
  73.             foreach (Cell cell in row.Cells)  
  74.             {  
  75.                   
  76.                 if (cell.RowIndex == 1)  
  77.                 {  
  78.                     cell.Range.Text = «Column « + cell.ColumnIndex.ToString();  
  79.                     cell.Range.Font.Bold = 1;  
  80.                       
  81.                     cell.Range.Font.Name = «verdana»;  
  82.                     cell.Range.Font.Size = 10;  
  83.                       
  84.                     cell.Shading.BackgroundPatternColor = WdColor.wdColorGray25;  
  85.                       
  86.                     cell.VerticalAlignment = WdCellVerticalAlignment.wdCellAlignVerticalCenter;  
  87.                     cell.Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter;  
  88.                         
  89.                 }  
  90.                   
  91.                 else  
  92.                 {  
  93.                     cell.Range.Text = (cell.RowIndex — 2 + cell.ColumnIndex).ToString();  
  94.                 }  
  95.             }  
  96.         }  
  97.             
  98.           
  99.         object filename = @«c:temp1.docx»;  
  100.         document.SaveAs2(ref filename);  
  101.         document.Close(ref missing, ref missing, ref missing);  
  102.         document = null;  
  103.         winword.Quit(ref missing, ref missing, ref missing);  
  104.         winword = null;  
  105.         MessageBox.Show(«Document created successfully !»);  
  106.     }  
  107.     catch (Exception ex)  
  108.     {  
  109.         MessageBox.Show(ex.Message);  
  110.     }  

Step 5: Once the code is executed successfully, the document output will be:

Clipboard03.jpg

 

The source code is attached, please post your feedback in the comments section.
 

Hope this helps someone.

        public ResultType_enum Create(Document document, Word.Application application, out Word._Document doc, out string message)
        {
            try
            {
                isWork = true;
                message = "";

                Object trueObj = true;
                Object falseObj = false;
                Object begin = Type.Missing;
                Object end = Type.Missing;
                doc = null;

                // если вылетим не этом этапе, приложение останется открытым
                try
                {
                    doc = application.Documents.Add(ref missingObj, ref missingObj, ref missingObj, ref missingObj);
                    // Общий Стиль
                    object patternstyle = Word.WdStyleType.wdStyleTypeParagraph;
                    Word.Style wordstyle = doc.Styles.Add("myStyle", ref patternstyle);
                    wordstyle.Font.Size = 9;
                    wordstyle.Font.Name = "Times New Roman";
                    Word.Range wordrange = doc.Range(ref begin, ref end);
                    object oWordStyle = wordstyle;
                    wordrange.set_Style(ref oWordStyle);

                    // Вставляем параграфы
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);

                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);
                    doc.Paragraphs.Add(ref missingObj);

                    doc.Paragraphs[1].Range.Text = "«Утверждаю»";
                    doc.Paragraphs[1].Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft;
                    doc.Paragraphs[1].Range.ParagraphFormat.LeftIndent = 300;
                    doc.Paragraphs[1].Range.ParagraphFormat.LineSpacing = 11;
                    doc.Paragraphs[1].SpaceAfter = 0;
                    doc.Paragraphs[1].SpaceBefore = 0;
                    doc.Paragraphs[2].LineUnitAfter = 0;
                    doc.Paragraphs[2].LineUnitBefore = 0;
                    doc.Paragraphs[3].LineUnitAfter = 0;
                    doc.Paragraphs[3].LineUnitBefore = 0;
                    doc.Paragraphs[4].LineUnitAfter = 0;
                    doc.Paragraphs[4].LineUnitBefore = 0;

                    doc.Paragraphs[2].Range.Text = "Руководитель";
                    doc.Paragraphs[2].Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft;
                    doc.Paragraphs[2].Range.ParagraphFormat.LeftIndent = 300;

                    doc.Paragraphs[4].Range.Text = "Форма 2. Сведения о качестве, технических характеристиках товара, его безопасности, функциональных характеристиках (потребительских свойствах) товара, размере и иные сведения о товаре, представление которых предусмотрено документацией об аукционе в электронной форме";
                    doc.Paragraphs[4].Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                    // Подсчитываем количество строк у продуктов (потому что одно свойство продукта занимает одну строку)
                    int propertiesCount = 0;
                    foreach (Product product in document.Products)
                    {
                        var myTemplate = product.Templates.FirstOrDefault(m => m.Name.Trim().ToLower() == "форма 2");
                        IEnumerable<Property> productProperties = product.Properties.SelectMany(m => m.ParamValues.Where(p => myTemplate.Param.Contains(p.Param))).Select(f => f.Property).Distinct();

                        propertiesCount += productProperties.Count();
                    }

                    Object defaultTableBehavior =
                     Word.WdDefaultTableBehavior.wdWord9TableBehavior;
                    Object autoFitBehavior =
                     Word.WdAutoFitBehavior.wdAutoFitWindow;
                    Word.Table wordtable = doc.Tables.Add(doc.Paragraphs[5].Range, 3 + propertiesCount, 8,
                      ref defaultTableBehavior, ref autoFitBehavior);

                    // Объединение ячеек
                    object begCell = wordtable.Cell(1, 1).Range.Start;
                    object endCell = wordtable.Cell(2, 1).Range.End;
                    Word.Range wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();

                    begCell = wordtable.Cell(1, 2).Range.Start;
                    endCell = wordtable.Cell(2, 2).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();

                    begCell = wordtable.Cell(1, 3).Range.Start;
                    endCell = wordtable.Cell(2, 3).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();

                    begCell = wordtable.Cell(1, 4).Range.Start;
                    endCell = wordtable.Cell(1, 6).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();

                    begCell = wordtable.Cell(1, 5).Range.Start;
                    endCell = wordtable.Cell(2, 7).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();

                    begCell = wordtable.Cell(1, 6).Range.Start;
                    endCell = wordtable.Cell(2, 8).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();

                    /*begCell = wordtable.Cell(4, 6).Range.Start;
                    endCell = wordtable.Cell(propertiesCount + 4, 6).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Cells.Merge();*/

                    // Окраска строки с номерами
                    begCell = wordtable.Cell(3, 1).Range.Start;
                    endCell = wordtable.Cell(3, 8).Range.End;
                    wordcellrange = doc.Range(ref begCell, ref endCell);
                    wordcellrange.Select();
                    application.Selection.Shading.BackgroundPatternColor = Word.WdColor.wdColorGray10;

                    // Заполнение заголовка
                    doc.Tables[1].Cell(1, 1).Range.Text = "№ п/п";
                    doc.Tables[1].Cell(1, 1).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(1, 2).Range.Text = "Наименование материала";
                    doc.Tables[1].Cell(1, 2).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(1, 3).Range.Text = "Указание на товарный знак (его словесное обозначение) (при наличии), знак обслуживания (при наличии), фирменное наименование (при наличии), патенты (при наличии), полезные модели (при наличии), промышленные образцы (при наличии), наименование страны происхождения товара";
                    doc.Tables[1].Cell(1, 3).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(1, 4).Range.Text = "Технические характеристики";
                    doc.Tables[1].Cell(1, 4).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                    doc.Tables[1].Cell(2, 4).Range.Text = "Требуемый параметр";
                    doc.Tables[1].Cell(2, 4).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(2, 5).Range.Text = "Требуемое значение";
                    doc.Tables[1].Cell(2, 5).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(2, 6).Range.Text = "Значение, предлагаемое участником";
                    doc.Tables[1].Cell(2, 6).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                    doc.Tables[1].Cell(1, 5).Range.Text = "Единица измерения";
                    doc.Tables[1].Cell(1, 5).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(1, 6).Range.Text = "Сведения о сертификации";
                    doc.Tables[1].Cell(1, 6).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                    doc.Tables[1].Cell(3, 1).Range.Text = "1";
                    doc.Tables[1].Cell(3, 1).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 2).Range.Text = "2";
                    doc.Tables[1].Cell(3, 2).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 3).Range.Text = "3";
                    doc.Tables[1].Cell(3, 3).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 4).Range.Text = "4";
                    doc.Tables[1].Cell(3, 4).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 5).Range.Text = "5";
                    doc.Tables[1].Cell(3, 5).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 6).Range.Text = "6";
                    doc.Tables[1].Cell(3, 6).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 7).Range.Text = "7";
                    doc.Tables[1].Cell(3, 7).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                    doc.Tables[1].Cell(3, 8).Range.Text = "8";
                    doc.Tables[1].Cell(3, 8).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                    // Заполняем продукты
                    ParamValue paramValue = null;
                    int productIndexCompilator = 0;
                    int propertyIndexCompilator = 0;
                    for (int i = 0; i < propertiesCount; i++)
                    {
                        if (!isWork) break;
                        // Получаем свойства продукта на шаблон
                        var myTemplate = document.Products.ElementAt(productIndexCompilator).Templates.FirstOrDefault(m => m.Name.Trim().ToLower() == "форма 2");
                        IEnumerable<Property> productProperties = document.Products.ElementAt(productIndexCompilator).Properties.SelectMany(m => m.ParamValues.Where(p => myTemplate.Param.Contains(p.Param))).Select(f => f.Property).Distinct();

                        if (propertyIndexCompilator == 0)
                        {
                            // Объединяем ячейки по продукту (т.к. свойство занимает строку)
                            begCell = wordtable.Cell(i + 4, 1).Range.Start;
                            endCell = wordtable.Cell(i + 4 + productProperties.Count() - 1, 1).Range.End;
                            wordcellrange = doc.Range(ref begCell, ref endCell);
                            wordcellrange.Select();
                            try
                            {
                                application.Selection.Cells.Merge();
                            }
                            catch
                            {

                            }

                            begCell = wordtable.Cell(i + 4, 2).Range.Start;
                            endCell = wordtable.Cell(i + 4 + productProperties.Count() - 1, 2).Range.End;
                            wordcellrange = doc.Range(ref begCell, ref endCell);
                            wordcellrange.Select();
                            try
                            {
                                application.Selection.Cells.Merge();
                            }
                            catch
                            {

                            }

                            begCell = wordtable.Cell(i + 4, 3).Range.Start;
                            endCell = wordtable.Cell(i + 4 + productProperties.Count() - 1, 3).Range.End;
                            wordcellrange = doc.Range(ref begCell, ref endCell);
                            wordcellrange.Select();
                            try
                            {
                                application.Selection.Cells.Merge();
                            }
                            catch
                            {

                            }

                            begCell = wordtable.Cell(i + 4, 8).Range.Start;
                            endCell = wordtable.Cell(i + 4 + productProperties.Count() - 1, 8).Range.End;
                            wordcellrange = doc.Range(ref begCell, ref endCell);
                            wordcellrange.Select();
                            try
                            {
                                application.Selection.Cells.Merge();
                            }
                            catch
                            {

                            }

                            doc.Tables[1].Cell(i + 4, 1).Range.Text = Convert.ToString(productIndexCompilator + 1) + '.';
                            doc.Tables[1].Cell(i + 4, 1).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                            doc.Tables[1].Cell(i + 4, 2).Range.Text = document.Products.ElementAt(productIndexCompilator).Name;
                            doc.Tables[1].Cell(i + 4, 2).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphJustify;

                            doc.Tables[1].Cell(i + 4, 3).Range.Text = document.Products.ElementAt(productIndexCompilator).TradeMark;
                            doc.Tables[1].Cell(i + 4, 3).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphJustify;

                            // Сертификация
                            paramValue = productProperties.ElementAt(propertyIndexCompilator).ParamValues.FirstOrDefault(m => m.Param.Name == "Сертификация");
                            if (paramValue != null)
                            {
                                doc.Tables[1].Cell(i + 4, 8).Range.Text = paramValue.Value;
                            }
                            else
                            {
                                doc.Tables[1].Cell(i + 4, 8).Range.Text = "";
                            }

                            doc.Tables[1].Cell(i + 4, 8).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
                        }

                        // Требуемый параметр
                        paramValue = productProperties.ElementAt(propertyIndexCompilator).ParamValues.FirstOrDefault(m => m.Param.Name == "Требуемый параметр");
                        if (paramValue != null)
                        {
                            doc.Tables[1].Cell(i + 4, 4).Range.Text = paramValue.Value;
                        }
                        else
                        {
                            doc.Tables[1].Cell(i + 4, 4).Range.Text = "";
                        }
                        doc.Tables[1].Cell(i + 4, 4).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                        // Требуемое значение
                        paramValue = productProperties.ElementAt(propertyIndexCompilator).ParamValues.FirstOrDefault(m => m.Param.Name == "Требуемое значение");
                        if (paramValue != null)
                        {
                            doc.Tables[1].Cell(i + 4, 5).Range.Text = paramValue.Value;
                        }
                        else
                        {
                            doc.Tables[1].Cell(i + 4, 5).Range.Text = "";
                        }
                        doc.Tables[1].Cell(i + 4, 5).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                        // Значение, предлагаемое участником
                        paramValue = productProperties.ElementAt(propertyIndexCompilator).ParamValues.FirstOrDefault(m => m.Param.Name == "Значение, предлагаемое участником");
                        if (paramValue != null)
                        {
                            doc.Tables[1].Cell(i + 4, 6).Range.Text = paramValue.Value;
                        }
                        else
                        {
                            doc.Tables[1].Cell(i + 4, 6).Range.Text = "";
                        }
                        doc.Tables[1].Cell(i + 4, 6).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                        // Единица измерения
                        paramValue = productProperties.ElementAt(propertyIndexCompilator).ParamValues.FirstOrDefault(m => m.Param.Name == "Единица измерения");
                        if (paramValue != null)
                        {
                            doc.Tables[1].Cell(i + 4, 7).Range.Text = paramValue.Value;
                        }
                        else
                        {
                            doc.Tables[1].Cell(i + 4, 7).Range.Text = "";
                        }
                        doc.Tables[1].Cell(i + 4, 7).Range.Paragraphs.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;

                        propertyIndexCompilator++;
                        if (productProperties.Count() == propertyIndexCompilator)
                        {
                            propertyIndexCompilator = 0;
                            productIndexCompilator++;
                        }

                    }
                }

                catch (Exception er)
                {
                    if (doc != null)
                        doc.Close(ref falseObj, ref missingObj, ref missingObj);
                    doc = null;
                }

                application.Visible = true;
                return ResultType_enum.Done;
            }
            catch (Exception ex)
            {
                doc = null;
                message = ex.Message + 'n' + ex.StackTrace;
                return ResultType_enum.Error;
            }
            finally {
                isWork = false;
            }
        }

Понравилась статья? Поделить с друзьями:
  • Microsoft office interop word documentclass
  • Microsoft office interop word document
  • Microsoft office interop word dll
  • Microsoft office interop word application word
  • Microsoft office interop excel что это