вторник, 22 июня 2010 г.

Использование WORD шаблонов.


Внимание! Обратите внимание на обновленную версию статьи "Использование WORD шаблонов C# 4.0 "



Инструментарий:
• Microsoft Visual Studio 2010
• Microsoft Office 2010 Professional Beta

исходники к статье


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

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

Предположим, существует некая компания «Ключи и Отвертки», которая занимается продажами обозначенных инструментов, и менеджеры компании хотят видеть ежедневный отчет о продажах в своих офисах.

Начнем с простого шаблона, который будет представлять из себя фирменный бланк компании и единственное поле для вставки данных. Создадим новый WORD документ и уменьшим отступы (Page Layout -> Margins -> Narrow).

Margins


После этого развернем документ в альбомную ориентацию (Page Layout -> Orientation -> Landscape).

Orientation

Далее создадим шапку (Insert -> Header –> Alpabet).

Footer

Напишем название. Далее впишем дополнительную информацию о компании и пометим ее курсивом с выравниванием по правому краю.

После создания шапки в документ автоматически был добавлен пустой подвал, перейти в редактирование которого можно просто перенеся туда курсор. Визуально отделим его от тела документа при помощи разделительной линии. (Insert –> Shapes -> Lines)

Line

Далее, в подвале сделаем выравнивание по правому краю и создадим закладку (Bookmark), которую будем отыскивать в коде и на ее место вставлять данные (Insert -> Bookmark).

InsertBookmark

И назовем ее AuthorName.

На этом завершим создание фирменного бланка, сохраним шаблон с именем screwdriver.dotx (Word Template)

Следующим шагом создадим из кода новый документ Word, на основе нашего шаблона, впишем автора документа и сохраним его на диск.

Создадим в Visual Studio WPF приложение с названием UsingWordTemplate ( File -> New -> Project -> Windows -> WPF Application).

Добавим на форму Label (Content="Автор:"), TextBox (Name=”autorTxtb”), в который будем вписывать имя автора документа и кнопку (Content="Сохранить" Name=”saveBtn”), с помощью которой будем сохранять созданный документ.

Добавим ссылку на .Net библиотеку Microsoft.Office.Interop.Word Version 14. В шапке кода главного окна добавим


using Word = Microsoft.Office.Interop.Word;


После чего определим несколько переменных


Word._Application oWord = new Word.Application();
object oMissing = System.Reflection.Missing.Value;


Переменная oWord будет представлять процесс WINWORD.EXE в памяти, oMissing представляет из себя аргумент представляющий значение по умолчанию, который требуют методы из пространства Microsoft.Office.Interop, так как они не принимают Null.

Так же определим событие формы Closing, в котором мы будем закрывать процесс WINWORD.EXE


private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
oWord.Quit(ref oMissing, ref oMissing, ref oMissing);
}


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

Вызовем контекстное меню текущего проекта и добавим в него заранее созданный шаблон screwdriver.dotx (Add -> Existing Item -> screwdriver.dotx), в его свойствах определим Copy To Output Directory как Copy always.

Определим обработчик кнопки:


private void saveBtn_Click(object sender, RoutedEventArgs e)
{
Word._Document oDoc = LoadTemplate(Environment.CurrentDirectory + "\\screwdriver.dotx");
SetTemplate(oDoc);
SaveToDisk(oDoc, Environment.CurrentDirectory + "\\New.docx");
oDoc.Close(ref oMissing, ref oMissing, ref oMissing);
}


Здесь все сводится к вызову трех методов LoadTemplate(),SetTemplate(), SaveToDisk(). Загружаем сохраненный шаблон, заполняем его данными с формы и сохраняем на диск, после чего закрываем документ.


Примечание:

Остерегайтесь использования Environment.CurrentDirectory так как значение может измениться в процессе работы.


static void Main(string[] args)
{
Console.WriteLine(Environment.CurrentDirectory);
Environment.CurrentDirectory = "c:\\";
Console.WriteLine(Environment.CurrentDirectory);
}


Как показано выше мы можем сами изменить ее значение, в данном случае Environment.CurrentDirectory больше указывает не на каталог приложения, а на корень диска C.


Ниже приведен код вызванных методов:


private Word._Document LoadTemplate(string filePath)
{
object oTemplate = filePath;
Word._Document oDoc = oWord.Documents.Add(ref oTemplate, ref oMissing, ref oMissing, ref oMissing);
return oDoc;
}

private void SetTemplate(Word._Document oDoc)
{
object oBookMark = "AuthorName";
oDoc.Bookmarks.get_Item(ref oBookMark).Range.Text = autorTxtb.Text;
}

private void SaveToDisk(Word._Document oDoc, string filePath)
{
object fileName = filePath;
oDoc.SaveAs(ref fileName, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
}


Сигнатуры вызываемых методов из пространства имен Microsoft.Office.Interop можно посмотреть по адресу:

http://msdn.microsoft.com/ru-ru/library/microsoft.office.interop.word%28en-us%29.aspx

Введем в текст бокс данные автора документа “Александр Кобелев” и сохраним документ на диск нажав кнопку “Сохранить”. В папке Debug нашего приложения появится файл New.docx, содержащий введенные данные на месте bookmark AuthorName.

Теперь добавим еще одну кнопку (Content="Print" Name="prntBtn") на форму, которая будет распечатывать документ на принтер и определим ее обработчик.


private void prntBtn_Click(object sender, RoutedEventArgs e)
{
Word._Document oDoc = LoadTemplate(Environment.CurrentDirectory + "\\screwdriver.dotx");
SetTemplate(oDoc);
PrintDoc(oDoc);
object notSave = Word.WdSaveOptions.wdDoNotSaveChanges;
oDoc.Close(ref notSave, ref oMissing, ref oMissing);
}


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


private void PrintDoc(Word._Document oDoc)
{
oDoc.PrintOut(ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing
, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing
, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing
, ref oMissing, ref oMissing, ref oMissing);
}


Метод PrintDoc просто вызывает метод PrintOut с параметрами по умолчанию.

Теперь добавим на шаблон таблицу 3 на 4, которая будет отображать продажи по городам. Верхняя строчка обозначит ключи и отвертки, а первый столбик города: Москва, Санкт-Петербург, Челябинск. Разукрасим таблицу по своему вкусу.

table

Далее мы могли бы поставить шесть закладок (boomark), задать им уникальные имена и искать их как в предыдущем случае, но это слишком накладно. Можно поступить проще – выделить всю таблицу и добавить закладку на все выделение сразу.

selectTable

Назовем новую закладку “SaleTable”.

Добавим на форму DataGrid, со свойством AutoGenerateColumns="True", а в код новую переменную

В конструкторе вызовем метод, который заполнит DataTable и свяжет его с DataGrid


public MainWindow()
{
InitializeComponent();
InitializeDataGrid();
}

private void InitializeDataGrid()
{
table = new DataTable();
table.Columns.Add();
table.Columns.Add();
table.Columns.Add();
var row = table.NewRow();
row[0] = "Город";
row[1] = "Ключи";
row[2] = "Отвертки";
table.Rows.Add(row);
table.Rows.Add(table.NewRow()[0] = "Москва");
table.Rows.Add(table.NewRow()[0] = "Санкт-Петербург");
table.Rows.Add(table.NewRow()[0] = "Челябинск");
dataGrid1.DataContext = table;
}


Добавим в метод SetTemplate() вызов метода SetTable() который будет заполнять данными таблицу.


private void SetTemplate(Word._Document oDoc)
{
. . .
SetTable(oDoc,"SaleTable",table);
}

private void SetTable(Word._Document oDoc,string bookmark, DataTable dataContext)
{
object oTableBookMark = bookmark;
var tbl = oDoc.Bookmarks.get_Item(ref oTableBookMark).Range.Tables[1];
int tblRow = 0;
int tblCell = 0;
foreach (Word.Column col in tbl.Columns)
{
foreach (Word.Cell cell in col.Cells)
{
cell.Range.Text = (string) dataContext.Rows[tblRow][tblCell];
tblRow++;
}
tblCell++;
tblRow = 0;
}
}


Как видно из листинга, мы перебираем по очереди все ячейки в шаблоне и сопоставляем их с данными из DataTable.

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

Для реализации немного изменим метод SetTable() , заменив явное присваивание на вызов метода SetCell().


...
foreach (Word.Cell cell in col.Cells)
{
SetCell(cell, (string)dataContext.Rows[tblRow][tblCell]);
tblRow++;
}
...

private void SetCell(Word.Cell cell, string text)
{
int val = 0;
if (int.TryParse(text, out val))
{
if (val < 100) cell.Shading.BackgroundPatternColor = Word.WdColor.wdColorRose;
if (val > 500) cell.Shading.BackgroundPatternColor = Word.WdColor.wdColorLightGreen;
}
cell.Range.Text = text;
}


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

Перейдем к графикам.

Для использования графиков потребуется ссылка на .Net библиотеку Microsoft.Office.Interop.Excel Version 14. Так же в шапке кода главного окна добавим using


using Excel = Microsoft.Office.Interop.Excel;


Сделаем несколько отступов вниз, для создания пространства между таблицей и графиком, добавим в шаблон график (Insert -> Chart -> Column -> 3d Column)

insertChart

3dColumn

и выставим все значения по нулям.

chartDefValue

После чего выделим график на шаблоне (кликнув по краю графика) и создадим новую закладку “ChartBookmark”, далее сохраним изменения в шаблоне.

Добавим в метод SetTemplate() вызов метода SetChart(), который будет заполнять данными график.


private void SetTemplate(Word._Document oDoc)
{
...
SetChart(oDoc, "ChartBookmark", table);
}

private void SetChart(Word._Document oDoc, string bookmark, DataTable dataContext)
{
object oChartBookMark = bookmark;
Word.Chart chart = oDoc.Bookmarks.get_Item(ref ChartBookMark).Range.InlineShapes[1].Chart;
Word.ChartData chartData = chart.ChartData;
chartData.Activate();

Excel.Workbook dataWorkbook = (Excel.Workbook)chartData.Workbook;
Excel.Worksheet dataSheet = (Excel.Worksheet)dataWorkbook.Worksheets[1];
dataSheet.Cells.get_Range("B2", oMissing).FormulaR1C1 = (string)dataContext.Rows[1][1];
dataSheet.Cells.get_Range("B3", oMissing).FormulaR1C1 = (string)dataContext.Rows[1][2];
dataSheet.Cells.get_Range("C2", oMissing).FormulaR1C1 = (string)dataContext.Rows[2][1];
dataSheet.Cells.get_Range("C3", oMissing).FormulaR1C1 = (string)dataContext.Rows[2][2];
dataSheet.Cells.get_Range("D2", oMissing).FormulaR1C1 = (string)dataContext.Rows[3][1];
dataSheet.Cells.get_Range("D3", oMissing).FormulaR1C1 = (string)dataContext.Rows[3][2];
dataWorkbook.Close(oMissing, oMissing, oMissing);
}


Теперь приложение полностью готово к работе, введем некоторые данные, чтобы провести функциональный тест. В строке автор я введу “Александр Кобелев” а значения продаж по городам у меня будут такие: Ключи/Отвертки Москва – 700/400, Санкт-Петербург – 50/300, Челябинск – 100/200. После нажатия кнопки “Сохранить” в папке Debug приложения появился файл New.docx, такого вида:

end

Александр Кобелев aka Megano