.NET1.1下,使用C#自動生成Word2003文檔(通過操作COM組件實現)

做了一個多月的C#生成Word文檔的工作,我從一開始的對這個一竅不通,到現在的順利完成了這個功能模塊,其中還是有點心得的。想想自己說不定以後還會用到,於是想吧這些心得寫下來,以供自己以後的學習。同時也希望對那些正在或正要編程實現自動生成Word的朋友有些小小的幫助。

對於用C#來自動生成Word文檔來說,最大的問題是微軟提供的所有文檔的源代碼一般都是VBA編程的,沒有C#的現成文檔,最多也只是一些How To文檔。顯然,VBA編程和C#是有一定區別的,VBA編程的風格和C#是完全不同的,它有着VB編程快捷的特性,可以省略參數,可以對Style對象賦值等的功能是C#所沒有的。其次,在這個任務通過Word錄製宏查看到的宏代碼也是由VB代碼顯示的,我們必須要把這些宏代碼轉換到C#代碼。所以,做這個任務的人必須先有一點VB的經驗(最起碼知道那些代碼是在幹什麼)。
 
下面是我自學C#生成Word文檔的過程(首先必須安裝好Word2003和.NET2003):


一、下載Word的VBA編程參考手冊和網上的在線資料
微軟大概覺得VB用的人太少了,想大力發展之,搞得Office編程的參考手冊都是VBA編程,害的我不懂VB的人也不得不學。不過沒辦法,要做這個工作,微軟的參考手冊是不能少的,可以從http://msdn.microsoft.com/office/downloads/vba/default.aspx這個頁面中下到相應的的Office(本人使用的是Word2003,但好像只用WordXP的參考手冊)的VBA Language References(當然這個文檔是全英文的,看她簡直是我的噩夢)。沒有必要看完這個參考手冊,只要搜索需要的函數,然後看看就可以了。

有了這個參考手冊是遠遠不夠的,我們需要去網上搜集大量的現成代碼來看看才能快速的上手。下面是一個微軟老大提供的關於C#生成Word的中文How To文檔,感覺很好,分享一下http://support.microsoft.com/search/default.aspx?query=Word&catalog=LCID%3D2052&spid=1108&qryWt=&mode=r&cus=False。當然,微軟提供的文檔都是最基礎的,我們只能在需要用到這個功能的時候才只得看看一看看,很全面,但沒有一個包含所有東西的程序。所以,我們還得再在網上淘資料。終於我在http://www.codeproject.com/aspnet/wordapplication.asp(建議:請看完Michela寫的這篇文章後再看下面的我的心得,他那裏介紹了怎麼建立一個項目,如添加引用等,我就不再累贅了,有空的話都想把他的文章翻譯過來 )。這個頁面裏找到了我所需要的:一個封裝Word操作的類(他的Word版本好像是XP),雖然這個類的功能很少,但我們可以按照原則自己寫代碼,擴充類庫。基本的原則是:把底層的Word操作封裝在這個類中,外層通過調用此封裝類實現對Word的操作。


二、編程實現:通過查看Word宏代碼完善自定義類庫
 
有了這樣一個大致的框架以後,我們就可以開始用C#開始實現各種Word操作的功能。總的來說,這項工作不難,但很繁瑣(要看你對Word操作的熟悉程度)。
 
一般情況下,自動生成的Word文檔會有一個模板Word文件(以.dot結尾,當然模板本身也可以是.doc的Word文檔)。在這個模板中,我們先設計好要導出文檔的總體框架,在那些需要插入文字的地方先做好書籤。在這個工作中,我們最常用到的就是書籤,使用書籤的好處是方便快捷,Word文檔中的差不多所有的定位都是通過書籤來完成的。爲了瞭解一個Word操作的具體編程實現,我們可以通過Word自帶的宏編程:在進行想要了解的操作之前,先錄製宏,操作完後再查看剛纔錄製的宏代碼,這樣我們就得到了進行這個操作的VB編碼。如:我們需要查看Word生成一個Table表的動作是怎樣的,我們可以先在進行插入Table前錄製宏,然後在Word中進行一個插入Table的操作,再停止宏,這樣我們就可以看到一個插入Table的宏代碼了。下面就是一個插入最簡單的Table的宏代碼: Sub Macro8()Sub Macro8()
'
' Macro8 Macro
' by 林輝(sharemeteor)
' 宏在 2005-8-19 由 MC SYSTEM 錄製
'
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        4, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitFixed
    With Selection.Tables(1)
        If .Style <> "網格型" Then
            .Style = "網格型"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
End Sub
 
瞭解這些後,我們就需要學會從VBA編程到C#實現之間的轉變(這是我碰到的最大難題)。總結下來,兩者間的函數名一般是相同的,但由於VB可以缺省參數而C#不行,所以我們必須同時瞭解那些缺省參數,並進行合理的填充。那些在VB代碼中出現的參數也要進行適當的改變才能應用於C#中。比如打開一個Word文檔的操作吧,Word的宏代碼如下: Sub Macro9()Sub Macro9()
' Macro9 Macro
' by 林輝(sharemeteor)
' 宏在 2005-8-19 由 MC SYSTEM 錄製
'
    Documents.Open FileName:="test.doc", ConfirmConversions:=False, ReadOnly:= _
        False, AddToRecentFiles:=False, PasswordDocument:="", PasswordTemplate:= _
        "", Revert:=False, WritePasswordDocument:="", WritePasswordTemplate:="", _
        Format:=wdOpenFormatAuto, XMLTransform:=""
End Sub 它只有11個參數,但在C#裏需要16個參數值(Word2003中是16個參數,WordXP爲15個)。在C#中,Word.ApplicationClass下的Documents屬性和VB宏代碼中的Documents對等,不過你需要獲得Word.ApplicationClass的實例後才能用。 // Open a file (the file must exists) and activate it
        public void Open( string strFileName)
        {
            object fileName = strFileName;
            object readOnly = false;
            object isVisible = true;
            object missing = System.Reflection.Missing.Value;
 
            oDoc = oWordApplic.Documents.Open(ref fileName, ref missing,ref readOnly, 
                ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, 
                ref missing, ref missing, ref isVisible,ref missing,ref missing,ref missing,ref missing);
 
            oDoc.Activate();
        }

缺省參數一般可以通過賦System.Reflection.Missing.Value值就可以了。而那些VB代碼中出現了的參數,我們必須先通過查找VBA Language References參考手冊瞭解其具體的值(實際上這些參數都是枚舉變量,其值大多數都是Object型的整數),然後再在C#中賦予相同的Object型的整數值。需要注意的是,C#中的參數一般都是引用型的,要加ref。

這裏有些方便的小技巧,在參考手冊的Reference/Enumerations下可以找到那些參數的名稱和其值,我們可以通過直接賦整數值實現,但在C#的Word類庫中,Word.Wd***這個枚舉量下都會有一個值和VB中的這個參數對應,所以建議用這些枚舉值進行賦值。如VB有個參數叫wdAlignParagraphCenter,我們通過查參考手冊知道它是WdParagraphAlignment下的枚舉值,那麼它在C#中的值爲Word.WdParagraphAlignment.wdAlignParagraphCenter。

三、編程中遇到的問題及解決
在這個工作中,碰到點問題是難免的,只要你用心,相信只是的問題,肯定可以解決的。下面是我碰到的一些問題和我的解決辦法,希望對大家有用。

1. Style等對象不能賦值的問題
感覺微軟對Office的類庫的設計可能存在問題,很多在VB中可以賦值的對象如Style,但在C#就是不能賦值。這引來了很多問題,如前面的產生Table的宏中就有“.Style = "網格型"”的語句,這在C#中是不可能用一條等價的語句來實現的。

這裏有兩種解決辦法,一種是乾脆不用Style,另一種是間接實現Style的賦值。

有些地方的Style是可以被替換的,如產生table的宏中的Style,它的Style只不過是是定義邊框的樣式,我們可以手工定義樣式來替代Style,用下列函數實現產生一個Table:         public void RunMacroForTable(int rows,int columns)
        {
            object _DefaultTableBehavior = Word.WdDefaultTableBehavior.wdWord9TableBehavior;
            object _AutoFitBehavior = Word.WdAutoFitBehavior.wdAutoFitFixed;
            oWordApplic.ActiveDocument.Tables.Add(oWordApplic.Selection.Range,rows,columns,
                ref _DefaultTableBehavior,ref _AutoFitBehavior);
 
            Word.Table table = oWordApplic.Selection.Tables[1];
//            oWordApplic.Selection.Rows.HeightRule = Word.WdRowHeightRule.wdRowHeightAtLeast;
            oWordApplic.Selection.Rows.Height = oWordApplic.CentimetersToPoints((float)0.75);
            //            if(table.Style != "網格型")
            //                table.Style = "網格型";
            對邊框進行定義#region 對邊框進行定義
            table.ApplyStyleHeadingRows = true;
            table.ApplyStyleLastRow = true;
            table.ApplyStyleFirstColumn = true;
            table.ApplyStyleLastColumn = true;
 
            Word.Border border = table.Borders[Word.WdBorderType.wdBorderLeft];
            border.LineStyle = Word.WdLineStyle.wdLineStyleSingle;
            border.LineWidth = Word.WdLineWidth.wdLineWidth150pt;
            border.Color = Word.WdColor.wdColorAutomatic;
 
            border = table.Borders[Word.WdBorderType.wdBorderRight];
            border.LineStyle = Word.WdLineStyle.wdLineStyleSingle;
            border.LineWidth = Word.WdLineWidth.wdLineWidth150pt;
            border.Color = Word.WdColor.wdColorAutomatic;
 
            border = table.Borders[Word.WdBorderType.wdBorderTop];
            border.LineStyle = Word.WdLineStyle.wdLineStyleSingle;
            border.LineWidth = Word.WdLineWidth.wdLineWidth150pt;
            border.Color = Word.WdColor.wdColorAutomatic;
 
            border = table.Borders[Word.WdBorderType.wdBorderBottom];
            border.LineStyle = Word.WdLineStyle.wdLineStyleSingle;
            border.LineWidth = Word.WdLineWidth.wdLineWidth150pt;
            border.Color = Word.WdColor.wdColorAutomatic;
 
            border = table.Borders[Word.WdBorderType.wdBorderHorizontal];
            border.LineStyle = Word.WdLineStyle.wdLineStyleSingle;
            border.LineWidth = Word.WdLineWidth.wdLineWidth075pt;
            border.Color = Word.WdColor.wdColorAutomatic;
            
            border = table.Borders[Word.WdBorderType.wdBorderVertical];
            border.LineStyle = Word.WdLineStyle.wdLineStyleSingle;
            border.LineWidth = Word.WdLineWidth.wdLineWidth075pt;
            border.Color = Word.WdColor.wdColorAutomatic;
            
            border = table.Borders[Word.WdBorderType.wdBorderDiagonalDown];
            border.LineStyle = Word.WdLineStyle.wdLineStyleNone;
            border = table.Borders[Word.WdBorderType.wdBorderDiagonalUp];
            border.LineStyle = Word.WdLineStyle.wdLineStyleNone;
            table.Borders.Shadow = false;
            #endregion
 
            oWordApplic.Options.DefaultBorderLineStyle = Word.WdLineStyle.wdLineStyleSingle;
            oWordApplic.Options.DefaultBorderLineWidth = Word.WdLineWidth.wdLineWidth150pt;
            oWordApplic.Options.DefaultBorderColor = Word.WdColor.wdColorAutomatic;
 
            oWordApplic.Selection.Rows.HeadingFormat = (int)Word.WdConstants.wdToggle;
 
            table.Rows.Alignment = Word.WdRowAlignment.wdAlignRowCenter;
        }

但有些地方的Sytle就替換不了了,有一種方法可以間接實現對Style等不能在C#中賦值對象的賦值,那就是通過調用VB.NET的dll。不得不佩服微軟的.NET框架,各個語言間可以隨意的調用,用起來相當之方便。
我們可以在VB.NET下面建個函數來調用那個語句,然後生成dll,C#項目只要引用這個dll,然後調用這個dll中的函數就可以了。下面是VB.NET下對Style賦值的函數(簡單吧!C#裏就是死活也不行)
Public Sub SetStyle()Sub SetStyle(ByVal header As String, ByVal oWordApplic As Word.ApplicationClass)
        oWordApplic.Selection.Style = oWordApplic.ActiveDocument.Styles(header)
    End Sub
其他那些不能在C#裏賦值的對象都可以通過這種方法實現賦值。

2.書籤過多,一個一個定位麻煩的問題
如果你的Word模板夠龐大,可能會出現有50多個書籤,而這些書籤的位置只是填充一些簡單數據的情況。如果我們編程時一個個的定位,然後一個個的填充數據肯定時非常麻煩的。這種情況下,我的解決辦法是設置XML配置文檔。

我們可以設置一個XML文件,其中存放需要填充數據的書籤(這些書籤處只是做簡單的插入文本)的名稱。如下面是我的XML文件的詳細內容: <?xml version="1.0" encoding="utf-8" ?> 
<Forms>
   <Form>
        <Name>data</Name>
        <Bookmarks>
            <Bookmark>date1</Bookmark>
            <Bookmark>date2</Bookmark>
            <Bookmark>project</Bookmark>
            <Bookmark>company</Bookmark>
        </Bookmarks>
   </Form>    
   <Form>
        <Name>company</Name>
        <Bookmarks>
            <Bookmark>recordnumber</Bookmark>
            <Bookmark>customer</Bookmark>
            <Bookmark>address</Bookmark>
            <Bookmark>postcode</Bookmark>
            <Bookmark>linkman</Bookmark>
            <Bookmark>telephone</Bookmark>
            <Bookmark>email</Bookmark>
            <Bookmark>faxnumber</Bookmark>
        </Bookmarks>
   </Form>
</Forms> 我們可以把需要填充的數據都存放到一個Hashtable中,key爲它們所對應的書籤名,這樣在C#中讀取這個XML文件後,把同一Name下的所有Bookmark存放在一個ArrayList中,我們就可以統一地進行填充了,使用的函數的形式如: foreach(string bookmark in bookmarks)
            {
                switch(bookmark)
                {
                    case "company":
                        test.GotoBookMark(bookmark);
                        test.SetFontColor(Word.WdColor.wdColorDarkBlue);
                        test.SetFontName("Times New Roman");
                        test.InsertText(hash[bookmark].ToString());
                        break;
                    case "catalog":
                        break;
                    case "modules":
                        break;
                    default:
                        test.GotoBookMark(bookmark);
                        test.InsertText(hash[bookmark].ToString());
                        break;
                }
            }這樣,對於那些不需要做特殊化處理的書籤,我們就統一的在default中進行了填充數據。

3.頁眉頁腳中添文字的問題
對於頁眉頁腳,需要注意的就是不要用書籤去定位,因爲頁眉頁腳和主文檔的頁面視圖不一樣,所以不能在普通的視圖下直接定位到頁眉頁腳所在書籤處。但只要你切換一下視圖,一切就OK了。 /**//// <summary>
        /// 設置頁眉
        /// </summary>
        /// <param name="strBookmarkName">要設置頁面中的一個書籤</param>
        /// <param name="text">頁眉的文字</param>
        public void SetHeader(string strBookmarkName,string text)
        {
            this.GotoBookMark(strBookmarkName);
            //            If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
            //            ActiveWindow.Panes(2).Close
            //            End If
            if(oWordApplic.ActiveWindow.View.SplitSpecial != Word.WdSpecialPane.wdPaneNone)
                oWordApplic.ActiveWindow.Panes[2].Close();

            //            If ActiveWindow.ActivePane.View.Type = wdNormalView Or ActiveWindow. _
            //            ActivePane.View.Type = wdOutlineView Then
            //            ActiveWindow.ActivePane.View.Type = wdPrintView
            //            End If
            if(oWordApplic.ActiveWindow.View.Type == Word.WdViewType.wdNormalView
                || oWordApplic.ActiveWindow.View.Type == Word.WdViewType.wdOutlineView)
                oWordApplic.ActiveWindow.ActivePane.View.Type = Word.WdViewType.wdPrintView;

            //            ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
            oWordApplic.ActiveWindow.ActivePane.View.SeekView = Word.WdSeekView.wdSeekCurrentPageHeader;

            this.GoToTheEnd();

//            this.GotoBookMark(strBookmarkName);
            this.SetFontColor(Word.WdColor.wdColorDarkBlue);
            this.InsertText(text);

            //            ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument
            oWordApplic.ActiveWindow.ActivePane.View.SeekView = Word.WdSeekView.wdSeekMainDocument;
        }
這裏,我先是通過書籤定位到頁眉所在頁,這樣比較方便,不用在頁眉視圖中再翻頁定位。

4.項目符號及多級項目符號的Style問題
這個問題我的解決中有點問題,要和模板一起設置才能用,感覺不好,就不拿出來給大家看了,以免誤導大家了

5.添加特殊字符的問題
當你要在文檔中添加特殊文字的時候(如你想添加一個þ),不能直接通過簡單的複製粘貼來實現(你根本沒辦法在.NET的IDE下看到þ)。這時,我們可以通過查找這個特殊字符的字體名稱和代表的16進制編碼來插入。我們可以查到þ所在的字符集是Wingdings,它的16進制編碼是/u00fe,這時,我們就能對這個þ進行插入,具體代碼如: test.SetFontName("Wingdings");
test.SetFontColor(Word.WdColor.wdColorDarkBlue);
test.InsertText("/u00fe");其中,test是一個被封裝的Word操作類的實例。

 
發佈了20 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章