VSTO中Word的Range複製方式 前言 "VSTO" 是一套用於創建自定義Office應用程式的Visual Studio工具包,通過Interop提供的增強Office對象,可以對Word文檔進行編程操作。 "Range" 是Word中執行操作的一個單元,可以理解成文檔中一個選中的部分或者區 ...
VSTO中Word的Range複製方式
前言
VSTO是一套用於創建自定義Office應用程式的Visual Studio工具包,通過Interop提供的增強Office對象,可以對Word文檔進行編程操作。Range是Word中執行操作的一個單元,可以理解成文檔中一個選中的部分或者區域,針對這個選中部分,可以應用格式、修改文字和顏色等功能。在相同的文檔和不同的文檔之間,將Range的內容從原處複製到其他的Range中,是一種常見的業務需求。本文總結了常見的幾種Range複製方式,對其特點進行了討論分析,並提出一些改良的地方。
剪貼板複製
剪貼板是windows進程間通信的一種方式。原理是這樣的,Range.Copy()
將內容複製到剪貼板中,然後AnotherRange.Paste()
從剪貼板中獲取數據,然後複製到自己的區域內。
//原理示意
/// <summary>
/// range間的複製
/// </summary>
/// <param name="source">源range</param>
/// <param name="target">目標range</param>
public void Copy(Word.Range source, Word.Range target)
{
source.Copy();
target.Paste();
}
這種方式可靠性低,速度慢。就像薛定諤的貓一樣,有時候可用,有時候不可用。原因在於使用了剪貼板作為中介,許多進程都在使用剪貼板,顯而易見,使用剪貼板有許多未知的問題。
改良
為了緩解這種弊端,我們稍微做下修改,在剪貼板失敗的時候,進行重試,大於一定次數就拋出異常。假設剪貼板失敗是獨立不相干的事件,可以在概率上提高成功的可能性。連續多次失敗的概率將會變得很小,除非存在顯著的剪貼板問題。依據的原理是貝努利分佈,這裡不予證明。
/// <summary>
/// range間的複製
/// </summary>
/// <param name="source">源range</param>
/// <param name="target">目標range</param>
public void Copy(Word.Range source, Word.Range target)
{
int num = 0;
//重試總次數
int limitNum = 5;
retry:
try
{
source.Copy();
target.Paste();
}
catch(Exception)
{
num++;
//連續多次失敗,就拋出異常
if (num > limitNum)
{
throw;
}
goto retry;
}
}
XML複製
我們知道Word文檔其實是一個OpenXml的結構樹,一個複雜無比的XML,所以Word中的元素是XML的一部分,也是一個XML。參考HMTL來說,兩個相同的標簽內容就是相同的兩個標簽。
同理通過Range.XML
獲取到Range的XML文本結構,再通過AnotherRange.InsertXML()
方法插入到目標range的區域,就完成了複製。
/// <summary>
/// range間的複製
/// </summary>
/// <param name="source">源range</param>
/// <param name="target">目標range</param>
public void Copy(Word.Range source, Word.Range target)
{
target.InsertXML(source.XML);
}
這種方式的穩定性比剪貼板的強,不存在中間過程的通信轉換,速度也快一些。但是這種方式,也不一定完全就能成功,推測是Word的格式相容性,有些Word文檔的XML不能複製,或許是舊版本的Word文檔,或許是某個版本Wps編輯的文檔,原因很難探究,畢竟Word是世界上最複雜的軟體之一了。
混合複製
通過上面的兩種複製方式的陳述,我們知道這兩種方式都有各自的特點。
方式 | 速度 | 可靠性 | 格式相容性 | |
---|---|---|---|---|
剪貼板 | 慢 | 低 | 高 | |
XML | 快 | 中 | 中 |
這兩種複製方式都有可能失敗。為了避免這種情況,可以設計一個方案,優先使用XML進行複製,如果失敗了,退化到使用剪貼板複製;重試若幹次,直到成功或者超過重試次數。
/// <summary>
/// range間的複製
/// </summary>
/// <param name="source">源range</param>
/// <param name="target">目標range</param>
public void Copy(Word.Range source, Word.Range target)
{
int num = 0;
int limitNum = 5;
retryCopy:
//偶數次使用XML複製
if (num % 2 == 0)
{
//no
}
//奇數次使用剪貼板複製
else
{
source.Copy();
}
try
{
if (num % 2 == 0)
{
target.InsertXML(source.XML);
}
else
{
target.Paste();
}
}
catch (Exception)
{
//失敗了進行重試
num++;
//超過重試次數,拋出異常
if (num > limitNum)
{
throw;
}
goto retryCopy;
}
}