技术开发 频道

使用Open XML SDK 2.0生成Office文档

  【IT168 技术文档】我们会利用OpenXML SDK 2.0写程序生成一个Word文档。在这个文档中有一个表含有产品信息。每个产品的名称,类别,价钱和图像都显示在一个表格里。而这些信息又是从后台数据库里提取出来的。下面是几个步骤:

  • 添加引用到OpenXML SDK

  • 生成一个Word文档

  • 定义一个表(Table)和它的列

  • 给这个表加一行台头

  • 将每个产品信息生成一行添加到表单里

  • 将表加入到文档中

  备注:在这个程序中我们只更改Program.cs里的程序代码。

  1. 在解决方案资源管理器中, 右击 项目CreateDocFromDatabase 然后选中添加引用。

  2. 在添加引用对话框中,选择.Net tab,向下滚动,选中DocumentFormat.OpenXml,, 点击确定按钮。

  3. 用同样步骤添加对System.Drawing 和 WindowsBase的引用.

  备注:如果有多个WindowsBase,选中路径是c:\Program Files\Reference Assemblies\Microsoft \Framework\v3.0\WindowsBase.dll

  4. 删除在上一步加入在Main(){ }的程序:在解决方案资源管理器中,右击Program.cs 打开程序编辑页面。删除Main() { }中的所有程序。你的程序应该看上去像是这样:

  using System;
  
using System.Collections.Generic;
  
using System.Linq;
  
using System.Text;
  
namespace CreateDocFromDatabase
  {
  
class Program
  {
  
static void Main(string[] args)
  {
  }
  }
  }

   5. 将下面的代码段加入到最后一个Using 语句后面;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | UsingStatement

  Using DocumentFormat.OpenXml;
  Using DocumentFormat.OpenXml.Packaging;
  Using DocumentFormat.OpenXml.Wordprocessing;
  Using wp
= DocumentFormat.OpenXml.Drawing.Wordprocessing;
  Using a
= DocumentFormat.OpenXml.Drawing;
  Using pic
= DocumentFormat.OpenXml.Drawing.Pictures;
  Using System.IO;
  Using System.Drawing;
  Your code should look like the following:
  
using System;
  
using System.Collections.Generic;
  
using System.Linq;
  
using System.Text;
  
using DocumentFormat.OpenXml;
  
using DocumentFormat.OpenXml.Packaging;
  
using DocumentFormat.OpenXml.Wordprocessing;
  
using wp = DocumentFormat.OpenXml.Drawing.Wordprocessing;
  
using a = DocumentFormat.OpenXml.Drawing;
  
using pic = DocumentFormat.OpenXml.Drawing.Pictures;
  
using System.IO;
  
using System.Drawing;
  
namespace CreateDocFromDatabase
  {
  
class Program
  {
  
static void Main(string[] args)
  {
  }
  }
  }

 

  6. 将下面的程序加入到method:static void Main(string[] args)中;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | OpenDocument

  // Name the word document
  string docName = @"CreateDocFromDatabase.docx";
  
// Reuse style info from template
  File.Copy(@"Template.docx", docName, true);
  
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(docName, true))
  {
  }

   备注:这一步,我们拷贝一个空文档到debug路径下,给其一个名字,并且用SDK打开文档

  7. 将下面的程序加入到method Main() 中的using (WordprocessingDocument myDoc = WordprocessingDocument.Open(docName, true)) {}中;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | CreateTable

  // Grab main document part
  MainDocumentPart mainPart = myDoc.MainDocumentPart;
  Document doc
= mainPart.Document;
  
// Connect to databse
  AdventureWorksDataContext db = new AdventureWorksDataContext();
  
// Create table and add properties
  Table table = new Table();
  
// Header strings for table
  string[] headerContent = new[] { "Name", "Subcategory", "Price", "Image" };
  
// Create row
  TableRow header = CreateRow(headerContent, null);
  table.AppendChild(header);

   备注:这一步,我们生成一个LINQ to SQL类的instance – AdventureWorksDataContext。我们有生成一个Table object。

  8. 将以下程序直接加到上一段程序后面;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | QueryDB

  var productQuery = from p in db.Product group p by p.ProductID;
  
int i = 1;
  
foreach (var product in productQuery)
  {
  
foreach (var item in product)
  {
  
// insert your next code snippet here
  }
  }

   备注:在这一步,我们从AdventureWorks2008 LINQ to SQL类连到AdventureWorks2008数据库,并从中读取数据出来。

  9. 将以下程序直接加到上一段程序的inner foreach loop里;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | ProcessProduct

  string price = "$";
  
if (item.ListPrice != 0)
  {
  
// Get data from query
  price += Math.Round(item.ListPrice, 2);
  
string imgId = "rIdImg" + i;
  i
++;
  
string[] content = new[] { item.Name,
  item.ProductSubcategoryID
== null ? null : item.ProductSubcategory.Name,
  price };
  
// Insert the next code snippet here
  }

   备注:在这一步中,每个产品的价格被四舍五入到小数点后两位。每个产品的Image 都生成一个id。我们也生成一个字串列string array来存储每个产品的名称(name),副类(Subcategory)和价格(price)。

  10. 将以下程序直接加到上一段程序的后面,但是要在‘if’语句里面; 为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | ProcessProductImage

  // Add a new image for every image in DB
  ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Gif, imgId);
  
// Stream image into image part
  imagePart.FeedData( new MemoryStream( item.ProductProductPhoto.First().ProductPhoto.LargePhoto.ToArray()));
  
// Calculate image size for xml
  Bitmap bitmap = new Bitmap( new MemoryStream( item.ProductProductPhoto.First().ProductPhoto.LargePhoto.ToArray()));
  
int widthInEmu, heightInEmu;
  CalculateEmus(bitmap,
out widthInEmu, out heightInEmu);
  
// Create a new drawing object based on xml
  Drawing d = GenerateDrawing(imgId, item.ProductProductPhoto.First().ProductPhoto.LargePhotoFileName, widthInEmu, heightInEmu);

   备注:在这一步中,我们生成了一个ImagePart 的instance,它的类是Gif。它的内容是从数据库中取来的。我们也生成了一个Bitmap对象。同样它里面的数据也是通过计算图形文档的高和宽而得来的。然后我们生成一个Drawing对象,并设置它指向刚才生成的ImagePart的instance。

  11. 将以下程序直接加到上一段程序的后面,但是要在‘if’语句里面; 为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | AddRow

  // Add row
  TableRow tr = CreateRow(content, d);
  table.AppendChild(tr);

   备注:在这一步中,我们生成一个TableRow对象,并把它加到最后我们要生成的文档中。到此,我们已经完成了处理每一个TableRow的步骤。

  12. 将以下程序加到foreach语句的后面(最外面的那个foreach语句),但是要在using语句的里面; 为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | AddRow.

  // Append table and save
  doc.Body.AppendChild(table);
  
// Save changes
  
// this step is not necessary as doc has been configured to automatically save changes
  doc.Save();

   备注:在这一步中,我们只是把生成的table加到文档中并保存文档。至此我们完成了所有需要加在Main()里的语句。

  13. 将以下程序加到method:static void Main(string[] args)的后面,在class Program 的closing bracket (}) 里面;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | CreateRow.

  private static TableRow CreateRow(string[] cellText, Drawing d)
  {
  
// create a table row and insert content
  TableRow tr = new TableRow();
  
// Add text
  foreach (string s in cellText)
  {
  TableCell tc
= new TableCell();
  Paragraph p
= new Paragraph();
  Run r
= new Run();
  Text t
= new Text();
  t.Text
= s;
  r.AppendChild(t);
  p.AppendChild(r);
  tc.AppendChild(p);
  tr.AppendChild(tc);
  }
  
// Add image
  if (d != null)
  {
  TableCell tcDrawing
= new TableCell();
  Paragraph pDrawing
= new Paragraph();
  Run rDrawing
= new Run();
  rDrawing.AppendChild(d);
  pDrawing.AppendChild(rDrawing);
  tcDrawing.AppendChild(pDrawing);
  tr.AppendChild(tcDrawing);
  }
  
return tr;
  }

   备注:这是一个辅助Method。我们生成每一个TableRow的内容,分别用TableCell来存放产品的名称,类别,价钱和图像(Name, Subcategory Name, Price and Drawing)。每个TableCell有是用Paragraph和Run组成的。一个Run是OpenXML中最基本的单位。

  14. 将以下程序直接加到上一段程序的后面,也是要在class Program 的closing bracket (}) 里面;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | GenerateDrawing.

  public static Drawing GenerateDrawing(string relId, string imageName, int width, int height)
  {
  Drawing element
=
  
new Drawing(
  
new wp.Inline(
  
new wp.Extent() { Cx = width, Cy = height },
  
new wp.EffectExtent() {
  LeftEdge
= 19050L, TopEdge = 0L, RightEdge = 0L, BottomEdge = 0L },
  
new wp.DocProperties() { Id = (UInt32Value)1U, Name = imageName, Description = imageName },
  
new wp.NonVisualGraphicFrameDrawingProperties(
  
new a.GraphicFrameLocks() { NoChangeAspect = true }),
  
new a.Graphic(
  
new a.GraphicData(
  
new pic.Picture(
  
new pic.NonVisualPictureProperties(
  
new pic.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = imageName },
  
new pic.NonVisualPictureDrawingProperties()),
  
new pic.BlipFill(
  
new a.Blip() { Embed = relId, CompressionState = a.BlipCompressionValues.Print },
  
new a.Stretch(
  
new a.FillRectangle())),
  
new pic.ShapeProperties(
  
new a.Transform2D(
  
new a.Offset() { X = 0L, Y = 0L },
  
new a.Extents() { Cx = width, Cy = height }),
  
new a.PresetGeometry(
  
new a.AdjustValueList()
  ) { Preset
= a.ShapeTypeValues.Rectangle }))
  ) { Uri
= "http://schemas.openxmlformats.org/drawingml/2006/picture" })
  ) { DistanceFromTop
= (UInt32Value)0U, DistanceFromBottom = (UInt32Value)0U, DistanceFromLeft = (UInt32Value)0U, DistanceFromRight = (UInt32Value)0U });
  
return element;
  }

   备注:这也是一个辅助Method。在这段程序中我们根据OpenXML文档规范生成一个Drawing对象。这个Drawing对象会被包含在我们上面生成的TableRow中的最后一个TableCell里面。

  15. 将以下程序直接加到上一段程序的后面,也是要在class Program 的closing bracket (}) 里面;为避免手工输入这些语句,可以通过插入代码段 | My Code Snippets | CalculateEmus.

  private static void CalculateEmus(Bitmap bitmap, out int widthInEmu, out int heightInEmu)
  {
  
float verticalResolution = bitmap.VerticalResolution;
  
float horizontalResolution = bitmap.HorizontalResolution;
  
int width = bitmap.Size.Width;
  
int height = bitmap.Size.Height;
  
float widthInInches = (float)width / horizontalResolution;
  
float heightInInches = (float)height / verticalResolution;
  widthInEmu
= (int)(widthInInches * 914400);
  heightInEmu
= (int)(heightInInches * 914400);
  }

   备注:这个辅助程序将产品图像的高和宽转换成用emu作为单位的值。EMU是English metric Unit的缩写,被用于OpenXML的文档规范。至此所有程序代码已经添加完毕。

  16. 将每个Method的代码隐藏起来,你的程序代码编辑窗口应该看上去像这样:

  17. 现在编辑解决方案:在解决方案资源管理器中,右击项目CreateDocFromDatabase ,选中菜单选项生成。编辑应该通过无误。

  18. 现在在运行程序之前我们要准备必须的文档:将文档Template.docx 从路径 c:\HOL\OFCHOL245\Resources\拷贝到c:\HOL\OFCHOL245\CreateDocFromDatabase\bin\Debug。可以通过以下几步完成:

  ·在Windows的任务栏(taskbar)中点击资源管理器图标打开资源管理器(从左边数第三个图标)

  ·在资源管理器中,在上方的路径中填入c:\HOL\OFCHOL245\Resources

  ·右击文档Template.docx,在菜单中选中复制。

  ·回到Visual Studio 2008,在解决方案资源管理器中,点击显示所有文件图标

  ·在解决方案资源管理器中,展开CreateDocFromDatabase | bin | Debug;右击Debug,在菜单中选择粘贴将Template.docx粘贴在bin\debug路径里。

0
相关文章