I am trying to create about 600 reports in microsoft office Word. The documents are populated with data from a database, and images found on a local drive.
I have figured out, that I might create a Word Template project in visual studio 2010, and program the template, so that when you enter a single value (id-number), it automatically fills out the entire document.
I am quite confident that this is possible. the only problem is. How do I loop through all entries in the database, open a new document based on the template and set the id-value?
for(int i = 0; i < idnumbers.Count(); i++)
{
Word.Application app = new Word.Application();
Word.Document doc = app.Documents.Add(@"C:\..\WordGenerator\bin\Debug\WordTemplate.dotx");
//input the id-number below: HOW??
doc.SaveAs(FileName: @"c:\temp\test.docx");
}
The application is supposed to run only once, generating the reports, and it doesn´t have to be fast. It just has to be easy to develop.
The problem here is, that it seems that the DocumentBase object is not accessible outside the Word project. The substitute Microsoft.Office.Interop.Word.Document does not have functionality like SelectContentControlsByTitle that allows me to find and set my ContentControls. And that is exactly what I need to do..
This is what my code looks like now to insert the text into my field:
Word.Application app = new Word.Application();
Word.Document doc = app.Documents.Add(@"C:\..\test.dotx");
foreach (Word.ContentControl cc in doc.SelectContentControlsByTitle("MyCCTitle"))
{
cc.Range.Text += "1234";
}
doc.SaveAs(FileName: @"c:\temp\test.docx");
Then an eventhandler on my template on BeforeSave fills out the document based on the text in MyCCTitle-titled object.
Maybe you should be looking at Microsoft.Office.Tools.Word.Document?
Document.SelectContentControlsByTitle
Don't use Office Automation.
Office automation opens up an instance of office in the background and performs the actions on it. Opening up an office instance 600 times doesn't seem like a very interesting thing to do. (and it would never run serverside)
Take a look at Open XML. You can find loads about it below:
http://openxmldeveloper.org/
edit: Openxmldeveloper is shutting down. Find all the sources mentioned above at http://www.ericwhite.com/ instead.
You Should read about OpenXML format if you are using Word 2007 or 2010 Format
http://msdn.microsoft.com/en-us/library/bb264572(office.12).aspx
Seems that there are 2 questions here:
How do you kick off the process for a particular id-value
How do you populate the document.
sunilp has answered Q2. Data bound content controls are the best way to inject data for Word 2007 and later.
OP's focus looks to be Q1.
There is no command line switch that lets you pass an arbitrary value to Word: http://support.microsoft.com/kb/210565
So as I see it you have 4 choices:
do all the work via the OpenXML SDK, never opening Word at all (as other posters have suggested)
create a minimal pre-existing document (containing your id number) using the OpenXML SDk, then open Word
automate Word to pass the id number to the document, perhaps as a document property
do the work to create the 600 documents in Word using VSTO or Word macros (VBA)
Me? I would create a docx containing data bound content controls in Word, and save it.
Then, in would inject my data into it as a custom xml part, and save it. (This step you could do using the OpenXML SDK, or in Word if you needed to have Word update the bindings for some downstream process of yours)
In regard to the answers above I agree with J. Vermeire that OpenXML is the way to go. I have been using an OpenXML based toolkit for over three years now, which produces .docx documents, merged from templates and database data. There is an example how to use it here. The example shows how to work with one document at the time, to work with more of them, just add a loop and call a method for document generation.
Add references for Document.OpenXml.dll
and WindowsBase.dll
using System.IO.Packaging;
using DocumentFormat.OpenXml.Packaging;
using System.DirectoryServices;
protected void btnOK_Click(object sender, EventArgs e)
{
try
{
Package package;
string strTemplateName = ddl_Templates.SelectedValue.ToString(); //Select Dotx template
string strClaimNo = "3284112";
string strDatePart = DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString();
//Word template file
string templateName = Server.MapPath("~\\LetterTemplates\\" + strTemplateName + ".dotx");
PackagePart documentPart = null;
//New file name to be generated from
string docFileName = Server.MapPath("~\\LetterTemplates\\" + strClaimNo + "_" + strTemplateName + "_" + strDatePart + ".docx");
File.Copy(templateName,docFileName, true);
string fileName = docFileName;
package = Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
DataSet DS = GetDataSet(strClaimNo, ""); // to get the data from backend to fill in for merge fields
try
{
if (DS != null)
{
if (DS.Tables.Count > 0)
{
if (DS.Tables[0].Rows.Count > 0)
{
foreach (System.IO.Packaging.PackageRelationship documentRelationship
in package.GetRelationshipsByType(documentRelationshipType))
{
NameTable nt = new NameTable();
nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w",
"http://schemas.openxmlformats.org/wordprocessingml/2006/main");
Uri documentUri = PackUriHelper.ResolvePartUri(
new Uri("/", UriKind.Relative), documentRelationship.TargetUri);
documentPart = package.GetPart(documentUri);
//Get document xml
XmlDocument xdoc = new XmlDocument();
xdoc.Load(documentPart.GetStream(FileMode.Open, FileAccess.Read));
int intMergeFirldCount = xdoc.SelectNodes("//w:t", nsManager).Count;
XmlNodeList nodeList = xdoc.SelectNodes("//w:t", nsManager);
foreach (XmlNode node in nodeList)
{
try
{
xdoc.InnerXml = xdoc.InnerXml.Replace(node.InnerText, DS.Tables[0].Rows[0][node.InnerText.Replace("«", "").Replace("»", "").Trim()].ToString());
}catch(Exception x) { }
}
StreamWriter streamPart = new StreamWriter(documentPart.GetStream(FileMode.Open, FileAccess.Write));
xdoc.Save(streamPart);
streamPart.Close();
package.Flush();
package.Close();
}
using (WordprocessingDocument template = WordprocessingDocument.Open(docFileName, true))
{
template.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
template.MainDocumentPart.Document.Save();
}
byte[] bytes = System.IO.File.ReadAllBytes(docFileName);
System.IO.File.Delete(docFileName);
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "application/vnd.msword.document.12"; //"application/msword";
Response.ContentEncoding = System.Text.Encoding.UTF8;
response.AddHeader("Content-Disposition", "attachment; filename=" + strClaimNo + "_" + strTemplateName + "_" + strDatePart + ".docx;");
response.BinaryWrite(bytes);
response.Flush();
response.Close();
}
else
{
throw (new Exception("No Records Found."));
}
}
else
{
throw (new Exception("No Records Found."));
}
}
else
{
throw (new Exception("No Records Found."));
}
}
catch (Exception ex)
{
package.Flush();
package.Close();
// Softronic to add code for exception handling
}
}
catch (Exception ex)
{
// add code for exception handling
}
finally
{
}
}