如何从模板程序化地创建Word文档

22

我正在尝试在微软办公软件Word中创建大约600份报告。这些文档由数据库中的数据和本地驱动器上找到的图像填充。

我已经想到了一个方法,可以在Visual Studio 2010中创建一个Word模板项目,并编写代码,使得当您输入单个值(ID号码)时,它会自动填充整个文档。

我相信这是可能的,唯一的问题是:如何循环遍历数据库中的所有条目,基于模板打开新文档并设置ID值?

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"); 
}
应用程序只需要运行一次,生成报告即可,并且速度不必很快。它只需要易于开发即可。 问题在于,似乎无法在 Word 项目之外访问 DocumentBase 对象。替代的 Microsoft.Office.Interop.Word.Document 没有像 SelectContentControlsByTitle 这样的功能,允许我查找并设置我的 ContentControls。而这正是我需要做的。
编辑:以下是我的代码,用于将文本插入到我的字段中:
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");

然后,在我的模板上BeforeSave事件处理程序根据MyCCTitle标题对象中的文本填写文档。


你可以使用docxtemplater,它允许你从模板生成Word文档。 - edi9999
7个回答

16

不要使用Office自动化。 Office自动化会在后台打开一个Office实例并对其执行操作。打开600个Office实例似乎并不是一件非常有趣的事情。(而且它永远不会在服务器端运行)

看看Open XML。您可以在以下链接中找到大量信息:

http://openxmldeveloper.org/

编辑:Openxmldeveloper已关闭。请在http://www.ericwhite.com/上找到上述提到的所有资源。


10
也许你应该看一下 Microsoft.Office.Tools.Word.Document?Document.SelectContentControlsByTitle 是一个方法,详情请参考此链接

最后我发现,这实际上是正确的方法。但是有很多事情你必须要注意。首先,你需要添加一个名为office的组件的引用,并确保你的程序集是正确的版本,否则你将无法访问到正确的方法和对象。 - Jesper Kihlberg

5

这里似乎有两个问题:

  1. 如何启动特定id值的进程

  2. 如何填充文档。

sunilp回答了Q2。对于Word 2007及更高版本,数据绑定内容控件是注入数据的最佳方式。

OP的重点似乎是Q1。

没有命令行开关可以让您将任意值传递给Word:http://support.microsoft.com/kb/210565

因此,我认为你有4个选择:

  1. 完全使用OpenXML SDK进行所有工作,从不打开Word(正如其他帖子中提到的)

  2. 使用OpenXML SDk创建一个最小的预先存在的文档(包含您的id号码),然后打开Word

  3. 自动化Word将id号码传递给文档,可能作为文档属性

  4. 使用VSTO或Word宏(VBA)在Word中完成创建600个文档的工作

我会在Word中创建一个包含数据绑定内容控件的docx文档,并保存它。

然后,我会将我的数据作为自定义xml部分注入其中,并保存它。(这一步可以使用OpenXML SDK完成,或者如果您需要让Word更新绑定以进行下游处理,也可以在Word中完成)


谢谢您的回答,能否详细说明一下最后一部分:“然后,将我的数据作为自定义XML部件注入其中,并保存它。”这可能是解决问题的方法。 - Jesper Kihlberg
您可以在Word中创建内容控件并与XML文件一起绑定。该XML文件也将保存在您的Word文档中。替换XML文件会更改文档中显示的数据。我建议您查看Word内容控件工具包,以便为您完成绑定。 - Jens

4

这对他想要实现的目标来说有些过度了,针对这种情况,使用Word自动化/互操作会更容易一些。 - BrokenGlass
过度设计?一点也不。OpenXML SDK易于使用,旨在正好做到这一点。在进行Word自动化/Interop时,您难道不必在服务器上安装Word吗?但是使用OpenXML,您不需要这样做。 - Dennis G
我想补充一下Moontear的评论,OpenXML格式只是XML,它非常适合他所尝试做的事情(生成600个文档)。 - Shuwaiee
1
我知道我可以使用OpenXML,但对我来说,在可绑定的ContentControls中使用所见即所得编辑器设计文档似乎更快。 - Jesper Kihlberg

-1
关于上面的答案,我同意J. Vermeire的看法,OpenXML是正确的选择。我已经使用基于OpenXML的工具包三年多了,它可以生成从模板和数据库数据合并而成的.docx文档。这里有一个如何使用它的示例here。该示例展示了如何一次处理一个文档,如果要处理更多文档,只需添加循环并调用文档生成方法即可。

一个更新的链接会很受欢迎,看起来您在此发布后7个月内链接中的数据已被删除。 - brw59
引用的文章已于2015年7月10日被删除,其最新版本可在强大的WebArchive中找到,网址为http://web.archive.org/web/20150607050033/https://www.codeproject.com/Articles/759408/Creating-Word-documents-in-Net-using-Docentric-Too。无论如何,这篇文章是关于Docentric Toolkit的,这是一个付费库... - AntonK

-1

如果您使用Oracle APEX,则可以访问apexofficeprint.com

如果您使用其他网络技术/框架,则可以访问cloudofficeprint.com


-1

Document.OpenXml.dllWindowsBase.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
        {

        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接