Как отобразить документ word в c

Как уже выше писали, необходима конвертация в формат RTF.

DOC — Бинарный формат, с весьма сложной структурой. Библиотеки, не требующие офиса для работы с ним, продают за большие деньги. Есть пара бесплатных. ОДна опенсорс — порт с джавовской либы POI. Вторая Spire DOC, имеет кучу ограничений на количество обрабатываемого текста.

DOCX — Архив с набором XML-файлов. Есть библиотека от майкрософт OpenXML, которая отлично справляется с docx файлами. Но она немного сложновата для освоения и чаще всего используют оболочку над ней — ClosedXML. Обе библиотеки бесплатные и не требуют установленного офиса. Или можно использовать кучу других бесплатных библиотек для работы с docx. Все они легко ставятся через nuget.

COM Interop, описанный выше, идеально подходит, если стоит офис. Он «из коробки» поддерживает все форматы и прекрасно конвертирует.

Если же офис может отсутствовать, то я бы посоветовал использовать или POI или связку POI/OpenXML.

И надо помнить, что RTF сильно отличается от DOC/DOCX, и результат может отличаться от того, что вы видите MS Word. Потому, если цель — просмотр документов, я бы посоветовал конвертацию в HTML.

Задача: вывести данные в документ Word. На самом деле это очень большая и необъятная тема, примерно как сам Word, 90% возможностей которого не используются обычными пользователями. Сузим до более простой и чаще встречающейся на практике задачи, с которой в своей время пришлось столкнуться мне самому: надо вывести красивую справку, договор, отчет или иной документ Word с добавлением данных из кода C#. Само собой должны поддерживаться версии Word до 2007, так что о новых форматах файлов придется забыть.

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

Задача на текущую статью: открыть из кода C# шаблон Word и что-то в него вставить. Шаблон в формате .dot приготовим заранее, в том же самом ворде. Для связи с ним будем использовать механизм COM Interoperability (сокращенно Interop), то есть запускать отдельный exe-процесс самого Word и через специальный интерфейс управлять им. Интерфейсы слава богу есть и находятся они в специальных библиотеках, поставляемых вместе с Office, но документация по ним крайне невнятная, поведение местами очень странное и не логичное. В версиях Visual Studio 2010 и выше возможности программирования Office расширены, но текущее руководство действительно и для 2008 студии.

Нам надо

1. Подключить нужные библиотеки
2. Открыть шаблон Word
3. Найти в нем нужное место
4. Вставить в него строку с информацией

1. Проект в студии у нас уже должен быть. В разделе Ссылки/References кликаем правой кнопкой, идем в «Добавить ссылку» и ищем Microsoft.Office.Interop.Word. В параметрах добавленной библиотеки ставим true в Копировать локально/Copy local, так как библиотеку надо копировать вместе с исполняемыми файлами проекта.

В код добавляем соответствующие using

using Word = Microsoft.Office.Interop.Word;
using System.Reflection;

2. Теперь вам предстоит провести много времени с замечательным интерфейсом Word, который представляет сам текстовый редактор и его потроха в виде разнообразных обьектов. Сейчас важны два — Application и Document. Переменные для них по ряду не очевидных причин лучше объявлять через интерфейсы.

Word._Application application;
Word._Document document;

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

Object missingObj = System.Reflection.Missing.Value;
Object trueObj = true;
Object falseObj = false;

Чтобы запустить Word и открыть в нем шаблон с диска (путь известен), потребуется примерно такой код

//создаем обьект приложения word
application = new Word.Application();
// создаем путь к файлу 
Object templatePathObj = "путь к файлу шаблона";;

// если вылетим не этом этапе, приложение останется открытым
try
{
    document = application.Documents.Add(ref  templatePathObj, ref missingObj, ref missingObj, ref missingObj);
}
catch (Exception error)
{
    document.Close(ref falseObj, ref  missingObj, ref missingObj);
    application.Quit(ref missingObj, ref  missingObj, ref missingObj);
    document = null;
    application = null;
    throw error;
}
_application.Visible = true;

Принципиально важны два момента

1. Мы создаем неуправляемый ресурс, который не соберет сборщик мусора — отдельный процесс в памяти с приложением Word, если мы его не закроем и не выведем на экран, он так и останется там висеть до выключения компьютера. Более того такие ворды могут накапливаться незаметно для пользователя, программист-то еще прибьет их вручную. Заботиться о высвобождения неуправляемого ресурса должен программист.

2. По умолчанию Word запускается невидимым, на экран его выводим мы.

Для начала рассмотрим самый простой и примитивный вариант — поиск и замена строки в документе Word. Некоторые программисты так и работают — ставят в шаблон текстовую метку вроде @@nowDate и заменяют ее на нужное значение.

Пришло время познакомится с фундаментом работы с Word — великим и ужасным объектом Range. Его суть сложно описать словами -это некоторый произвольный кусок документа, диапазон (range), который может включать в себя все что угодно — от пары символов, до таблиц, закладок и прочих интересных вещей. Не стоит путать его с Selection — куском документа, выделенным мышкой, который само собой можно конвертировать в Range. Соотвественно нам надо получить Range для всего документа, найти нужную строку внутри него, получить Range для этой строки и уже внутри этого последнего диапазона заменить текст на требуемый. И не стоит забывать, что документ может иметь сложную структуру с колонтитулами и прочей ересью, возможный универсальный метод для замены всех вхождений данной строки:

// обьектные строки для Word
object strToFindObj = strToFind;
object replaceStrObj = replaceStr;
// диапазон документа Word
Word.Range wordRange;
//тип поиска и замены
object replaceTypeObj;
replaceTypeObj = Word.WdReplace.wdReplaceAll; 
// обходим все разделы документа
for (int i = 1; i <= _document.Sections.Count; i++)
{
    // берем всю секцию диапазоном
    wordRange = _document.Sections[i].Range;

    /*
    Обходим редкий глюк в Find, ПРИЗНАННЫЙ MICROSOFT, метод Execute на некоторых машинах вылетает с ошибкой "Заглушке переданы неправильные данные / Stub received bad data"  Подробности: http://support.microsoft.com/default.aspx?scid=kb;en-us;313104
    // выполняем метод поиска и  замены обьекта диапазона ворд
    wordRange.Find.Execute(ref strToFindObj, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref replaceStrObj, ref replaceTypeObj, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing);
    */

    Word.Find wordFindObj = wordRange.Find;
    object[] wordFindParameters = new object[15] { strToFindObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, replaceStrObj, replaceTypeObj, _missingObj, _missingObj, _missingObj, _missingObj };

    wordFindObj.GetType().InvokeMember("Execute", BindingFlags.InvokeMethod, null, wordFindObj, wordFindParameters);
}

Редкий глюк подробно описан здесь.

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

Даже если нам надо найти (и например отформатировать) именно строку с текстом внутри документа, лучше всего выдать наружу найденный Range и уже с ним производить разные злодеяния. Получим примерно такой метод:

object stringToFindObj = stringToFind;
Word.Range wordRange;
bool rangeFound;

//в цикле обходим все разделы документа, получаем Range, запускаем поиск
// если поиск вернул true, он долже ужать Range до найденное строки, выходим и возвращаем Range
// обходим все разделы документа
for (int i = 1; i <= _document.Sections.Count; i++)
{
    // берем всю секцию диапазоном
    wordRange = _document.Sections[i].Range;

    /*
    // Обходим редкий глюк в Find, ПРИЗНАННЫЙ MICROSOFT, метод Execute на некоторых машинах вылетает с ошибкой "Заглушке переданы неправильные данные / Stub received bad data"  Подробности: http://support.microsoft.com/default.aspx?scid=kb;en-us;313104
    // выполняем метод поиска и  замены обьекта диапазона ворд
    rangeFound = wordRange.Find.Execute(ref stringToFindObj, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing);
    */

    Word.Find wordFindObj = wordRange.Find;

    object[] wordFindParameters = new object[15] { stringToFindObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj };

    rangeFound = (bool)wordFindObj.GetType().InvokeMember("Execute", BindingFlags.InvokeMethod, null, wordFindObj, wordFindParameters);

    if (rangeFound) { return wordRange; }
}

// если ничего не нашли, возвращаем null
return null;

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

// оформляем обьектные параметры
object stringToFindObj = stringToFind;
bool rangeFound;

/*
Обходим редкий глюк в Find, ПРИЗНАННЫЙ MICROSOFT, метод Execute на некоторых машинах вылетает с ошибкой "Заглушке переданы неправильные данные / Stub received bad data" 
http://support.microsoft.com/default.aspx?scid=kb;en-us;313104
rangeFound = containerRange.Find.Execute(ref stringToFindObj, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing, ref wordMissing);
*/

Word.Find wordFindObj = containerRange.Find;

object[]  wordFindParameters = new object[15] { stringToFindObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj, _missingObj };

rangeFound = (bool)wordFindObj.GetType().InvokeMember("Execute", BindingFlags.InvokeMethod, null, wordFindObj, wordFindParameters);

if (rangeFound) { return containerRange; }
else { return null; }

Если строку надо просто заменить, то сойдет простейшее

_range.Text = "Это текст заменит содержимое Range";

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

Если нам надо просто встать в начало документа (и что-то вставить уже туда):

object start = 0;
object end = 0;
_currentRange = _document.Range(ref start, ref end);

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

Object pathToSaveObj = pathToSaveString;
_document.SaveAs(ref pathToSaveObj, Word.WdSaveFormat.wdFormatDocument, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj, ref _missingObj);
  1. Работаем с MS Word из C#, часть 0, класс и тестовый проект-пример WinForms
  2. Работаем с MS Word из C#, часть 1. Открываем шаблон, ищем текст внутри документа
  3. Работаем с MS Word из C#, часть 2. Вставляем текст на закладку и форматируем
  4. Работаем с MS Word из C#, часть 3. Работа с таблицами
  5. Работаем с MS Word из C#, часть 4. Обьединяем несколько файлов в один, считаем количество страниц
  6. Microsoft.Office.Interop.Word Namespace
  7. Range Interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 using Word = Microsoft.Office.Interop.Word;//добавить в ссылки
    public partial class Form1 : Form
    {
        Button button;
        RichTextBox richTextBox;
        public Form1()
        {
            InitializeComponent();
            richTextBox = new RichTextBox();
            richTextBox.Size = new Size(400, 400);
            button = new Button();
            button.Text = "Открыть";
            button.Location = new Point(10, 10);
            this.Size = new Size(500, 500);
            richTextBox.Location = new Point(10, button.Bottom + 10);
            richTextBox.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
            this.Controls.AddRange(new Control[] { button, richTextBox });
            button.Click += new EventHandler(button_Click);
        }
 
        void button_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "MS Word dosuments (*.docx)|*.docx|Rich text format (*.rtf)|*.rtf";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (openFileDialog.FilterIndex == 1)//если формат документа Word 2007
                {
                    Word.Application app = new Microsoft.Office.Interop.Word.Application();//процесс ворда
                    Object docxFileName = openFileDialog.FileName;//имя файла
                    Object missing = Type.Missing;
                    //открыли дркумент
                    app.Documents.Open(ref docxFileName, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing);
                    //путь к папке с временными файлами
                    string temp = System.IO.Path.GetTempPath();
                    //для передачи параметров при пересохранении
                    Object lookComments = false;
                    Object password = String.Empty;
                    Object AddToRecentFiles = true;
                    Object WritePassword = String.Empty;
                    Object ReadOnlyRecommended = false;
                    Object EmbedTrueTypeFonts = false;
                    Object SaveFormsData = false;
                    Object SaveAsAOCELetter = false;
                    //имя файла без расширения
                    Object rtfFileName = openFileDialog.SafeFileName.Substring(0, openFileDialog.SafeFileName.Length - ".docx".Length);
                    //создали рандом
                    Random random = new Random();
                    //проверяем есть ли файл с таким именем
                    while (System.IO.File.Exists(rtfFileName + ".rtf"))
                        //генерируем случайное имя файла
                        rtfFileName += random.Next(0, 9).ToString();
                    //формат RTF
                    Object wdFormatRTF = Word.WdSaveFormat.wdFormatRTF;
                    //приписали расширение
                    rtfFileName += ".rtf";
                    //приписали путь к временным файлам
                    rtfFileName = temp + rtfFileName;
                    //пересохранили
                    app.ActiveDocument.SaveAs(ref rtfFileName,
                        ref wdFormatRTF, ref lookComments, ref password, ref AddToRecentFiles, ref WritePassword, ref ReadOnlyRecommended,
                        ref EmbedTrueTypeFonts, ref missing, ref SaveFormsData, ref SaveAsAOCELetter, ref missing,
                        ref missing, ref missing, ref missing, ref missing);
                    //переменная
                    Object @false = false;
                    //закрыли текущий документ
                    app.ActiveDocument.Close(ref @false, ref missing, ref missing);
                    //вышли из ворда
                    app.Quit(ref @false, ref missing, ref missing);
                    //прочли файл
                    richTextBox.LoadFile((String)rtfFileName);
                }
                if (openFileDialog.FilterIndex == 2)
                    richTextBox.LoadFile(openFileDialog.FileName);
            }
        }
    }

i need to open a word document in a panel control of Windows Forms Application to view/edit file and save.

i use this statement :

    [DllImport("user32.dll")]
    public static extern int FindWindow(string strclassName, string strWindowName);

    [DllImport("user32.dll")]
    static extern int SetParent(int hWndChild, int hWndNewParent);

    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    static extern bool SetWindowPos(
        int hWnd,               // handle to window
        int hWndInsertAfter,    // placement-order handle
        int X,                  // horizontal position
        int Y,                  // vertical position
        int cx,                 // width
        int cy,                 // height
        uint uFlags             // window-positioning options
    );

    [DllImport("user32.dll", EntryPoint = "MoveWindow")]
    static extern bool MoveWindow(
        int hWnd,
        int X,
        int Y,
        int nWidth,
        int nHeight,
        bool bRepaint
    );

    const int SWP_DRAWFRAME = 0x20;
    const int SWP_NOMOVE = 0x2;
    const int SWP_NOSIZE = 0x1;
    const int SWP_NOZORDER = 0x4;
    const int SWP_FRAMECHANGED = 0x20;
    ToolsComponents.MSWord word = new ToolsComponents.MSWord();

    private void toolStripButton2_Click(object sender, EventArgs e)
    {
        word.CreateWordDocument();
        word.OpenFile(@"C:UsersMEDocumentstest.docx", true);
        int wordWnd = FindWindow("Opusapp", null);
        if (wordWnd != 0)
        {
            int ret = SetParent(wordWnd, pnlShowForm.Handle.ToInt32());

            //int ret2 = FindWindow("Opusapp", null);
            //ret = SetParent(wordWnd, pnlShowForm.Handle.ToInt32());
            SetWindowPos(wordWnd, pnlShowForm.Handle.ToInt32(), 0, 0, pnlShowForm.Bounds.Width - 20, pnlShowForm.Bounds.Height - 20, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOMOVE | SWP_DRAWFRAME);
            MoveWindow(wordWnd, -5, -33, pnlShowForm.Bounds.Width + 10, pnlShowForm.Bounds.Height + 57, true);
        }
    }

    private void frmDocumentManager_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (word != null)
        {
            word.CloseDoc(true);
            word.Quit();
        }

but this is not a good solution and have problem in runtime.
in sometimes MS word and document started outside the form and i can’t control this.

Понравилась статья? Поделить с друзьями:

А вот еще интересные статьи:

  • Как отобразить динамику в excel
  • Как отобразить график в excel
  • Как отобразить диапазон в excel
  • Как отобразить границы таблицы word
  • Как отобразить день недели в excel

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии