Introduction
The attached example demonstrates the use of Cathal Coffey's "DocX" DLL which can be found on CodePlex here: http://docx.codeplex.com/. The project offers a DLL which can be downloaded and added to a project and then used to programmatically create Word documents on the fly and without any reliance upon the Microsoft Office DLLs. I noticed that an update to the project broke the invoice project example that I was using and I had to modify it to get it up and running again. You might find the example useful if you need to generate Word documents programmatically and if you also found the example failing following the update.
The Invoice example is a great introduction to the use of the DLL as it demonstrates a number of common tasks:
- Programmatic creation of a template document
- Programmatic population of the template coupled with renaming and saving the document
- Adding graphics to the page layout (a company icon in this case)
- Populating fields created as custom properties in the document
- Creating and populating tables and inserting them into the document
Much of the replacement invoice project does essentially the same thing as the original version although I altered the appearance of the document. This example does correct the issues I found with the example that I originally downloaded from the CodePlex project site.
![DocX-DLL.jpg]()
Figure 1 - Generated Invoice Document
Attached Project
The attached project contains a single example; it is an updated version of the invoice example provided on the CodePlex project site. This updated version corrects the failed parts of the example I encountered and I have updated the comments to hopefully better describe what the code is doing at any given point in the document generation process.
The project itself is a simple console mode application that generates a fake invoice based upon a collection of canned data used to simulate acquiring the data from a database. Whilst running, the code checks to see if a document template exists (and this template is actually just a standard Word document and not really a template file). If the template document is not found, the code creates one programmatically and saves it.
Continuing on, the code next recovers data from the data source (in this case, a collection of dummy data). That recovered data is then used to replace the default property values stored in the template with live content. Once populated with data, the code lastly saves the new template based document with a canned file name. Given the design of the original project, one may look into the bin folder to locate both the document template and, after running the code, the populated document with an alternative file name so as to not overwrite the template itself. Figure 1 in this document provides a screen shot of the generated document.
The code is fully annotated and you can see what is going on by reading the comments embedded within the code. You may find it more convenient to open the project and review the code and comments from within Visual Studio 2012.
using System;
using System.Linq;
using System.IO;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.IO;
namespace Novacode
{
class Program
{
static Assembly gAssembly;
static DocX gDocument;
static void Main(string[] args)
{
gAssembly = Assembly.GetExecutingAssembly();
try
{
if (File.Exists(@"InvoiceTemplate.docx"))
{
gDocument = DocX.Load(@"InvoiceTemplate.docx");
gDocument =
CreateInvoiceFromTemplate(DocX.Load(@"InvoiceTemplate.docx"));
gDocument.SaveAs("NewLoadedShipment.docx");
}
else
{
gDocument = CreateInvoiceTemplate();
gDocument.Save();
gDocument = DocX.Load(@"InvoiceTemplate.docx");
gDocument = CreateInvoiceFromTemplate(DocX.Load(@"InvoiceTemplate.docx"));
gDocument.SaveAs("NewLoadedShipment.docx");
}
}
catch (Exception ex)
{
Console.WriteLine("An Error has occurred");
Console.WriteLine("Message: " + ex.Message);
Console.WriteLine(ex.StackTrace);
Console.WriteLine("Press any key to continue");
Console.Read();
}
}
private static DocX CreateInvoiceFromTemplate(DocX template)
{
#region Logo
Paragraph logo_paragraph =
template.Tables[0].Rows[0].Cells[1].Paragraph;
logo_paragraph.Pictures[0].Remove();
Novacode.Image logo =
template.AddImage(
gAssembly.GetManifestResourceStream("Novacode.Resources.FakeLogo.png"));
Picture logo_picture = logo_paragraph.InsertPicture(logo.Id);
#endregion
#region Set Custom Property values
template.AddCustomProperty(
new CustomProperty("company_name", "Bart's Parts"));
template.AddCustomProperty(
new CustomProperty("company_slogan", "The King of OEM distributions"));
template.AddCustomProperty(
new CustomProperty("hired_company_username", "Joe Bagadonuts"));
template.AddCustomProperty(
new CustomProperty("hired_company_address_line_one", "1100 Main Street"));
template.AddCustomProperty(
new CustomProperty("hired_company_address_line_two", "Atlanta, GA"));
template.AddCustomProperty(
new CustomProperty("hired_company_address_line_three", "30303"));
template.AddCustomProperty(
new CustomProperty("invoice_date", DateTime.Today.Date.ToShortDateString()));
template.AddCustomProperty(
new CustomProperty("invoice_time", DateTime.Today.Date.ToShortTimeString()));
template.AddCustomProperty(
new CustomProperty("hired_company_details_line_one",
"1100 North Main Street, Fremont, CA 12345"));
template.AddCustomProperty(
new CustomProperty("hired_company_details_line_two",
"Phone: 012-345-6789, Fax: 012-345-6789, e-mail: support@oem.com"));
#endregion
#region Replace Placeholder Table
Table t = template.Tables[1];
Table invoice_table =
CreateAndInsertInvoiceTableAfter(t, ref template);
t.Remove();
return template;
#endregion
}
private static DocX CreateInvoiceTemplate()
{
DocX document = DocX.Create(@"InvoiceTemplate.docx");
Table layout_table = document.InsertTable(2, 2);
layout_table.Design = TableDesign.TableNormal;
layout_table.AutoFit = AutoFit.Window;
#region Create document style
Formatting large_dark_formatting = new Formatting();
large_dark_formatting.Bold = true;
large_dark_formatting.Size = 16;
large_dark_formatting.FontColor = Color.Black;
Formatting dark_formatting = new Formatting();
dark_formatting.Bold = true;
dark_formatting.Size = 12;
dark_formatting.FontColor = Color.Black;
Formatting light_formatting = new Formatting();
light_formatting.Italic = true;
light_formatting.Size = 11;
light_formatting.FontColor = Color.Black;
#endregion
#region Company Name
Paragraph upper_left_paragraph =
layout_table.Rows[0].Cells[0].Paragraph;
CustomProperty company_name =
new CustomProperty("company_name", "Company Name");
layout_table.Rows[0].Cells[0].Paragraph.InsertDocProperty(
company_name, large_dark_formatting);
upper_left_paragraph.InsertText("\n", false);
#endregion
#region Company Slogan
CustomProperty company_slogan =
new CustomProperty("company_slogan",
"Company slogan goes here.");
upper_left_paragraph.InsertDocProperty(
company_slogan, light_formatting);
#endregion
#region Company Logo
Paragraph upper_right_paragraph = layout_table.Rows[0].Cells[1].Paragraph;
Novacode.Image logo = document.AddImage(gAssembly.GetManifestResourceStream("Novacode.Resources.logo_template.png"));
Picture picture_logo = upper_right_paragraph.InsertPicture(logo.Id, "", "");
upper_right_paragraph.Alignment = Alignment.right;
#endregion
#region Hired Company Address
CustomProperty hired_company_username =
new CustomProperty("hired_company_username",
"User Name:");
CustomProperty hired_company_address_line_one =
new CustomProperty("hired_company_address_line_one",
"Street Address,");
Paragraph lower_left_paragraph =
layout_table.Rows[1].Cells[0].Paragraph;
lower_left_paragraph.InsertText("TO:\n", false, dark_formatting);
lower_left_paragraph.InsertDocProperty(
hired_company_username, light_formatting);
lower_left_paragraph.InsertText("\n", false);
lower_left_paragraph.InsertDocProperty(
hired_company_address_line_one, light_formatting);
lower_left_paragraph.InsertText("\n", false);
CustomProperty hired_company_address_line_two =
new CustomProperty("hired_company_address_line_two",
"City,");
lower_left_paragraph.InsertDocProperty(
hired_company_address_line_two, light_formatting);
lower_left_paragraph.InsertText("\n", false);
CustomProperty hired_company_address_line_three =
new CustomProperty("hired_company_address_line_three",
"Zip Code");
lower_left_paragraph.InsertDocProperty(
hired_company_address_line_three, light_formatting);
#endregion
#region Date & Invoice number
Paragraph lower_right_paragraph =
layout_table.Rows[1].Cells[1].Paragraph;
CustomProperty invoice_date =
new CustomProperty("invoice_date",
DateTime.Today.Date.ToString("d"));
lower_right_paragraph.InsertText("Date: ",
false, dark_formatting);
lower_right_paragraph.InsertDocProperty(invoice_date,
light_formatting);
CustomProperty invoice_time =
new CustomProperty("invoice_time",
DateTime.Today.Date.ToShortTimeString());
lower_right_paragraph.InsertText("\nTime: ",
false, dark_formatting);
lower_right_paragraph.InsertText("", false,
light_formatting);
lower_right_paragraph.InsertDocProperty(invoice_time,
light_formatting);
lower_right_paragraph.Alignment = Alignment.right;
#endregion
#region Statement of thanks
document.InsertParagraph(string.Empty, false);
Table invoice_table = document.InsertTable(7, 4);
invoice_table.Design = TableDesign.LightShadingAccent1;
invoice_table.Alignment = Alignment.center;
Paragraph thankyou =
document.InsertParagraph("\nThank you for your business, " +
"see us again for all of your OEM parts needs.",
false, dark_formatting);
thankyou.Alignment = Alignment.center;
#endregion
#region Hired company details
CustomProperty hired_company_details_line_one =
new CustomProperty("hired_company_details_line_one",
"Street Address, City, ZIP Code");
CustomProperty hired_company_details_line_two =
new CustomProperty("hired_company_details_line_two",
"Phone: 000-000-0000, Fax: 000-000-0000, " +
"e-mail: support@companyname.com");
Paragraph companyDetails =
document.InsertParagraph(string.Empty, false);
companyDetails.InsertDocProperty(hired_company_details_line_one,
light_formatting);
companyDetails.InsertText("\n", false);
companyDetails.InsertDocProperty(hired_company_details_line_two,
light_formatting);
companyDetails.Alignment = Alignment.center;
#endregion
return document;
}
private static Table CreateAndInsertInvoiceTableAfter(
Table t, ref DocX document)
{
DataTable data = GetDataFromDatabase();
Table invoice_table =
t.InsertTableAfterSelf(data.Rows.Count + 1,
data.Columns.Count);
invoice_table.Design = TableDesign.DarkListAccent1;
#region Table title and column headers
Formatting table_title = new Formatting();
table_title.Bold = true;
invoice_table.Rows[0].Cells[0].Paragraph.InsertText(
"Invoice ID", false, table_title);
invoice_table.Rows[0].Cells[0].Paragraph.Alignment =
Alignment.left;
invoice_table.Rows[0].Cells[1].Paragraph.InsertText(
"Part Number", false, table_title);
invoice_table.Rows[0].Cells[1].Paragraph.Alignment =
Alignment.left;
invoice_table.Rows[0].Cells[2].Paragraph.InsertText(
"Description", false, table_title);
invoice_table.Rows[0].Cells[2].Paragraph.Alignment =
Alignment.left;
invoice_table.Rows[0].Cells[3].Paragraph.InsertText(
"Unit Price", false, table_title);
invoice_table.Rows[0].Cells[3].Paragraph.Alignment =
Alignment.left;
invoice_table.Rows[0].Cells[4].Paragraph.InsertText(
"Number Ordered", false, table_title);
invoice_table.Rows[0].Cells[4].Paragraph.Alignment =
Alignment.left;
invoice_table.Rows[0].Cells[5].Paragraph.InsertText(
"Total", false, table_title);
invoice_table.Rows[0].Cells[5].Paragraph.Alignment =
Alignment.left;
#endregion
for (int row = 1; row < invoice_table.RowCount; row++)
{
for (int cell = 0; cell < invoice_table.Rows[row].Cells.Count; cell++)
{
Paragraph cell_paragraph =
invoice_table.Rows[row].Cells[cell].Paragraph;
cell_paragraph.InsertText(
data.Rows[row - 1].ItemArray[cell].ToString(), false);
}
}
invoice_table.AutoFit = AutoFit.Contents;
invoice_table.Alignment = Alignment.center;
return invoice_table;
}
private static DataTable GetDataFromDatabase()
{
DataTable table = new DataTable();
table.Columns.AddRange(new DataColumn[]
{ new DataColumn("InvoiceId"), new DataColumn("PartNo"),
new DataColumn("Description"), new DataColumn("UnitPrice"),
new DataColumn("UnitsOrderd"), new DataColumn("RowTotal")});
table.Rows.Add
(
"78123",
"801-ST344",
"Steering Column",
"$287.65",
"1",
"$287.65"
);
table.Rows.Add
(
"78124",
"71-AC9488",
"Compressor, AC",
"$614.82",
"1",
"$614.82"
);
table.Rows.Add
(
"78125",
"783342",
"Air filter, Fram",
"$9.12",
"1",
"$9.12"
);
table.Rows.Add
(
"78126",
"AC49034",
"Spark Plug, Platinum",
"$5.12",
"8",
"$40.96"
);
table.Rows.Add
(
"78127",
"FMC-66-1243",
"Bumper, Front",
"$212.45",
"1",
"$212.45"
);
table.Rows.Add
(
"",
"",
"Tax",
"",
"",
"93.20"
);
table.Rows.Add
(
"",
"",
"Total",
"",
"",
"$1258.20"
);
return table;
}
}
}