21

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..


EDIT: 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.

hoijui
  • 3,615
  • 2
  • 33
  • 41
Jesper Kihlberg
  • 546
  • 1
  • 4
  • 16
  • You can use [docxtemplater](https://github.com/open-xml-templating/docxtemplater), which allows you to do just that : generate a Word document from a Template. – edi9999 Aug 22 '22 at 10:47

7 Answers7

15

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.

Jens
  • 3,249
  • 2
  • 25
  • 42
10

Maybe you should be looking at Microsoft.Office.Tools.Word.Document?

Document.SelectContentControlsByTitle

Foole
  • 4,754
  • 1
  • 26
  • 23
  • At last I found out, that this was actually the way to go.. But there is a lot of things that you have to be aware of. First of all, you need to add a reference to the component named office and you have to make shure that your assemblies are the right version, otherwise you wont have access to the correct methods and objects. – Jesper Kihlberg Nov 30 '10 at 19:52
4

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

Shuwaiee
  • 677
  • 6
  • 7
  • this would be overkill for what he is trying to achieve, Word automation/interop is much easier to do for this scenario. – BrokenGlass Nov 29 '10 at 13:44
  • Overkill? Not at all. The OpenXML SDK is easy to work with and is meant to do exactly this. Don't you have to have Word installed on your server when doing word automation/interop? With OpenXML you don't. – Dennis G Nov 29 '10 at 13:58
  • Plus i wanna add to Moontear comment, OpenXML format is just XML it so fast at what he is trying to do (generating 600 document) – Shuwaiee Nov 29 '10 at 14:40
  • 1
    I know that I might use OpenXML, but to me it seems so much faster to be able to design my document in the wysiwyg editor with bindable ContentControls. – Jesper Kihlberg Nov 30 '10 at 08:23
4

Seems that there are 2 questions here:

  1. How do you kick off the process for a particular id-value

  2. 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:

  1. do all the work via the OpenXML SDK, never opening Word at all (as other posters have suggested)

  2. create a minimal pre-existing document (containing your id number) using the OpenXML SDk, then open Word

  3. automate Word to pass the id number to the document, perhaps as a document property

  4. 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)

JasonPlutext
  • 15,352
  • 4
  • 44
  • 84
  • Thanks for your answer, could you please elaborate a bit about the last part: "Then, in would inject my data into it as a custom xml part, and save it." That might be the way to do it. – Jesper Kihlberg Nov 30 '10 at 08:21
  • You can create content controls in word and tie them together with an xml file. that xml file will also be saved into your word document. replacing the xml file would change the displayed data of your document. i would recommend taking a look at the word content control toolkit to do the binding for you. – Jens Nov 30 '10 at 08:35
-1

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.

  • an updated link would be appreciated, looks like the data in the link was removed 7 months after you posted here – brw59 Jun 12 '17 at 18:54
  • The referenced article was deleted at 10 Jul 2015, and its recent version is available in the mighty WebArchive as http://web.archive.org/web/20150607050033/https://www.codeproject.com/Articles/759408/Creating-Word-documents-in-Net-using-Docentric-Too . Anyway it is about Docentric Toolkit, which is a paid library... – AntonK Jul 27 '17 at 23:21
-1

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
        {

        }
    }
slavoo
  • 5,798
  • 64
  • 37
  • 39
-1

If you use Oracle APEX, then apexofficeprint.com

If you use other web technologies/frameworks, then cloudofficeprint.com

Gaus
  • 59
  • 1
  • 3
  • 11