65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 .NET 3.0 自动生成 Word 2007 文档

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.18/5 (9投票s)

2007年8月26日

CPOL

4分钟阅读

viewsIcon

108629

downloadIcon

1960

本文介绍如何使用 .NET 3.0 自动生成 Word 2007 文档

引言

假设我所有客户的信息都存储在数据库系统或 XML 文件中,现在我需要创建数百封发送给客户的信件或报告。这些信件内容相同,但客户姓名、地址等信息不同。

有几种方法可以完成这项任务,但 Office Word 2007 的新功能帮助我轻松完成了它。使用内容控件创建新的文档模板是我感兴趣的新功能。.NET Framework 3.0 还提供了一种新的方法来复制和替换此模板的内容。

在本文中,我将重点介绍如何创建文档模板,然后从该模板自动生成其他文档。

背景

Open XML 标准化已成为 Office 2007 中基于 ECMA 标准的新默认格式。它绝对具有一些优势,因为 XML 是完美的格式,并且已经得到了许多供应商的支持。

您可以通过点击以下链接了解更多关于 OpenXML 的信息

Using the Code

我使用 Visual Studio 2005 和 .NET Framework 3.0 来实现我的应用程序。

为了帮助您开始阅读本文,以下步骤将向您展示如何

  1. 创建带有内容控件的 Word 2007 文档模板。
  2. 创建自定义 XML 文件,将内容控件映射到自定义 XML 文件中的 XML 元素。
  3. 创建关系以链接模板文档和自定义 XML 文件。
  4. 定义 OrderInformation 类。
  5. 定义 GenerateDocument 类。
  6. 运行应用程序 GenerateWord2007.exe

第一步:创建带有内容控件的 Word 2007 文档模板

想象一下,这是我必须设计的信件

Screenshot - GenerateWord2007_1.jpg

在新文档中,我使用“开发工具”选项卡插入适当的纯文本内容控件。如果“开发工具”选项卡未显示在 Microsoft Word 2007 中,则应选中 Word 选项中的“在功能区中显示开发工具”选项卡。将此文档命名为 LabelForm.docm

第二步:创建自定义 XML 文件,将文档模板的内容控件映射到自定义 XML 文件中的 XML 元素

根据 LabelForm.docm 的纯文本内容控件,我创建了一个新的自定义 XML 文件 item1.xml

Screenshot - GenerateWord2007_2.jpg

此 XML 的架构完全灵活,可以随意构建,自定义 XML 的每个元素都设置为 LabelForm.docm 的每个纯文本内容控件。

第三步:创建模板文档与自定义 XML 文件之间的关系

为了让内容控件和自定义 XML 文件相互理解,我必须编写一些宏来链接它们。在 Word 2007 屏幕上,我创建了新的宏 CreateCustomXMLPart 来将 item1.xml 附加到 LabelForm.docm

///Initial CustomXMLPart
Sub CreateCustomXMLPart()
    ActiveDocument.CustomXMLParts.Add
    ActiveDocument.CustomXMLParts(12).Load ("\\customXml\item1.xml")
End Sub

另一个宏 LoadCustomXMLPart 用于映射 LabelForm.docm 的内容控件。

 ///Load CustomXMLPart
Sub LoadCustomXMLPart() 
    Dim strXPath1 As String 
    strXPath1 = "/Customer/Address" 
    ActiveDocument.ContentControls(1).XMLMapping.SetMapping strXPath1 
    Dim strXPath2 As String 
    strXPath2 = "/Customer/City" 
    ActiveDocument.ContentControls(2).XMLMapping.SetMapping strXPath2 
    ........
End Sub    

我已准备好将其中一个节点映射到内容控件。运行这些宏,LabelForm.docm 的所有纯文本内容控件都会自动更新。将其命名为 LetterFormTemplate.docm

Screenshot - GenerateWord2007_3.jpg

因为我的目标是从 SQL Server 读取数据,并且每个记录都会生成到新文档,所以我编写了另一个宏,以便在打开此模板时将数据绑定到 LetterFormTemplate.docm 的相应内容控件。

Private Sub Document_Open()
    Call BindData
End Sub

 ///Sub Bind each column of the record to the appropriate 
            content control
Sub Sub BindData() 
    Dim part As CustomXMLPart 
    Set part = ActiveDocument.CustomXMLParts.SelectByNamespace("")(1)
    Dim ctlAddress As ContentControl 
    Dim ctlCity As ContentControl
    ........
    Set ctlAddress = ActiveDocument.ContentControls(1)
    Set ctlCity = ActiveDocument.ContentControls(2) 
    ........
    ctlAddress.XMLMapping.SetMapping "/Customer/Address", "", part 
    ctlCity.XMLMapping.SetMapping "/Customer/City", "", part 
    ........
End Sub    

我已完全完成文档模板的创建。

第四步:定义 OrderInformation 类

OrderInformation 类的目的是仅构建像 item1.xml 这样的 XML 结构,以便我可以将此数据绑定到 LetterFormTemplate.docm

  public string Address
        {
            get { return _address; }
            set { _address = value; }
        }
  public string City
        {
            get { return _city; }
            set { _city = value; }
        }
        .....
  .........
// Build the XML structure
 public string ToXml()
        {
            XmlDocument xmlDoc = new XmlDocument();

            XmlElement root = xmlDoc.CreateElement("Customer");
            xmlDoc.AppendChild(root);
            AppendChild(xmlDoc, root, "Address", _address);
            AppendChild(xmlDoc, root, "City", _city);
            AppendChild(xmlDoc, root, "Region", _region);
            AppendChild(xmlDoc, root, "PostCode", _postcode);
            AppendChild(xmlDoc, root, "Country", _country);
            AppendChild(xmlDoc, root, "CustomerID", _customerID.ToString());
            AppendChild(xmlDoc, root, "OrderID", _orderID.ToString());
            AppendChild(xmlDoc, root, "ShippedDate", _shippedDate.ToString());
            AppendChild(xmlDoc, root, "HomePhone", _homePhone);
            AppendChild(xmlDoc, root, "LastName", _lastName);
            AppendChild(xmlDoc, root, "FirstName", _firstName);
            AppendChild(xmlDoc, root, "Title", _title);
                     
            return xmlDoc.InnerXml;
        }
private void AppendChild(XmlDocument xmlDoc, XmlElement root, 
			string nodeName, string nodeValue)
        {
            XmlElement newElement = xmlDoc.CreateElement(nodeName);
            newElement.InnerText = nodeValue;
            root.AppendChild(newElement);
        }     

第五步:定义 GenerateDocument 类

实现了名为 GenerateWord() 的 main 函数,用于生成以 CustomerID 的值命名的、来自 LetterFormTemplate.docm 的新文档。在此类中,.NET 3.0 的 System.IO.Package 用于复制模板,替换自定义 XML 文件,最后生成新文档。

 ///Generate Word document.            
public void GenerateWord( OrderInformation orderInfo)
        {
            string templateDoc = "LetterFormTemplate.docm";
            string filename = string.Format("{0}.docm", orderInfo.CustomerID);
           // Copy a new file name from template file
            File.Copy(templateDoc, filename, true);
           // Open the new Package
            Package pkg = Package.Open(filename, FileMode.Open, FileAccess.ReadWrite);
           // Specify the URI of the part to be read
            Uri uri = new Uri("/customXml/item1.xml", UriKind.Relative);
            PackagePart part = pkg.GetPart(uri);
           // Load XML 
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(orderInfo.ToXml());
           // Open the stream to write document
            StreamWriter partWrt = new StreamWriter
			(part.GetStream(FileMode.Open, FileAccess.Write));
            doc.Save(partWrt);

            partWrt.Flush();
            partWrt.Close();           
        }    

一切都已完成。现在我只需实现我的要求。

* 从 SQL Server 读取数据以创建新的 OrderInformation 实例。假设 Northwind 数据库已设置。我执行一个查询来创建发往拥有订单号 10205 Customer 的信件。

///Get Data From SQL Server
 public OrderInformation GetOrderInformation()
        {
            string sqlQuery;
            sqlQuery = "SELECT o.OrderID, o.CustomerID, o.ShippedDate, ";
            sqlQuery = sqlQuery + "e.LastName, e.FirstName, e.Title,e.Address, _
			e.City, e.Region, e.PostalCode,e.HomePhone,e.Country ";
            sqlQuery = sqlQuery + "FROM Orders o ";
            sqlQuery = sqlQuery + "INNER JOIN Employees e ON _
				e.EmployeeID = o.EmployeeID ";
            sqlQuery = sqlQuery + "WHERE o.OrderID = 10250";
            using (SqlConnection conn = new SqlConnection_
		(@"Integrated Security=SSPI;Persist Security Info=False;_
		Initial Catalog=Northwind;Data Source=localhost"))
            {
                SqlCommand cmd = new SqlCommand(sqlQuery, conn);
                cmd.Connection.Open();

                SqlDataReader sdr = cmd.ExecuteReader();
                sdr.Read();
                OrderInformation orderInfo = new OrderInformation();
                orderInfo.OrderID = sdr.GetInt32(0);
                orderInfo.CustomerID = sdr.GetString(1);
                orderInfo.ShippedDate = sdr.GetDateTime(2);
                orderInfo.LastName = sdr.GetString(3);
                orderInfo.FirstName = sdr.GetString(4);
                orderInfo.Title = sdr.GetString(5);
                orderInfo.Address = sdr.GetString(6);
                orderInfo.City = sdr.GetString(7);
                orderInfo.Region = sdr.GetString(8);
                orderInfo.PostCode = sdr.GetString(9);
                orderInfo.HomePhone = sdr.GetString(10);
                orderInfo.Country = sdr.GetString(11);

                return orderInfo;
            }
        }    

* 从包含多个 Customer 的 XML 文件读取数据

Screenshot - GenerateWord2007_4.jpg

解析 XML 数据,将其存储在 OrderInformation 列表中。

public List ListOrders()
        {
            XmlDocument customers = new XmlDocument();
            Hashtable nodeData = new Hashtable();

            customers.Load("Data.xml");
            List<orderinformation /> orderList = new List<orderinformation />();
            foreach (XmlNode customer in customers.DocumentElement.ChildNodes)
            {
                foreach (XmlElement element in customer.ChildNodes)
                {

                    {
                        nodeData.Add(element.Name, element.InnerText);
                    }
                }
                if (nodeData.Count > 0)
                {
                    OrderInformation orderInfo = new OrderInformation();
                    orderInfo.OrderID = Convert.ToInt32(nodeData["OrderID"]);
                    orderInfo.CustomerID = nodeData["CustomerID"].ToString();
                    orderInfo.ShippedDate = Convert.ToDateTime(nodeData["ShippedDate"]);
                    orderInfo.LastName = nodeData["LastName"].ToString();
                    orderInfo.FirstName = nodeData["FirstName"].ToString();
                    orderInfo.Title = nodeData["Title"].ToString();
                    orderInfo.Address = nodeData["Address"].ToString();
                    orderInfo.City = nodeData["City"].ToString();
                    orderInfo.Region = nodeData["Region"].ToString();
                    orderInfo.PostCode = nodeData["PostCode"].ToString();
                    orderInfo.HomePhone = nodeData["HomePhone"].ToString();
                    orderInfo.Country = nodeData["Country"].ToString();
                    // Add To List 
                    orderList.Add(orderInfo);
                    nodeData.Clear();
                }       
            }
            return orderList;
        }     

第六步:运行应用程序 GenerateWord2007.exe

此应用程序通过两种方法实现,用于生成从 SQL Server 或 XML 文件读取数据的新文档。

Screenshot - GenerateWord2007_5.jpg

从 SQL Server

private void button1_Click(object sender, EventArgs e)
     {
         OrderInformation orderInfo;
         GenerateDocument doc = new GenerateDocument();
         orderInfo =  doc.GetOrderInformation();
         doc.GenerateWord(orderInfo);
     }  

从 XML 文件

private void button2_Click(object sender, EventArgs e)
     {
         GenerateDocument doc = new GenerateDocument();
         List orderList = new List();
         orderList = doc.ListOrders();
         foreach (OrderInformation orderInfo in orderList)
         {
             doc.GenerateWord(orderInfo);
         }
     }

关注点

学习 Open XML 绝对是值得的。一些新的扩展,例如 Microsoft Word 的 *.docx、Microsoft Excel 的 *.xlsx 或 PowerPoint 的 *.pptx,使我们无需使用 API 即可读取或修改它们。我只是想分享我的想法,以便您对该概念有一个大致了解,或者您可能会创建更有效的东西。

历史

  • 2007年8月26日:初始发布
© . All rights reserved.