阅读目录程序员
在各种应用系统开发中,和Word相关的应用可谓至关普遍。如各种MIS系统、各类和实际业务结合紧密的系统、须要制式报表的系统等,都须要对Word进行操做,典型的应用包括:编程
一、内嵌Word。在系统中内嵌Word,这样,既能够利用Word强大的功能进行文档的新建、编辑、修改、排版,同时还节省了用户对于编辑器操做的学习成本,提升了文档格式的通用性。app
二、Word的二次开发。经过Word自带的宏,利用VBA(Visual Basic Appplication)进行开发,实现各类复杂的自动化功能。框架
三、前台不显示Word操做界面,而在后台对Word文档进行操做。包括:1)读入word文档,解析内容,获取须要的数据;2)把数据写入Word模板,生成符合格式要求的Word文档。编辑器
上述应用中,前两个应用领域相对特定,且须要对Word进行深度的二次开发,本人涉猎有限,于是不进行过多的讨论。而对于第三种应用,因为Word软件的普及率很是高,基本上能够把DOC文档看做是一个通用的文档结构。同时,Word在格式控制方面功能很是强大。所以,使用Word来制做输出文件或者报表,不光格式易于控制(用户能够在Word中制做好须要的模板,替换真实数据就得到须要的输出文档或者报表),用户的接受度等方面都有很大的优点,近年来愈来愈受到重视。下文主要尝试讨论如何利用Word模板生成须要的Word文档的实现。函数
1983年,微软发布了基于MS-DOS的Word 1.0版,至今已经30余年了。对于Word的二次开发,也是有着悠久的历史。就本人的开发经验而言,在近十年前,就已经在Visual Basic 6.0平台上,进行内嵌Word的开发,这个在当年也是很是流行的一种开发。时至今日,Word的二次开发仍然是每一个开发者频繁遇到的问题。学习
可是,Word的开发相对于其余的二次开发,甚至于相对于同门的也很复杂的Excel来讲,开发的难度都要大不少,缘由来自如下方面:测试
一、Word 的对象结构复杂。因为Wrod有着久远的历史,这既是它的优点也是它的包袱,它必需要保持足够的兼容性,所以DOC文档结构也就变得很是的复杂了。在Word中,有着复杂的对象结构,如Application、Document、BookMarks、Range、Selection、Paragraph等,它们之间既有层级关系,还有嵌套关系,有时为了一个小小的功能,却没法找到操做的对象。网站
Word 的对象结构ui
二、Word功能复杂。做为微软的拳头产品,多年以来,Word的功能愈来愈强大。尽管大多数的功能对于二次开发来讲是彻底用不到的,但还得去了解和学习,这就须要付出额外的代价。以Find为例,其参数居然高达15个,以下所示:
Find.Execute(FindText, MatchCase, MatchWholeWord, MatchWildcards, MatchSoundsLike, MatchAllWordForms, Forward, Wrap, Format, ReplaceWith, Replace, MatchKashida, MatchDiacritics, MatchAlefHamza, MatchControl)
但大多数状况下,咱们只会用到FindText、ReplaceWith等极少数参数而已。
三、版本问题。Word的众多版本也给二次开发带来不少困扰,开发者必需要对于当前多种Word版本都存在的状况有所考虑,并作好兼容性的处理才行。
谈到Word的二次开发,就必需要提到DsoFramer。它是微软提供一款开源的用于在线编辑、调用Word、 Excel 、PowerPoint等的ActiveX控件。国内不少著名的OA中间件,电子印章,签名留痕等大多数是依此改进而来的。
DsoFramer操做Word很简单,加载ActiveX控件后就能够直接操做Office文档了。以咱们要进行的主要操做——替换文档中的关键字为例,在Visual Basic中代码以下:
dso.Open "new.doc" dso.Replace "[标题]","新标题",3 dso.Save "c:\new2.doc" dso.Close
在VB6中加载控件,以下图所示:
因为DsoFramer是COM时代的产物,适用于VB、VC开发者,在 .Net下开发,或者进行Web应用开发,就显得有点力不从心。在实际开发中,经常出现一些莫名其妙的错误。另外,它的工做模式须要先在界面中打开文档再进行各类操做,这种模式也不适应Web应用程序的须要。
微软在.Net框架下,推出了Microsoft.Office.Interop.Word及其余的互操做方式,可以更好地对Office文档进行二次开发。
使用Interop进行二次开发,首先须要了解Word的对象结构,完整的Word对象结构图以下(来自官方的VBA_Word帮助文件):
Application: 用来表现WORD应用程序,包含其它全部对象。他的成员常常应用于整个Word,能够用它的属性和方法控制Word环境。
Document对象: Document对象是Word编程的核心。当打开一个已有的文档或建立一个新的文档时,就建立了一个新的Document对象,新建立的Document将会被添加到Word Documents Collection。
Selection: Selection对象是描述当前选中的区域。若选择区域为空,则认为是当前光标处。
Rang: 是Document的连续部分,根据起始字符的结束字符定议位置。
Bookmark: 相似于Rang,但Bookmark能够有名字并在保存Document时Bookmark也被保存。
了解到Word的对象结构后,就能够考虑怎样操做了。
一、如何打开和关闭Application及Document对象。
打开和关闭操做比较简单,实现代码以下:
//打开 Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application(); Microsoft.Office.Interop.Word.Document doc = app.Documents.Open(ref fn, ref oMiss, ref oTrue, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oTrue,ref oMiss, ref oMiss, ref oMiss, ref oMiss); //关闭 doc.Close(ref oFalse, ref oMiss, ref oMiss); doc = null; app.Quit(ref oFalse, ref oMiss, ref oMiss); app = null;
二、写入
因为Word的结构复杂,要找到写入的位置就比较复杂。在Interop操做中,能够对Range的text进行操做,如:
doc.Range.Text="newtext";
写入报表,最经常使用的方法,是把模板作好,定义好特征串,进行替换便可。天然而然咱们想到了经过Word的替换功能来完成。其主要代码以下:
object s1 = OldString; object s2 = NewString; object rep = Microsoft.Office.Interop.Word.WdReplace.wdReplaceAll; doc.Content.Find.ClearFormatting(); doc.Content.Find.Execute( ref s1, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref s2, ref rep, ref oMiss, ref oMiss, ref oMiss, ref oMiss);
用简单的字符串测试,代码工做正常,可是,用实际的数据测试发现没法完成替换。追踪后发现问题:替换的目标字符串不能过长,不然就会替换失败,这个结果和Word软件中替换的实际状况一致。
因为批量查找替换操做不能完成替换成长文本目标,直观的解决思路就是采用手动的方式,找到一个特征串替换一个。可是在Interop中,因为Find对象比较复杂,屡次尝试没有成功,比较实验后,发现能够采用遍历方式进行替换。
因为文档下有多个段落,于是能够对文档中的每一个段落进行遍历,若是在段落中找到特征串,就把段落的文字提取出来,放在字符串中,对该字符串进行替换后再从新赋值给这个段落。这种方式须要段落的格式保持一致,这样就能够拼出完成段落来了。核心代码以下:
for (int i = 0; i < doc.Paragraphs.Count; i++) { try //只能用尝试的方法来进行替换 { if (doc.Paragraphs[i].Range.Text.IndexOf(OldStringArray[k]) >= 0) { doc.Paragraphs[i].Range.Text = doc.Paragraphs[i].Range.Text.Replace(OldStringArray[k], NewStringArray[k]); } } catch { } }
在实际操做中,发现遍历操做很是容易出错,缘由在于文档对象存在着不少的段落,超过了能够看见的段落数量,所以就必须加入一个错误捕获功能以忽略一些意外的错误。
经过这种替换,能够成功的完成整段的替换,效果以下图:
若是被替换的特征串并非独立的段落、或者位于表格中的话,上述代码可否工做正常呢?以下图所示,在段落中和表格中增长两个特征串进行替换,结果以下图所示:
结果能够看到,表格中虽然顺利替换,但格式仍是受到影响。而段落中的文字虽然替换了,格式也被改成统一的格式了。
对于一个追求完美的程序员来讲,上述的bug是没法容忍的,尽管它已经能够凑合使用了,但要忽视的确作不到。根据前面的铺垫,可能感受到问题的解决还得把Word的内部构造搞清楚。
在网上搜索了好久,都没办法找到关于查找和替换的更详细的解决方法。通过一段时间的困惑以后,忽然发现,其实这些资料我本身自己就有。就是使用VBA开发Office的一系列资料,里面关于Word的对象结构,有着远比网上只言片语靠谱的解答。学习的过程直接跳过,把几条重要的结论给出来:
1)用Content的Find查找,只能进行批量的查找和替换,若是想找到第一个,停下来,操做,是不行的。
2)上述的“查找——操做”的思路,只能用Selection对象来完成,而Selection对象,Document的属性中没有、Content的属性中也没有。只有谁有?Application!
3)用Application的Selection的Find找到后,结果就在Selection.Text中,但要替换,只能对Selection.Range.Text进行赋值才行。
下面是实现代码:
object oFindText=OldString; app.Selection.SetRange(0, 0); app.Selection.Find.Execute(ref oFindText,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oTrue,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss); if (app.Selection.Find.Found) { app.Selection.Range.Text=NewString; }
再次对上述第二种模板进行替换,结果以下:
这段来之不易的代码,固然要保存在CommonCode(v2.0.6)中,之后要调用Word模板实现生成新文档就很是简单了,代码以下:
CommonCode.WordUtil.ReplaceAndSave(Application.StartupPath + "\\temp2.doc", Application.StartupPath + "\\1.doc", new string[] { "[%单选%]", "[%分数%]", "[%数量%]" }, new string[]{@"
一、关于公开信息搜密,正确的是
A.在互联网公开信息中搜密须要高深的技术
B.在互联网中的主流网站中不存在秘密
C.只要经过关键词搜索和按期跟踪网站就可能找到秘密信息
D.公开信息搜密由于方法简单,因此效果较差,不受重视","98","10"});
对于替换Word模板内容生成Word文档的需求,在.Net下能够采用Interop的方式来实现。具体的实现手段,有批量替换、遍历替换、单步查找并替换等方式。批量替换不能进行长文本的替换故不可用,遍历段落替换不能对段内的关键词进行保持格式的替换,也不完美。单步查找替换调用全局的查找功能(app.Selection.Find),并可以定位查找到的内容并进行操做,是完成需求的最佳方案。
单步查找替换实现方案被整合至CommonCode.WordUtil.ReplaceAndSave函数中,能够直接使用。
说明:引用CommonCode.dll和Microsoft.Office.Interop.Word.dll便可。
原来demo缺了log4net引用,添加