我的VSTO之路(三):Word基本知识
在前一篇文章中,我初步介绍了如何如何开发一个VSTO程序,在本文中,我将进一步深入介绍Word的插件开发。Word是一个大家在日常工作中一直接触的文档工具,也是微软最赚钱的产品之一。从最初的Word 1.0到现在的Word 2010历经了13代的演化,已经成为了一个比较复杂的系统。(这里稍微跑题一下,Office 2010的版本代号是version 14,但是我为什么说Word一共演化了13代呢?因为Office并没有Version 13,上一代的Office 2007是Version 12,微软觉得13这个死数字不吉利,所以直接跳过了……Oh my god
)。言归正传,我录制了一段视频来演示本文所要介绍的内容。
这段视频,描述了一个简单的Word搜索插件,包含了以下几个功能点
- 自定义Ribbon
- 自定义Task Pane
- VSTO插件中获取Word内容全文
- 修改Word内容和样式
其中关于如何创建Ribbon和Task Pane的内容,我已经在前一篇文章中介绍了,如果你还不熟悉,可以看这里。
Word Object Model 介绍
首先,要开发出良好的程序,我们需要了解我们的开发平台,而Word本身是一个很复查的平台,我在这里先从Word的对象模型开始介绍。Word Object Model中一共包含有数百个不同类型的对象,其中最关键的也是最常用的是Application、Document、Range、Selection和Bookmark,他们的关系如下图:

我来依次介绍这几个对象:
Application 对象
Application代表Word程序,而一个Word程序内可以包含多个Word文档。用通俗的话来说,无论你开几个Word文档,都是在一个Word进程里面管理。这我们以后会讲到的Excel不一样。同时Application又是所有Word对象根,你可以通过Application对象,获得其他对象。在Addin开发过程中,我们可以通过以下方式来获得Application对象:
Globals.ThisAddIn.Application
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Document 对象
Document对象代表着一个Word文档,即便你刚打开你的Word,是一个空的新文档,也会有一个Document。在开发过程中,以下这个属性从Application中获得当前的Document对象:
Globals.ThisAddIn.Application.ActiveDocument
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
此外Application对象也维护着一个集合,即Application.Documents,里面包含着现在所有打开的Word文档。
Range 对象
Range是一个比较特殊的对象(我其实都不知道如何用中文翻译贴切地翻译这个单词),在你日常使用Word的过程中,你甚至可能不会知道有这样一个对象存在,但如果你想通过程序方式修改Word正文的内容,Range是一个很关键的对象。用微软官方的表述,Range代表着文档中一片连续的区域,微软为它列出了一下几个特性:
- Range的组成成分可以是单独的插入点,也可以是一个文本范围或整个文档。
- Range包含非打印字符,例如空格、制表符和段落标记。
- Range可以是当前所选内容所表示的区域,也可以表示当前所选内容之外的区域。
- Range与始终可见的所选内容不同,它在文档中是不可见的。
- Range不随文档保存,仅存在于代码运行期间。
我再为它加2条
- Range有明确的开始和结束,但不同的Range之间是可以有交集的
- Range的长度是在变化的,如果你往一个Range里面插入的一个单词,它的长度会自动变长。
获得Range对象的方式很多,我们可以通过Document对象的Range(ref object Start = Type.Missing, ref object End = Type.Missing)方法,创建一个自定义的Range。通过Word中文档相关的对象都有一个Range属性,比如Paragraph.Range、Selection.Range。
Selection 对象
Selection代表着当前光标所选中的对象,我们在开发过程中这个对象会和Application.WindowSelectionChange一起使用。
1: //
2: // Summary:
3: // Occurs when the selection changes in the active document window.
4: event ApplicationEvents4_WindowSelectionChangeEventHandler WindowSelectionChange;
Delegate接口
1: [TypeLibType(16)]
2: [ComVisible(false)]
3: public delegate void ApplicationEvents4_WindowSelectionChangeEventHandler(Selection Sel);
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Bookmark 与 Content Control
Bookmark即书签,在Word文档中做一个标记,方便查阅。开发过程中,我们可以基于Range来创建Bookmark。如:
1: // 将第一段文档标记为一个BookMark
2: Word.Range range = Globals.ThisAddIn.Application.ActiveDocument.Paragraphs[0].Range;
3: range.Bookmarks.Add("JustinTest");
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
至此我们已经介绍完毕了Word中的主要5个对象,现在我们运用我们学到的东西,来实开头视频中的那个插件
插件:自定义Search面板
我先定义一下我们要实现的功能点,获得Word文档的内容,取得与检索关键字相关的上下文并显示在ListView中,当用户点中ListView中的项目时,高亮显示Word文档中对应的内容。以下是如何实现这几个功能的介绍。
获得Word的全文
要取得当前Word的全文,我们主要要解决两个问题。
- 如何获得当前的Document对象?
- 如何通过Document对象获得文档内容?
对于第一个问题,因为搜索功能主要是写在Task Pane中的UserControl中(这一点在上一篇文章中已经有过介绍),所以取得Document对象的主要方法,是通过Application对象获得ActiveDocument的对象,即当前编辑的文档:
Globals.ThisAddIn.Application.ActiveDocument
对于第二个问题,我们有两个方法:首先,Document对象有Paragraphs集合,这个集合里面包含了每个段落的对象,而每个段落对象,都有Range属性,我们可以通过Paragraph.Range.Text,来获得每个段落的正文。其次Document对象有一个Range方法,通过它我们可以把整个Document作为一个Range。
Search按钮代码
1: private void btnSearch_Click(object sender, EventArgs e)
2: {
3: // 清楚文档中的高亮显示
4: ClearMark();
5:
6: lvSearchResult.Items.Clear();
7: if (string.IsNullOrWhiteSpace(tbSearchText.Text))
8: {
9: return;
10: }
11:
12: // 按段落检索
13: Word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;
14: if (currentDocument.Paragraphs != null &&
15: currentDocument.Paragraphs.Count != 0)
16: {
17: foreach (Word.Paragraph paragraph in currentDocument.Paragraphs)
18: {
19: MatchCollection mc = Regex.Matches(paragraph.Range.Text, tbSearchText.Text.Trim(), RegexOptions.IgnoreCase);
20: if (mc.Count > 0)
21: {
22: foreach (Match m in mc)
23: {
24: try
25: {
26: int startIndex = paragraph.Range.Start + m.Index;
27: int endIndex = paragraph.Range.Start + m.Index + m.Length;
28:
29: Word.Range keywordRange = currentDocument.Range(startIndex, endIndex);
30:
31: // 获取上下文信息
32: // 获取前两个单词的位置(如果有)
33: startIndex = GetStartPositionForView(paragraph, m, startIndex);
34:
35: // 获取后两个单词的位置(如果有)
36: endIndex = GetEndPositionForView(paragraph, m, endIndex);
37:
38: // 在ListView中展示检索的关键字以及其上下文
39: Word.Range range = currentDocument.Range(startIndex, endIndex);
40: ListViewItem item = new ListViewItem(range.Text);
41: item.Tag = keywordRange;
42: lvSearchResult.Items.Add(item);
43: }
44: catch (Exception ex)
45: {
46: MessageBox.Show(ex.Message);
47: }
48: }
49: }
50: }
51: }
52: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
介绍一下这段代码的几个功能点:
- 在Search功能开始时,先清除文档中的高亮显示(ClearMark方法稍后会介绍)。
- 分段落,依次查找关键字。
- 获得关键字的上下文,并放入ListView中显示。需要注意的时候,我在ListViewItem的tag对象里面,存入了Keyword在文档中的Range,为了ListView点击事件。
ListView点击事件
1: private void lvSearchResult_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
2: {
3: ClearMark();
4: if (lvSearchResult.SelectedItems.Count > 0)
5: {
6: Word.Range range = lvSearchResult.SelectedItems[0].Tag as Word.Range;
7:
8: // 为了可以恢复被修改的Range,我先将该Range和原本的Color放入Class的成员
9: _LastRange = range;
10: _LastRangeBackColor = range.HighlightColorIndex;
11: range.HighlightColorIndex = Word.WdColorIndex.wdYellow;
12: }
13: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
总体来说这段代码很简单,我稍微介绍一下Range对象的使用,这里我修改了HighlightColorIndex属性,来修改文字的背景色,如果你修改字体相关的样式,可以通过Range.Font属性。(此外,我会在下一篇文中,介绍如何通过Range加超链接、书签或者Content Control)。
其他方法
1: private void ClearMark()
2: {
3: if (_LastRange != null)
4: {
5: _LastRange.HighlightColorIndex = _LastRangeBackColor;
6: }
7: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
我在Search事件和ListView点击方法中都会先调用这个方法,它会使用在lvSearchResult_ItemSelectionChanged中保存的Range和Color,来恢复之前的样式。
总结
本文中,我介绍了Word对象模型的基本内容和我写的一个Word插件,包含对Application、Document和Range对象的操作。Word程序包含的内容是很多的,本来想把自己了解的Word知识一次写完,但写到这里发现已经写了很多了,怕大家会看得累
,所以先发出来,再下一篇文章,我会进一步深入介绍Word插件开发。下次内容预告:
- 修改右键菜单
- 往文档中插入内容
- 添加超链接、书签
- 基于选中内容,显示悬浮框
最后,本文范例的代码可以在这里下载。此外,本文欢迎转载,但请保留出处,大家如果有问题,可以联系我 justin.tyrael@gmail.com。
我的VSTO之路(三):Word基本知识的更多相关文章
- 我的VSTO之路(四):深入介绍Word开发
原文:我的VSTO之路(四):深入介绍Word开发 在上一篇文章中,我介绍了Word的对象模型和一些基本开发技巧.为了更好的介绍Word插件开发,我为本文制作了一个Word书签的增强版,具体功能是让用 ...
- 我的VSTO之路(五):Outlook初步开发之联系人扩展
原文:我的VSTO之路(五):Outlook初步开发之联系人扩展 上一讲我们完成对Word的介绍,文本开始,我将着重介绍Outlook.Outlook是微软Office中一个非常实用的工具,尤其在一个 ...
- 我的VSTO之路(二):VSTO程序基本知识
原文:我的VSTO之路(二):VSTO程序基本知识 开始之前,首先我介绍一下我的开发环境:VS2010 + Office 2010,是基于.Net framework 4.0和VSTO 4.0.以下的 ...
- 我的VSTO之路:序
原文:我的VSTO之路:序 VSTO是微软提供给.Net开发人员的一个接口,通过他我们可以对Office程序做一些处理.但是这个接口并不尽善尽美,相比微软的很多其他产品,VSTO的稳定性并不好,相关的 ...
- 学习之路三十九:新手学习 - Windows API
来到了新公司,一开始就要做个程序去获取另外一个程序里的数据,哇,挑战性很大. 经过两周的学习,终于搞定,主要还是对Windows API有了更多的了解. 文中所有的消息常量,API,结构体都整理出来了 ...
- VSTO学习笔记(三) 开发Office 2010 64位COM加载项
原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...
- word2vec 中的数学原理具体解释(三)背景知识
word2vec 是 Google 于 2013 年开源推出的一个用于获取 word vector 的工具包,它简单.高效,因此引起了非常多人的关注.因为 word2vec 的作者 Tomas M ...
- RxSwift之路 1#Swift语法知识准备
RxSwift之路 1#Swift语法知识准备 在开始学习 RxSwift 之前,一定要对 Swift 相关语法有所了解,否则就很难理解为什么可以这样.关于 Swift 的学习其实只要看看 Swift ...
- Redis——学习之路三(初识redis config配置)
我们先看看config 默认情况下系统是怎么配置的.在命令行中输入 config get *(如图) 默认情况下有61配置信息,每一个命令占两行,第一行为配置名称信息,第二行为配置的具体信息. ...
随机推荐
- mysql使用硬链接配合truncate 删除2.2T的表 --杨奇龙
http://blog.csdn.net/wyzxg/article/details/8626814 http://blog.itpub.net/22664653/viewspace-750408/ ...
- Fragment的懒加载
我们在做应用开发的时候,一个Activity里面可能会以viewpager(或其他容器)与多个Fragment来组合使用,而如果每个fragment都需要去加载数据,或从本地加载,或从网络加载,那么在 ...
- Volley的简单二次封装
新建一个application package com.honghe.myvolley.app; import com.android.volley.RequestQueue; import com. ...
- Java 国际化 语言切换
Java国际化 我们使用java.lang.Locale来构造Java国际化的情境. java.lang.Locale代表特定的地理.政治和文化.需要Locale来执行其任务的操作叫语言环境敏感的 ...
- 禁止鼠标多次点击选中div中的文字
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Fire ...
- Stream类
为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...
- LiLei&HanMeiMei的隐式马尔可夫爱情
一篇非常棒的隐马尔可夫入门文章...推荐! from: http://staffwww.dcs.shef.ac.uk/people/W.Liu/hmm.html
- maven占位符
maven占位符默认是${} 也可以自己指定. pom.xml配置如下: <plugin> <groupId>org.apache.maven.plugins</grou ...
- 阿里云服务器如何安装memcached
方法/步骤 1 使用Xshell登陆阿里云服务器. 请使用root帐号登陆.下面的操作全部在home目录里执行 2 安装libevent. 输入命令 yum -y install libevent-d ...
- 层模型--绝对定位(position:absolute)
如果想为元素设置层模型中的绝对定位,需要设置position:absolute(表示绝对定位),这条语句的作用将元素从文档流中拖出来,然后使用left.right.top.bottom属性相对于其最接 ...