從 .NET 開發人員的角度理解 Excel 對象模型 (From MSDN)

從 .NET 開發人員的角度理解 Excel 對象模型

發佈日期 : 10/27/2004 | 更新日期 : 10/27/2004

Ken Getz

MCW Technologies, LLC

適用於:

用於 Microsoft Office 系統的 Microsoft Visual Studio 工具

Microsoft Office Excel 2003

Microsoft Visual Studio .NET 2003

摘要 : 討論了 Microsoft Office Excel 2003 提供的一些對象,並且說明了如何使用它們通過用於 Microsoft Office 系統的 Microsoft Visual Studio 工具來創建託管代碼解決方案。重點主要是 Application、Workbook、Worksheet 和 Range 對象。Visual Basic .NET 和 Visual C# 代碼示例演示了每個對象一些屬性、方法和事件。

Microsoft 下載中心 下載 ExcelObj.exe。

本頁內容

簡介 簡介
Application 對象 Application 對象
Workbook 類 Workbook 類
Worksheet 類 Worksheet 類
Range 對象 Range 對象
下期內容 下期內容

簡介

對 於打算利用用於 Microsoft Office 系統的 Microsoft Visual Studio 工具的開發人員和想要僅僅使用 COM 自動化來控制 Microsoft Office Excel 2003 應用程序的人來說,他們需要能夠與 Excel 對象模型提供的對象進行交互。Excel 提供了數百個您可能想要與之交互的對象,但是您可以通過集中於這些可用的對象的一個非常小的子集來獲得對象模型方面的一個良好的開端。這些對象包括:

  • Application

  • Workbook

  • Worksheet

  • Range

盡 管不可能具體地量化,但是當您使用 Excel 時,大部分的工作將以這四個類以及它們的成員爲核心。在本文檔中,您將學會如何利用這些類中的每一個,另外,我們還會向您介紹每個類的一些屬性、方法和事 件。您也將會看到一些可以嘗試的示例,這些示例演示了每個對象的一些功能。

提示 總的說來,使用 Microsoft Visual Basic .NET 的開發人員在使用 Microsoft Office 對象時,相比於使用 Microsoft Visual C# 的開發人員來說要輕鬆得多,一個重要的原因在於:Visual Basic for Applications (VBA) 方法常包含可選參數,而 Visual Basic .NET 支持可選參數。C# 開發人員將發現他們必須爲每個可選方法參數提供一個值,而 Visual Basic .NET 開發人員可以簡單地使用命名的參數只提供他們需要的值。另外,C# 不支持帶有參數的屬性(除了索引器以外),然而,許多 Excel 屬性可以接受參數。您將會發現,對於 C# 開發人員來說,一些屬性(例如可用於 VBA 和 Visual Basic .NET 的 Application.Range 屬性)需要單獨的訪問器方法(get_Range 方法替換了 Range 屬性)。在本文中,請注意這樣的語言之間存在的差異。

在大多數情況下,您將會發現 Excel 對象模型直接模擬其用戶界面。不難猜想,Application 對象提供了封裝整個應用程序的包裝,並且每個 Workbook 對象都包含 Worksheet 對象的一個集合。其中,表示單元格的主要的抽象是 Range 對象,這使得您能夠使用單個單元格或者單元格組。

下 面的每個部分都將描述一個主要的 Excel 對象,挑選對象的特定成員來進行演示。由於可供研究的對象有數百個,所以不可能在這裏對所有的對象進行深入的探討:您將會得到足夠的對象模型方面的知識來 開始您的工作,並且可以使用 Excel 聯機幫助來獲得更詳細的信息。

提示 在本文中,您將看到 DirectCastCType 方法的許多用途。其原因在於示例項目有自己的 Option Strict 設置 — 這意味着 Visual Basic .NET 需要嚴格的類型轉換。許多 Excel 方法和屬性返回 Object 類型或者依賴於晚期綁定:例如,Application.ActiveSheet 屬性返回 Object ,而不是您猜想的 Worksheet 。因此,爲了儘可能地進行嚴格的類型轉換,示例啓用了 Option Strict ,並且顯式地處理每種類型轉換。(如果不在 Visual Basic .NET 中使用 Option Strict ,您編寫的代碼可能編譯良好,但是會在運行時失敗。這就是 Option Strict 意義所在 — 它大大減少了非法轉換在運行時產生異常的可能性)。如果您是一名正在閱讀本文檔的 C# 開發人員,您可能會讚賞這種決定。

這 本白皮書引用了示例項目 ExcelObjectModel.sln。這個項目包含一個 Excel 工作簿以及相關的 Visual Basic .NET 代碼。並不是本文中展示的每個示例都出現在這個示例項目中,但是需要多於一行或兩行代碼的任何示例都放到了工作簿中,並且在項目內設置了調用代碼的超級鏈 接。

提示 在這篇篇幅有限的文章中,不可能對每個對象或成員進行註解。甚至不可能提及這些類中的一小部分。研究任何大型對象模型最好的工具是 Object Browser 窗口,其中,您可以找到每個類的列表、以及該類的成員。您將會發現,在本文檔中討論的許多類成員適用於許多其他不同的類:例如,在 Sheets 集合的上下文中討論的 PrintOut 方法同樣適用於 ChartWorksheetRange 和其他的對象。本文檔旨在讓您知道什麼是可用的,而剩下的東西要靠您好奇的本性來挖掘了。

Application 對象

Excel Application 對象代表 Excel 應用程序本身。這可能聽起來是顯而易見的,但是 Application 對象公開了大量關於運行時應用程序、應用到該實例的一些選項、以及在該實例內打開的當前用戶對象的信息。Application 對象提供了許多成員,其中的許多成員您從來都不需要研究,但是其他的一些成員對於您的應用程序的行爲是否正確至關緊要。您可以將這些成員分爲以下種類:

  • 在 Excel 中控制狀態和顯示的成員

  • 返回對象的成員

  • 執行操作的成員

  • 處理文件操作的成員

  • 其他成員

下面幾部分介紹了這些組中的每一個、以及演示一些成員的代碼示例。

在 Excel 中控制狀態和顯示的成員

Application 對象提供了一個大的屬性集來控制 Excel 的一般狀態。表 1 列出了與狀態有關的 Application 對象屬性的一個子集。

表 1. 一些控制 Excel 的狀態的 Application 屬性

屬性

類型

說明

Cursor

XlMousePointer (xlDefault, xlIBeam, xlNorthwestArrow, xlWait)

獲取或設置鼠標指針的外觀。

EditDirectlyInCell

布爾值

直接就地獲取或設置編輯單元格的能力。如果爲 False,則您只能在公式欄中編輯單元格。

FixedDecimal

布爾值

如果爲 True,則所有的數字值都使用 FixedDecimalPlaces 屬性來確定小數位數;否則將忽略 FixedDecimalPlaces 屬性(默認值爲 False)。

FixedDecimalPlaces

Long

確定用於數值數據的小數位數(如果 FixedDecimal 屬性爲 True)。

Interactive

布爾值

獲取或設置用戶通過鍵盤和鼠標與 Excel 交互的能力;如果將此屬性設置成 False,則一定要確保在異常處理程序中將其重新設置成 True。Excel 不會自動爲您重新設置它。

MoveAfterReturn

布爾值

如果爲 True,則當您按下 Enter 鍵時,選擇會移到下一個單元格;默認值爲 True。

MoveAfterReturnDirection

xlDirection (xlDown, xlToLeft, xlToRight, xlUp)

指示在按下 Enter 鍵之後移動的方向(如果 MoveAfterReturn 屬性爲 True)。默認值爲 xlDown

ScreenUpdating

布爾值

如果爲 True,Excel 就會在每個方法調用之後更新其屏幕。爲了節省時間並且使您的應用程序看起來更加專業,您可以在代碼運行時關掉顯示。一旦完成,就一定要再次將此屬性值重新設置爲 True。Excel 不會自動爲您重新設置它。

SheetsInNewWorkbook

Long

獲取或設置 Excel 自動放置在新的工作簿中的工作表的數目。

StandardFont

字符串

獲取或設置 Excel 中默認字體的名稱;只有在重新啓動 Excel 之後纔會生效。

StandardFontSize

Long

獲取或設置 Excel 中默認字體的大小;只有在重新啓動 Excel 之後纔會生效。

StartupPath(只讀)

字符串

返回包含 Excel 啓動加載項的文件夾的完整路徑。

TemplatesPath(只讀)

字符串

返回包含模板的文件夾的完整路徑;此值代表着一個 Windows 特殊文件夾。

在表 1 所列出的所有屬性中,您最可能使用的一個屬性是 ScreenUpdating 屬性。通過利用這個屬性,您不但可以使您的 Excel 應用程序看起來更加專業,還可以使它們運行得更快 — 在每次修改後更新顯示會嚴重影響代碼的運行效率,特別是在大範圍中通過編程方式填寫時。然而,重要的是,當您完成您的工作時始終要設置這個屬性,因爲 Excel 不會爲您重新設置它。因此,當使用 ScreenUpdating 屬性時,您將需要始終使用如下代碼片段,並且利用 .NET 異常處理來確保屏幕更新恢復:

' Visual Basic
Try
    ThisApplication.ScreenUpdating = False
    ' Do your work that updates the screen.

Finally
    ThisApplication.ScreenUpdating = True
End Try

// C#
try
{
    ThisApplication.ScreenUpdating = false;
    // Do your work that updates the screen.
}

finally
{
    ThisApplication.ScreenUpdating = true;
}

Application 對象還提供了一組控制 Excel 中的顯示的屬性。您可以修改這些屬性中的任何一個來改變用戶在屏幕上所看到的內容。 2 列出了可用的顯示選項的一個子集。

表 2 一些控制 Excel 的外觀的 Application 屬性

屬性

類型

說明

DisplayAlerts

布爾值

如果爲 True(默認值),則當運行代碼時,只要有必要(例如在刪除一個工作表時),Excel 就會顯示警告消息。設置爲 False 以忽略警告。Excel 的行爲就好像您已經爲每個警告選擇了默認值。

DisplayFormulaBar

布爾值

如果爲 True(默認值),Excel 就會顯示標準公式欄以編輯單元格;設置爲 False 以隱藏公式欄。

DisplayFullScreen

布爾值

如果爲 True,則 Excel 以全屏模式運行(這種模式與僅僅最大化 Excel 窗口的效果不一樣);其默認值爲 False。

提示ScreenUpdating 屬性完全一樣,重新設置 DisplayAlerts 屬性是非常重要的。因爲 Excel 不會爲您重新設置此屬性,並且它設置爲 False,所以在您關閉工作簿之前 Excel 不會提示您保存它們;沒有仔細地重新設置 DisplayAlerts 屬性可能會在您不小心的情況下導致您丟失數據。

返回對象的成員

許多 Application 對象的屬性返回其他的對象。因爲 Visual Studio .NET 提供的標準 Microsoft Office 項目模板只包含 ThisApplicationThisWorkbook 對象,所以您通常需要利用 Application 類的對象成員來引用 Excel 提供的其他對象。您可以使用這些成員通過諸如 ActiveWindow 的屬性檢索對特定子對象的引用,或者通過諸如 Charts 的屬性檢索對一個可用的對象集的引用。 3 列出了 Application 對象的返回對象的屬性的一個子集。

表 3. Application 對象的可用的返回對象的屬性的一個子集。

屬性

類型

說明

ActiveCell

範圍

返回對活動窗口(頂部的窗口)中當前活動單元格的引用。如果沒有活動窗口,此屬性會產生一個錯誤。

ActiveChart

圖表

返回對當前活動的圖表的引用。對於一個嵌入式圖表來說,只有當此圖表被選中或被激活時纔可認爲是活動的。

ActiveSheet

對象

返回對活動工作簿中的活動工作表的引用。

ActiveWindow

窗口

返回對活動窗口(頂部的窗口)的引用;如果沒有活動窗口,則不返回任何結果。

Charts

工作表

返回 Sheet 對象(Chart 和 Worksheet 對象的父對象)的集合,這些對象包含對活動工作簿中的每個圖表的引用。

Selection

對象

返回應用程序中選中的對象。可能是一個 Range、一個 Worksheet 或任何其他的對象 — 同樣適用於 Window 類,在這種情況下,選擇通常是一個 Range 對象。如果當前沒有對象被選中,則不返回任何結果。

Sheets

工作表

返回 Sheet 對象的集合,這些對象包含對活動工作簿中每個工作表的引用。

Workbooks

工作簿

返回 Workbook 對象的集合,這些對象包含對所有打開的工作簿的引用。

您將會最常與 Application 類的 Workbooks 屬性交互。這個屬性使得您能夠循環訪問打開的工作簿、打開或創建一個新的工作簿。下面的部分描述了這個屬性的行爲。

工作簿集合

Workbooks 集合使得有可能使用所有打開的工作簿、創建一個新的工作簿以及將數據導入一個新的工作簿。下表列出了您將發現的 Workbooks 集合的主要用途:

  • 創建一個新的工作簿 使用如下代碼(您也可以指定一個工作簿模板的名稱作爲 Add 方法的一個參數):

    ' Visual Basic
    Dim wb As Excel.Workbook = ThisApplication.Workbooks.Add()
    
    // C#
    Excel.Workbook wb = ThisApplication.Workbooks.Add(Type.Missing);
    
  • 關閉所有打開的工作簿 。與大多數的集合不同,這個集合允許您一次性地關閉所有的成員。下面的方法調用關閉所有打開的工作簿:

    ' Visual Basic
    ThisApplication.Workbooks.Close()
    
    // C#
    ThisApplication.Workbooks.Close();
    
  • 打開一個現有的工作簿 (使用 Workbooks 集合的 Open 方法)。最簡單的形式是使用 Open 方法,如下面的代碼片段所示。Open 方法提供了大量的可選參數,這些參數會影響它在特定環境中的行爲,但是,您通常不需要使用這些可選參數:

    ' Visual Basic
    Dim wb As Excel.Workbook = _
      ThisApplication.Workbooks.Open("C:/YourPath/YourWorkbook.xls")
    
    // C#
    Excel.Workbook wb = ThisApplication.Workbooks.Open( 
        "C://YourPath//Yourworkbook.xls", 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing);
    

    提示 如果您是一名 C# 開發人員,您將習慣於在方法調用中看到對 Type.Missing 值的引用。因爲 Excel 對象模型是以 VBA 爲目標編寫的,所以它的許多方法接受可選參數 — 有時達到 30 個可選參數。您將需要使用 Type.Missing 值的許多實例,或者提供每個參數的特定默認值。

  • 以工作簿的形式打開一個文本文件、數據庫或 XML 文件(使用 OpenTextOpenDatabaseOpenXml )。這些方法提供了極大的靈活性,但即使是對它們進行簡單的介紹,也會佔用比這裏安排的多得多的篇幅。就現在而言,您知道這些方法存在就足夠了 — 如果您需要將這些項中的任何一個加載到 Excel 中,您可以更仔細地研究這些項。您可以使用如下代碼以工作簿的形式加載一個文本文件(使用逗號作爲分隔符,從文本文件中的第三行開始):

    ' Visual Basic
    Dim wb as Excel.Workbook = _
        ThisApplication.Workbooks.OpenText("C:/Test.txt", StartRow:=3, _
        DataType:=xlDelimited, Comma:=True)
    
    // C#
    Excel.Workbook wb = 
        ThisApplication.Workbooks.OpenText("C://Test.txt", 
        Type.Missing, 3, Excel.XlTextParsingType.xlDelimited, 
        Excel.XlTextQualifier.xlTextQualifierDoubleQuote, 
        Type.Missing, Type.Missing, Type.Missing, True, 
        Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing);
    
  • 引用單個工作簿 。您可以使用整數(指示在集合中的位置)或工作簿名作爲 Workbooks 集合中的索引。然而,如果您想要通過名稱引用工作簿,您將需要注意對其進行引用的方法:您必須使用您在標題欄看到的名稱,在保存該文件之前,這個名稱不包括“.xls”擴展名:

    ' Visual Basic
    Dim wb As Excel.Workbook = ThisApplication.Workbooks(1)
    ' Before Book1 is saved:
    wb = ThisApplication.Workbooks("Book1")
    ' After Book1 is saved:
    wb = ThisApplication.Workbooks("Book1.xls")
    
    // C#
    Excel.Workbook wb = ThisApplication.Workbooks[1];
    // Before Book1 is saved:
    wb = ThisApplication.Workbooks["Book1"];
    // After Book1 is saved:
    wb = ThisApplication.Workbooks["Book1.xls"];
    

    提示 當您引用特定的工作簿時,您利用的是默認的索引器(Item 屬性)。除了 Item 屬性之外,Workbooks 集合像 Microsoft Office 提供的所有集合一樣,包括 Count 屬性,Count 屬性返回集合中的項(本例中的 Workbooks )的數目。

執行操作的成員

Application 對象提供了許多允許您執行操作(從重新計算當前數據到撤銷對數據的更改)的方法。下表枚舉了 Application 對象的一些方法,並且使用了一些小例子對每個方法進行了描述。這一部分的示例出現在示例工作簿中的 Application Object 工作表內:

  • Calculate :強制重新計算所有打開的工作簿、特定的工作簿或者特定的範圍:

    ' Visual Basic
    ThisApplication.Calculate
    ' Or...
    ThisWorkbook.Calculate
    ' Or...
    ThisApplication.Range("SomeNamedRange").Calculate
    
    // C#
    ThisApplication.Calculate();
    // Or...
    ThisWorkbook.Calculate();
    // Or...
    ThisApplication.get_Range("A1", "B12").Calculate();
    

    正如示例代碼中所示,RangeWorksheet 對象也提供 Calculate 方法。使用可以將計算範圍限定在您想要重新計算的最小單元格數內的對象的方法。Excel 中的重新計算引擎非常快,但是如果您可以限制所涉及到的單元格數,您就可以優化這一操作。只有在您想重新計算每個打開的工作簿中的每個未決更改時才使用 Application.Calculate

    提示 Visual Basic .NET 和 C# 在處理 Excel 成員時並不完全一樣。例如,Excel、VBA 和 Visual Basic .NET 中的 Range 屬性在 C# 中只能通過 get_Range 方法進行訪問。在本文中,您將會發現此訪問器成員和其他訪問器成員的一些例子。

  • Checkspelling :返回一個 Boolean 來指示提供的參數是否拼寫正確。您可以選擇提供一個自定義字典的名稱和一個 Boolean 來指示您是否想要忽略大小寫。下面的代碼片段檢查您所提供的值的拼寫,並且在工作表上指示其結果:

    ' Visual Basic
    Private Sub TestSpelling()
        Dim rng As Excel.Range = _
          ThisApplication.Range("CheckSpelling")
        Dim strOut As String
    
        If ThisApplication.CheckSpelling( _
          rng.Offset(0, 1).Value.ToString) Then
            strOut = "Spelled correctly"
        Else
            strOut = "Spelled incorrectly"
        End If
        rng.Offset(0, 2).Value = strOut
    End Sub
    
    // C#
    private void TestSpelling()
    {
        // If you specify only a named range in the call 
        // to get_Range, use Type.Missing for the second parameter.
        Excel.Range rng = ThisApplication.
        get_Range("CheckSpelling", Type.Missing);
    
        // Note that C# requires you to retrieve and set
        // the Value2 property of the Range, rather than 
        // the Value property, because the Value property 
        // is parameterized, making it unavailable to C# code:
        rng.get_Offset(0, 2).Value2 = 
            (ThisApplication.CheckSpelling(
        rng.get_Offset(0, 1).Value2.ToString(), 
            Type.Missing, Type.Missing) 
        ? "Spelled correctly" 
        : "Spelled incorrectly");
    }
    

    提示 前面的代碼片段使用了 Range 對象的 Offset 方法,到目前爲止您還都沒有遇到過。在本文後面討論 Range 對象的部分中將對這二者進行討論。Range 類的使用很容易理解:Range 對象代表一個單元格或者一組單元格。在本例中,Range 對象引用已命名範圍 CheckSpellingOffset 屬性從相關 Range 的左上角的指定行數和列數處返回一個 Range 對象,並且允許您處理相對於一個已知位置的單元格。

  • Evaluate :將 Excel 名稱轉換成實際的引用或值。這個方法允許您以字符串的形式創建引用,並且在需要時將其轉換成一個實際對象引用,或者求表達式的值。下面的示例允許您將單元格的地址輸入到示例工作表中,並且該代碼將文本放到您指定地址的單元格中:

    ' Visual Basic
    Private Sub TestEvaluate()
        Dim rng As Excel.Range = _
          ThisApplication.Range("Evaluate")
    
        Try
            Dim rngNew As Excel.Range = _
              ThisApplication.Evaluate( _
              DirectCast(rng.Offset(0, 1).Value), Excel.Range)
            rngNew.Value = "Hello, World!"
        Catch ex As Exception
            MessageBox.Show(ex.Message, ThisApplication.Name)
        End Try
    End Sub
    
    // C#
    private void TestEvaluate()
    {
        Excel.Range rng = ThisApplication.
            get_Range("Evaluate", Type.Missing);
    
        try 
        {
            Excel.Range rngNew = 
                (Excel.Range) ThisApplication.Evaluate(
                rng.get_Offset(0, 1).Value2);
            rngNew.Value2 = "Hello, World!";
        } 
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, ThisApplication.Name);
        }
    }
    
  • MailSystemMailSessionMailLogoffMailLogonSendMail :這些成員允許您登錄您所安裝的電子郵件系統、將當前的工作簿作爲附件發送、以及從您所安裝的電子郵件系統中註銷。MailSystem 屬性指出已安裝的電子郵件系統,而 MailSession 屬性返回對當前電子郵件會話(如果有活動的會話,您就不需要登錄)的引用。下面的示例將示例工作簿作爲一個簡單的電子郵件消息的附件發送:

    ' Visual Basic
    Private Sub TestEmail()
        If ThisApplication.MailSystem = Excel.XlMailSystem.xlMAPI Then
            If ThisApplication.MailSession Is Nothing Then
                Dim frm As New SendMail
                If frm.ShowDialog = DialogResult.OK Then
                  ThisApplication.MailLogon( _
                  frm.EmailName, frm.EmailPassword, frm.DownloadNewMail)
                End If
            End If
            Dim strEmail As String = _
             ThisApplication.Range("SendMail").Offset(0, 1). _
             Value.ToString
            ThisWorkbook.SendMail(strEmail, "Sample Excel Email")
            ThisApplication.MailLogoff()
        Else
            MessageBox.Show( _
             "This demonstration works only if MAPI is installed.")
        End If
    End Sub
    
    // C#
    private void TestEmail()
    {
        if (ThisApplication.MailSystem == 
            Excel.XlMailSystem.xlMAPI ) 
        {
            if ( ThisApplication.MailSession == null ) 
            {
                SendMail frm = new SendMail();
                if (frm.ShowDialog() == DialogResult.OK )
                {
                    ThisApplication.MailLogon(frm.EmailName, 
                        frm.EmailPassword, frm.DownloadNewMail);
                }
            }
            string  strEmail = ThisApplication.
                get_Range("SendMail", Type.Missing).
                get_Offset(0, 1).Value2.ToString();
            ThisWorkbook.SendMail(strEmail, 
                "Sample Excel Email", Type.Missing);
            ThisApplication.MailLogoff();
        } 
        else 
        {
            MessageBox.Show("This demonstration works only if " + 
                "MAPI is installed.");
        }
    }
    

    Workbook 類提供了 SendMail 方法;這是很有意義的,因爲可以通過電子郵件發送的最小粒度的對象就是工作簿本身。您會注意到,SendMail 方法並沒有提供任何將文本附加到您所發送的消息的方法,並且尋址方面也不夠靈活。很明顯,提供這些成員只是使通過電子郵件發送一個工作簿變得容易。如果您 想獲得功能更加齊全的支持,則您需要研究其他與電子郵件交互的方法。此外,如果您當前不在線,並且沒有連接到您的電子郵件系統,則上面的示例代碼將失敗。 如果 MailSession 屬性沒有返回任何結果,則您可以處理這種失敗情況,並且不再嘗試發送郵件。

  • Quit :允許您通過編程方式退出 Excel。如果您將 DisplayAlerts 屬性設置爲 False,則系統不會提示您保存任何未保存的數據。此外,如果您將 WorkbookSaved 屬性設置爲 True,則不管您有沒有進行更改,Excel 都不會提示您保存它:

    ' Visual Basic
    ThisApplication.Quit
    
    // C#
    ThisApplication.Quit();
    
  • Undo :取消用戶在用戶界面內進行的最後一次操作。這個方法不會對代碼進行的操作產生影響,並且只能撤銷單個操作。這項功能並不是十分的強大,但是它確實可以使您在執行您的代碼之前撤銷用戶進行的最後一次操作:

    ' Visual Basic
    ThisApplication.Undo
    
    // C#
    ThisApplication.Undo();
    

處理文件操作的成員

Application 對象提供了幾個成員,通過這些成員,您可以與 Excel 應用程序的上下文內的文件系統交互。下面幾部分描述了您可能會使用到的一些成員。(這部分所描述的示例在示例工作簿的 Application File Handling 工作表中。)

DefaultFilePath 屬性

這個簡單的屬性獲取或者設置 Excel 用於加載和保存文件的路徑:

' Visual Basic
' When the workbook opens:
ThisApplication.Range("DefaultFilePath").Value = _
  ThisApplication.DefaultFilePath

' When you save the DefaultFilePath property:
ThisApplication.DefaultFilePath = _
  ThisApplication.Range("DefaultFilePath"). _
  Value.ToString

// C#
// When the workbook opens:
ThisApplication.get_Range("DefaultFilePath", Type.Missing).
    Value2 = ThisApplication.DefaultFilePath;

// When you save the DefaultFilePath property:
ThisApplication.DefaultFilePath = 
    ThisApplication.get_Range("DefaultFilePath", Type.Missing).
    Value2.ToString();

DefaultSaveFormat 屬性

此屬性獲取或者設置保存工作簿的默認格式。Excel 爲此屬性提供了大量的選項,這些選項都是 XlFileFormat 枚舉的成員。示例工作簿允許您從可選項中進行選擇,如 1 所示。下面的代碼片段演示了示例如何加載和保存此屬性的值。

在這個例子中,示例工作表中的列 E 包含 XlFileFormat 枚舉(在名爲“XlFileFormat ”的範圍中)的所有可能的值的名稱列表,而列 F 包含相應的整數值。 2 展示了這兩個列的一個子集。DefaultSaveFormat 命名範圍(在 1 中)包含對 XlFileFormat 範圍的引用,這使得您可以從一個列表中進行選擇。一旦您選擇保存該值,代碼就必須使用 Range.Find 方法找到您已經選擇的字符串,然後使用 Range.Offset 方法以您找到的值的指定偏移量返回一個值。(有關 Range.Find 方法的詳細信息,請參閱本文後面標題爲“在範圍內搜索”的部分。)最後,代碼將整數值(轉換成適當的枚舉類型)重新保存到 DefaultSaveFormat 屬性。

檢索 DefaultSaveFormat 的當前值非常簡單。下面的代碼將值轉換成文本,並且在示例工作表上的正確 Range 中顯示它:

' Visual Basic
' When the workbook opens, convert the enumerated value 
' into a string:
ThisApplication.Range("DefaultSaveFormat").Value = _
  ThisApplication.DefaultSaveFormat.ToString

// C#
// When the workbook opens, convert the enumerated value 
// into a string:
ThisApplication.get_Range("DefaultSaveFormat", Type.Missing).
    Value2 = ThisApplication.DefaultSaveFormat.ToString();

重新賦選定的值要困難一些。這涉及到三個步驟。代碼必須處理下列任務:

從工作表上的 DefaultSaveFormat 範圍檢索選擇的保存格式的名稱:

' Visual Basic
' Retrieve the name of the new save format, as a string:
Dim strSaveFormat As String = _
  ThisApplication.Range("DefaultSaveFormat"). _
  Value.ToString()

// C#
// Retrieve the name of the new save format,
// as a string:
string strSaveFormat = ThisApplication.
  get_Range("DefaultSaveFormat", Type.Missing).
  Value2.ToString();

在工作表上臨近 XlFileFormat 範圍的列中查找相匹配的整數值(調用 Range 類的 Find 方法)。然後,代碼使用 Range.Offset 屬性來檢索右邊一列的值:

' Visual Basic
Dim intSaveFormat As Integer = _
  CType(ThisApplication.Range("XlFileFormat"). _
  Find(strSaveFormat).Offset(0, 1).Value, Integer)

// C#
Excel.Range rng = ThisApplication.
    get_Range("xlFileFormat", Type.Missing);
Excel.Range rngFind = rng.Find(strSaveFormat, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Excel.XlSearchDirection.xlNext, Type.Missing, Type.Missing, 
    Type.Missing);
// In C#, use the get_Offset method instead of the Offset property:
int intSaveFormat = 
    Convert.ToInt32(rngFind.get_Offset(0, 1).Value2);

將整數值重新賦值給 DefaultSaveFormat 屬性:

' Visual Basic
ThisApplication.DefaultSaveFormat = _
  CType(intSaveFormat, Excel.XlFileFormat)

// C#
ThisApplication.DefaultSaveFormat = 
  Excel.XlFileFormat) intSaveFormat;

1. 從可用類型的列表中選擇一種文件格式。

2. 示例工作表上的 XlFileFormat 範圍的一個子集。

RecentFiles 屬性

RecentFiles 屬性返回一個字符串的集合,包含出現在“文件”菜單的最近使用的文件列表內的所有文件的名稱。這個列表的長度將隨着用戶已經選擇保留的文件的數目的變化而 變化。示例工作簿在其打開時調用這一過程,將最近的文件列表複製到示例工作表上一個名爲 RecentFiles 的範圍中:

' Visual Basic
Private Sub ListRecentFiles()
    Dim i As Integer
    Dim rng As Excel.Range = DirectCast( _
      ThisApplication.Range("RecentFiles"). _
      Cells(1, 1), Excel.Range)
    For i = 1 To ThisApplication.RecentFiles.Count
        rng.Offset(i - 1, 0).Value = _
          ThisApplication.RecentFiles(i).Name
    Next
End Sub

// C#
private void ListRecentFiles()
{
    Excel.Range rng = (Excel.Range)ThisApplication.
        get_Range("RecentFiles", Type.Missing).Cells[1, 1];

    for (int i = 1; i <= ThisApplication.RecentFiles.Count; i++)
    {
        rng.get_Offset(i - 1, 0).Value2 = 
            ThisApplication.RecentFiles[i].Name;
    } 
}

FileDialog 屬性

FileDialog 屬性返回 FileDialog 對象,這個對象處理四種類型的文件操作。由該屬性返回的這個 FileDialog 對象允許您:

  • 選擇一個文件,並且將其打開。

  • 選擇一個文件位置,並且保存當前工作簿。

  • 選擇一個文件夾。

  • 選擇一個文件名。

通過使用這個對話框,您可以利用 Microsoft Office 提供的所有文件處理功能。FileDialog 屬性需要您通過傳遞給它一個 msoFileDialogType 枚舉值(msoFileDialogFilePickermsoFileDialogFolderPickermsoFileDialogOpenmsoFileDialogSaveAs )來選擇對話框的特定使用。然後,您就可以與這個屬性返回的 FileDialog 對象進行交互了。

與許多其他的對象相似,FileDialog 對象是由 Microsoft.Office.Core 命名空間提供的。爲了避免鍵入每個 Office 對象的完整路徑,示例項目使用 Importsusing 語句來導入這個命名空間。本文中的代碼片段還假定您已經將適當的命名空間引用添加到您的文件中了:

' Visual Basic 
Imports Office = Microsoft.Office.Core

// C#
using Office = Microsoft.Office.Core;

FileDialog 對象的 Show 方法顯示對話框,並且返回 -1(如果您按 OK)和 0(如果您按 Cancel)。如果您已經使用了 msoFileDialogOpenmsoFileDialogSaveAs 枚舉值,您可以使用該類的 Execute 方法來實際打開或者保存文件。SelectedItems 屬性包含一個字符串的集合,每個字符串代表一個選擇的文件名。

例如,下面來自示例工作簿的代碼提示您打開一個新的工作簿。這個代碼片段允許多重選擇、清除可用篩選器的列表、添加兩個新的篩選器,然後顯示對話框,如 3 所示。如果您選擇一個文件或多個文件,則代碼會調用 FileDialog 對象的 Execute 方法來打開請求的文件:

' Visual Basic
With ThisApplication.FileDialog( _
  Office.MsoFileDialogType.msoFileDialogOpen)
    .AllowMultiSelect = True
    .Filters.Clear
    .Filters.Add "Excel Files", "*.xls;*.xlw"
    .Filters.Add "All Files", "*.*"
    If .Show <> 0 Then
        .Execute
    End If
End With

// C#
dlg = ThisApplication.get_FileDialog(
    Office.MsoFileDialogType.msoFileDialogOpen);
dlg.Filters.Clear();
dlg.Filters.Add("Excel Files", "*.xls;*.xlw", Type.Missing);
dlg.Filters.Add("All Files", "*.*", Type.Missing);
if(dlg.Show() != 0)
    dlg.Execute();

3. 使用 FileDialog 類來顯示標準 File Open 對話框。

下面來自示例的代碼片段演示了您可以如何使用該對話框來選擇一個文件夾:

' Visual Basic
With ThisApplication.FileDialog( _
  Office.MsoFileDialogType.msoFileDialogFolderPicker)
    If .Show <> 0 Then
        ThisApplication.Range("FolderPickerResults"). _
          Value = .SelectedItems.Item(1)
    End If
End With

// C#
dlg = ThisApplication.get_FileDialog(
    Office.MsoFileDialogType.msoFileDialogFolderPicker);
if (dlg.Show() != 0)
{
    ThisApplication.get_Range("FolderPickerResults", Type.Missing).
        Value2 = dlg.SelectedItems.Item(1);
}

Application 對象還提供了 GetOpenFileNameGetSaveAsFileName 方法,這些方法允許您選擇一個要打開的文件的文件名。儘管您可以使用這些方法,但是您將發現 Microsoft .NET Framework 提供的相對應的 OpenFileDialogSaveFileDialog 控件功能更加豐富,而且更加易於使用。

其他有用的成員

Application 對象提供了一些不適用於其他種類的成員,例如 WorksheetFunction 屬性、Names 集合和 Windows 集合。下面幾部分將描述這些成員。

WorksheetFunction

Application 對象包含一個屬性 WorksheetFunction ,這個屬性返回 WorksheetFunction 類的實例。這個類提供了許多共享/靜態方法,其中的每個方法都包裝了一個 Excel 工作表函數。這些方法中的每一個都公開許多 Excel 電子表格計算函數中的一個,而 VBA 沒有提供這些函數。而且其中的一些成員在 Visual Basic .NET 和 C# 的運算符和方法中已經具備,因此您不大可能會使用這些成員(例如,And 方法)。

您在 WorksheetFunction 類的方法中將會發現大量有趣的和有用的函數,總結在下面的列表中:

  • 數學函數 ,例如 AcosAcoshAsinAsinhCoshDegreesLnLogMedianMaxMinModeRadians 等等。

  • 域函數 ,允許您對範圍執行運算,例如 DAverageDCountDCountADGetDMaxDMinDProductDSum 等等。

  • 邏輯函數 ,例如 IsErrIsErrorIsLogicalIsNAIsNonTextIsNumberIsText

  • 統計函數 ,例如 BetaDistBinomDistChiTestChiInvLogNormDistNegBinomDistPearsonSumProductSumSqTDistTTest , VarVarP 等等。

  • 電子表格函數 ,在 .NET Framework 中,您不大可能會使用這些函數,例如 AndOrChoose 等等。

  • 與泰國有關的函數:您將會發現大量使人莫名其妙的函數,這些函數用於處理泰國數字、日曆和貨幣(謠傳 Excel 小組曾經特別喜歡吃泰國食品,因而添加了這些函數來幫助計算他們在當地泰國餐館的餐費,但是現在看來這個謠言是不真實的),例如 BahtTextIsThaiDigitThaiDayOfWeekThaiDigitThaiMonthOfYearThaiNumSoundThaiNumStringThaiStringLengthThaiYearRoundBahtDownRoundBahtUp

在 Visual Studio .NET 項目中,利用 WorksheetFunction 類非常容易。因爲項目模板爲您提供了 ThisApplication 對象,您可以簡單地引用該對象的 WorksheetFunction 屬性。示例應用程序包含一個名爲 Other Application Members 的工作表,如圖 4 所示,這個示例只測試了這個類的幾個成員。

WorksheetFunction 類及其成員提供了一個好例子,說明了爲什麼從 Visual Basic 中使用 Excel 對象要比從 C# 中使用等效的代碼容易得多。WorksheetFunction 類的許多方法要求 C# 開發人員傳遞 30 個參數,其中的大多數爲空。當然,通過編寫封裝各種不同的方法組(一些具有一個必需的參數,一些具有兩個必需的參數,等等)的包裝無疑可以減輕這種負擔。 出於本文的目的,代碼調用“裸”方法,而不使用包裝方法。當然,C# 代碼很難看。

單擊 Demonstrate WorksheetFunction 鏈接運行下面的代碼(有關 Sort 方法的詳細信息,請參閱“對範圍內的數據進行排序”部分):

' Visual Basic
Private Sub TestWorksheetFunction()
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisWorkbook.ActiveSheet, Excel.Worksheet)
    Dim rng As Excel.Range = ws.Range("RandomNumbers")
    Dim rnd As New System.Random

    Dim i As Integer
    For i = 1 To 20
        ws.Cells(i, 2) = rnd.Next(100)
    Next i
    rng.Sort(rng, _
      Orientation:=Excel.XlSortOrientation.xlSortColumns)

    With ThisApplication.WorksheetFunction
        ws.Range("Min").Value = .Min(rng)
        ws.Range("Max").Value = .Max(rng)
        ws.Range("Median").Value = .Median(rng)
        ws.Range("Average").Value = .Average(rng)
        ws.Range("StDev").Value = .StDev(rng)
    End With
End Sub

// C#
private void TestWorksheetFunction() 
{
    Excel.Worksheet ws = (Excel.Worksheet) ThisWorkbook.ActiveSheet;
    Excel.Range rng = ws.get_Range("RandomNumbers", Type.Missing);
    System.Random rnd = new System.Random();

    for ( int i = 1 ; i <= 20; i++)
        ws.Cells[i, 2] = rnd.Next(100);

    rng.Sort(rng, Excel.XlSortOrder.xlAscending, 
        Type.Missing, Type.Missing, Excel.XlSortOrder.xlAscending, 
        Type.Missing, Excel.XlSortOrder.xlAscending, 
        Excel.XlYesNoGuess.xlNo, Type.Missing,Type.Missing, 
        Excel.XlSortOrientation.xlSortColumns, 
        Excel.XlSortMethod.xlPinYin, 
        Excel.XlSortDataOption.xlSortNormal, 
        Excel.XlSortDataOption.xlSortNormal, 
        Excel.XlSortDataOption.xlSortNormal);

    Excel.WorksheetFunction wsf = ThisApplication.WorksheetFunction;
    ws.get_Range("Min", Type.Missing).Value2 = wsf.Min(rng, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing);
    ws.get_Range("Max", Type.Missing).Value2 = wsf.Max(rng, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing);
    ws.get_Range("Median", Type.Missing).Value2 = wsf.Median(rng,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing);
    ws.get_Range("Average", Type.Missing).Value2 = wsf.Average(rng, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing);
    ws.get_Range("StDev", Type.Missing).Value2 = wsf.StDev(rng, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing);
}

4. 選擇 WorksheetFunction 工作表來檢驗 WorksheetFunction 類及其有用的方法。

正如您在示例代碼中看到的,您可以把 Range 對象作爲參數傳遞給 WorksheetFunction 方法。此外,您也可以將單值或值列表作爲參數進行傳遞。這些方法通常可接受多達 32 個參數,因此,如果您想要計算一個固定的數字列表的平均值,您可以使用如下代碼:

' Visual Basic
dblAverage = ThisApplication.WorksheetFunction.Average( _
 12, 14, 13, 19, 21)

// C#
// Note the number of Type.Missing values--the method accepts
// 30 parameters.
dblAverage = ThisApplication.WorksheetFunction.Average(
    12, 14, 13, 19, 21, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing);

Window 類和 Windows 集合

正如您可能期望的,Application 對象提供了對 Excel 應用程序內顯示的窗口的控制,並且您可以使用 Application 對象的 Windows 屬性來打開、關閉和排列 Excel 對象窗口。

Windows 屬性返回 Window 對象的集合,並且您可以調用 Arrange 方法來排列所有打開的窗口(或者只是可見的窗口)。指定一個 XlArrangeStyle 枚舉值來指示您想要以何種方式排列窗口,並且還可以選擇指定一些關於您是否只想排列可見的窗口、以及您想如何同步窗口滾動的信息。例如,要在 Excel 工作區中平鋪顯示窗口,您可以使用如下代碼:

' Visual Basic
ThisApplication.Windows.Arrange( _
  Excel.XlArrangeStyle.xlArrangeStyleTiled)

// C#
ThisApplication.Windows.Arrange( 
  Excel.XlArrangeStyle.xlArrangeStyleTiled, 
  Type.Missing, Type.Missing, Type.Missing);

如果您想要通過編程方式創建一個新的窗口,您可以調用工作簿的 NewWindow 方法,例如:

' Visual Basic
ThisWorkbook.NewWindow()

// C#
ThisWorkbook.NewWindow();

因爲 NewWindow 方法返回 Window 對象,所以您也可以編寫如下代碼,它設置新窗口的標題,然後並將其激活:

' Visual Basic
With ThisWorkbook.NewWindow()
    .Caption = "New Window"
    .Activate()
End With

// C#
Excel.Window wnd = ThisWorkbook.NewWindow();
wnd.Caption = "New Window";
wnd.Activate();

Windows 類提供控制相關窗口的外觀和行爲的屬性和方法,包括顏色、標題、窗口特性的可視性、以及滾動行爲。您可以編寫如下代碼來使用特定窗口的屬性:

' Visual Basic
With ThisApplication.Windows(3)
    .GridlineColor = ColorTranslator.ToOle(Color.Red)
    .Caption = "A New Window"
    .DisplayHeadings = False
    .DisplayFormulas = False
    .DisplayWorkbookTabs = False
    .SplitColumn = 1
End With

// C#
wnd = ThisApplication.Windows[3];
wnd.GridlineColor = ColorTranslator.ToOle(Color.Red);
wnd.Caption = "A New Window";
wnd.DisplayHeadings = false;
wnd.DisplayFormulas = false;
wnd.DisplayWorkbookTabs = false;
wnd.SplitColumn = 1;

提示 雖然 VBA 和 .NET 都通過相似的範式使用顏色 — 每種都使用三個一組的字節,包含顏色中紅、綠和藍組成部分,編碼成 32 位整數的三個低位字節 — 但是它們處理顏色的方式不同。您可以使用 System.Drawing.ColorTranslator.ToOle 方法從 .NET 顏色轉換到 VBA 所需的 OLE 顏色。

單擊 Other Application Members 工作表上的 Work with Windows 會運行示例程序 TestWindows,它包含這一部分中以小程序塊的形式提供的所有代碼。單擊相同的工作表中的 Reset Windows 會運行下面的過程,它將關閉除了第一個窗口以外的所有窗口,然後把第一個窗口最大化:

' Visual Basic
Private Sub ResetWindows()
    Dim i As Integer
    For i = ThisApplication.Windows.Count To 2 Step -1
        ThisApplication.Windows(i).Close()
    Next
    ThisApplication.Windows(1).WindowState = _
      Excel.XlWindowState.xlMaximized
End Sub

// C#
private void ResetWindows()
{
    for (int i = ThisApplication.Windows.Count; i >= 2; i--)
        ThisApplication.Windows[i].Close(
          false, Type.Missing, Type.Missing);
    ThisApplication.Windows[1].WindowState = 
        Excel.XlWindowState.xlMaximized;
}

Name 類和 Names 集合

Application 對象提供了它的 Names 屬性,這個屬性返回 Name 對象的集合。每個 Name 對象都對應於 Excel 應用程序中的命名範圍。有許多檢索對命名範圍的引用的方法 — 您可以使用 Workbook 對象的 Names 屬性,也可以使用 Worksheet 對象的 Names 屬性。

爲了創建一個新的命名範圍,可以使用 Names 集合的 Add 方法,如下面的代碼片段所示。除了兩個必需的參數之外,Add 方法還接受許多可選的參數:

' Visual Basic
Dim nm As Excel.Name
nm = ThisApplication.Names.Add( _
  "NewName", "='Other Application Members'!$A$6")

// C#
Excel.Name nm;

nm = ThisApplication.Names.Add(
    "NewName", @"='Other Application Members'!$A$6", 
    Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing);

指定名稱和位置(以及其他可選的參數),然後,您就可以在您的代碼中引用該範圍:

' Visual Basic 
ThisApplication.Range("NewName").Value = "Hello, World!"

// C#
ThisApplication.get_Range(
    "NewName", Type.Missing).Value2 = "Hello, World!";

爲了檢索有關命名範圍的信息,您可以使用 Name 類的各種屬性。下面的列表描述了一些最常用的成員:

  • Name 返回分配給命名範圍的名稱。

  • RefersTo 以標準格式 ("=SheetName!$B$25") 返回一個包含實際目標地址的字符串。

  • RefersToR 1C 1 以“R1C1”格式 ("=SheetName!R25C2") 返回目標地址。

  • Value 返回對解析爲範圍的內容的命名範圍的引用。

單擊示例中的 Work with Names 鏈接運行下面的代碼,用關於所有命名範圍的信息填充工作表的一個區域:

' Visual Basic
Dim nm As Excel.Name
Dim rng As Excel.Range = ThisApplication.Range("Names")
Dim i As Integer

For i = 0 To ThisApplication.Names.Count – 1
    nm = ThisApplication.Names.Item(i + 1)
    rng.Offset(i, 0).Value = nm.Name
    ' Without the leading "'", these references
    ' get evaluated, rather than displayed directly.
    rng.Offset(i, 1).Value = "'" & nm.RefersTo.ToString
    rng.Offset(i, 2).Value = "'" & nm.RefersToR1C1.ToString
    rng.Offset(i, 3).Value = nm.Value
Next i

// C#
Excel.Range rng = ThisApplication.get_Range("Names", Type.Missing);
for ( int i = 0 ; i <= ThisApplication.Names.Count - 1; i++)
{
    nm = ThisApplication.Names.Item(i + 1, 
        Type.Missing, Type.Missing);
    rng.get_Offset(i, 0).Value2 = nm.Name;
    // Without the leading "'", these references
    // get evaluated, rather than displayed directly.
    rng.get_Offset(i, 1).Value2 = "'" + nm.RefersTo.ToString();
    rng.get_Offset(i, 2).Value2 = "'" + nm.RefersToR1C1.ToString();
    rng.get_Offset(i, 3).Value2 = nm.Value;
}

Application 事件

除了 Application 類提供的所有其他方法之外,您還將發現有一大組事件可用。雖然不可能以任何一種一致的方式演示所有事件,但是單單根據名稱,就可以比較清楚地知道它們的用途。下面幾部分描述了這些事件的一個子集,並討論了在您自己的應用程序中最可能使用的事件。

提示 傳遞給 Office 應用程序中的事件處理程序的參數會讓人感到與用在本機 .NET 事件中的參數不同。通常,.NET 事件處理程序總是接收 Object 變量(該變量引用引發事件的對象)和第二個參數(該參數從 EventArgs 基類繼承而來,包含關於事件的額外信息)。沒有這樣定義良好的事件設計模式用於 Office 應用程序,因此每個事件處理程序都接受任意數目的參數(由最初的開發人員定義)。

錶行爲

Application 對象提供了各種與表(包括圖表和工作表)相關的事件。下面的列表包含關於許多這樣的事件的信息:

  • 當任何一個表被激活時,SheetActivate 都會發生。Excel 將一個包含對被激活的表的引用的 Object 變量傳遞給事件處理程序。

    提示 正如 Excel 中傳遞一個引用表的 Object 的任何情況一樣,在可以使用這個引用之前,您需要將引用強制轉換成一個正確類型(WorksheetChart ,視具體的情況而定)的變量。然而,如果您已經禁用了 Visual Basic .NET 中的 Option Strict 設置,您可以利用晚期綁定。在您鍵入時,您將仍然不能利用 IntelliSense,這使得編寫代碼變得更加困難。本文檔中所有在 Sheets 集合內使用項的示例都顯式地將結果強制轉換成所需要的特定類型的表(WorksheetChart )。

  • 在 Excel 提供默認的雙擊處理之前,當任何表被雙擊時,SheetBeforeDoubleClick 都會發生。Excel 將下列參數傳遞給事件處理程序:一個包含對錶的引用的 Object 變量、一個包含離雙擊位置最近的單元格的 Range 對象、一個允許您取消默認事件處理的 Boolean 值(默認爲 False)。(此事件沒有出現在圖表中。)

    提示 所有在它們的名稱中包括單詞“Before”的事件都允許您取消默認的事件處理。傳遞給您的事件處理程序的參數通常名爲 Cancle,具有默認值 False。如果將這個參數設置爲 True,Excel 將不會執行事件的默認處理。

  • 在 Excel 提供默認的右鍵單擊處理之前,當任何表被右鍵單擊時,SheetBeforeRightClick 都會發生。Exce 將下列參數傳遞給事件處理程序:一個包含對錶的引用的 Object 變量、一個包含離右擊位置最近的單元格的 Range 對象、一個允許您取消默認事件處理的 Boolean 值(默認爲 False)。(此事件沒有出現在圖表中。)

  • 當任何表被重新計算時,SheetCalculate 都會出現。Excel 將一個包含對重新計算的表的引用的 Object 傳遞給事件處理程序。

  • 當任何工作表中的單元格發生變化(通過用戶或者通過運行代碼)時,SheetChange 都會發生。Excel 將一個 Object 變量(包含對錶的引用)和一個 Range 變量(引用改變的範圍)傳遞給事件處理程序。

  • 當任何表單被停用時(即當它不再有焦點時),SheetDeactivate 都會發生。只有當焦點轉移到同一工作簿內的另一個表時,這個事件處理程序纔會運行。Excel 將一個包含對已經停用的表的引用的 Object 變量傳遞給事件處理程序。

  • 當您單擊任何工作簿內的任何超級鏈接時,SheetFollowHyperlink 都會發生。Excel 將一個引用包含此鏈接的表的 Object 變量和一個包含對您所單擊的鏈接的引用的 Hyperlink 對象傳遞給事件處理程序。(示例項目使用了這個事件,從而在示例內提供了導航。)

  • 當工作表上的選擇改變時,SheetSelectionChange 會發生(該事件沒有出現在圖表中)。Excel 將一個引用選擇發生改變的表的 Object 變量和一個引用新選擇的 Range 變量傳遞給事件處理程序。(注意,在最初的選擇發生改變之前,Excel 沒有傳遞關於最初的選擇的信息。)

    這一部分中的每個事件也可用作 Workbook 類提供的事件。如果該事件是由 Application 對象提供的,則它可以被 Excel 內當前打開的任何一個表引發。當它是由 Workbook 對象提供的,則該事件只有在它影響特定工作簿中的一個表時纔會發生。此外,您還將發現 Worksheet 類提供的相同事件。在這種情況下,事件名不包含單詞“Sheet”(例如,您將會找到 FollowHyperlink 而不是 SheetFollowHyperlink ,等等),並且事件處理程序不傳遞對錶的引用 — 這種信息隱含在接收事件的對象中。另外,事件及其使用方法和參數與此處您所看到的事件相同。

Window 行爲

Application 對象(和相應的 Workbook 對象)提供了各種處理 Window 對象的行爲的事件。下面的列表描述了這些事件:

  • 當任何窗口被激活時,WindowActivate 都會發生。Excel 將下面兩個參數傳遞給事件處理程序:一個是 Workbook 對象,這個對象引用提供窗口的工作簿;一個是引用被選擇的窗口的 Window 對象。與其他激活事件一樣,這個事件也是只有在 Excel 內的焦點移動時才激發。切換到另一個應用程序,然後再回到 Excel 時,不會引發此事件。

  • 當任何窗口被停用時,WindowDeactivate 都會發生。有關更多信息,請參閱 WindowActivate 事件描述。

  • 當任何工作簿窗口重新調整大小時,WindowResize 都會發生。Excel 將一個引用提供窗口的工作簿的 Workbook 對象和一個引用大小重新調整的窗口的 Window 對象傳遞給事件處理程序。

    Workbook 類提供的事件中,事件處理程序不會接收對 Workbook 的引用 — 這種信息隱含在引發此事件的對象中。

Workbook 管理

Application 對象提供了各種當您與任何 Workbook 對象交互時都會發生的事件。這些事件過程中的每一個都接收 Workbook 變量,該變量指示參與事件的特定工作簿。下面的列表描述了可用事件的一個子集:

  • 當創建一個新的工作簿時,NewWorkbook 會發生。Excel 將一個引用新的工作簿的 Workbook 變量傳遞給事件處理程序。(此事件只由 Application 類提供。)

  • 當任何工作簿被激活時,WorkbookActivate 都會發生。Excel 將一個引用被激活的工作簿的 Workbook 變量傳遞給事件處理程序。(與其他的“激活”事件一樣,只有在您從一個工作簿切換到另一個工作簿時這個事件才發生。)

  • 當一個打開的工作簿剛好在默認事件處理之前關閉時,WorkbookBeforeClose 會發生。Excel 將一個引用將要關閉的工作簿的 Workbook 變量以及一個允許事件處理程序取消默認事件處理(即保持工作簿打開)的 Boolean 值(默認爲 False)傳遞給事件處理程序。

    警告 如果您草率地將 Cancel 參數設置爲 True,而不考慮任何條件,則所有的工作簿將永遠不會被關閉。

  • 當工作簿內的打印剛好在默認事件處理之前開始時,WorkbookBeforePrint 會發生。Excel 將一個引用包含打印內容的工作簿的 Workbook 變量以及一個允許事件處理程序取消默認事件處理(即跳過請求的打印)的 Boolean 值(默認爲 False)傳遞給事件處理程序。

  • 當剛好在默認事件處理之前保存工作簿時,WorkbookBeforeSave 會發生。Excel 將一個引用保存的工作簿的 Workbook 變量以及一個允許事件處理程序取消默認事件處理(即取消保存)的 Boolean 值(默認爲 False)傳遞給事件處理程序。

  • 當任何工作簿被停用時,WorkbookDeactivate 都會發生。Excel 將一個引用已經停用的工作簿的 Workbook 變量傳遞給事件處理程序。(與其他的“激活”事件一樣,這個事件只有在您從一個工作簿切換到另一個工作簿時纔會發生。)

  • 當將新的表添加到工作簿時,WorkbookNewSheet 會發生。Excel 將一個引用工作簿的 Workbook 變量和一個引用新表的 Object 變量傳遞給事件處理程序。

  • 當一個工作簿打開時,WorkbookOpen 會發生。Excel 將一個引用新打開的工作簿的 Workbook 變量傳遞給事件處理程序。

    Workbook 類提供了自己的一組事件,與您在此處看到的事件非常相似。所有以“Workbook ”開頭的事件在沒有該委託(“Activate ”而不是“WorkbookActivate ”,等等)的情況下出現在 Workbook 類的事件列表中。Workbook 類事件處理程序不接收 Workbook 變量作爲參數;該信息隱含在引發這個事件的對象中。此外,Workbook 類還提供了其他 Application 對象事件的鏡像,但是隻爲單個工作簿捕獲它們,這與爲所有的工作簿捕獲這些事件形成了對比。本文檔的剩餘部分將不討論事件,因爲您現在已經瞭解了一些您最有可能會使用的事件。

Workbook 類

正如您可能想象到的那樣,Workbook 類代表了 Excel 應用程序內的一個單一的工作簿。在這一部分,您將會了解這個類的一些成員,包括那些最常使用的屬性和方法。

提示 許多 Application 類的成員也作爲 Workbook 類的成員加以介紹。在這種情況下,其屬性適用於特定的工作簿,而不適用於活動工作簿。這一部分所要討論的成員遠比上一部分中討論的少,主要因爲您對許多提到的成員已經有所瞭解。

Workbook 類的屬性

Workbook 類提供了大量的屬性(大約 90 個),並且有許多屬性處理多數開發人員從不會考慮到的特殊情況;例如,AutoUpdateFrequency 屬性返回共享工作簿的自動更新的分鐘數;如果工作簿使用 1904 日期系統(一種日期順序方案,它將 1904 年 1 月 2 日作爲對應於值 1 的日期,通常使用於 Macintosh 計算機),Date1904 屬性會返回 True 值;PasswordEncryptionAlgorithm 屬性可以讓您設置用於加密密碼的確切算法,等等。

這一部分只是介紹您最可能用到的 Workbook 對象屬性,而不是試圖全面介紹其衆多屬性。通常的規則是:如果您需要工作簿的某一行爲,而其他人可能已經請求該行爲,實際上最可能的情況是一個屬性允許該行爲,而通常由一個方法提供該行爲。在您向一個工作簿中添加自己的代碼之前要仔細檢查文檔。

以下列表描述了一些最常使用的 Workbook 屬性:

  • NameFullNamePath (字符串,只讀):這些屬性分別返回不同版本的工作簿名稱。FullName 返回完整路徑名稱,包括工作簿文件名。Name 只是返回名稱部分,而 Path 則只返回路徑部分。單擊示例工作簿中的 Name Information 鏈接來運行以下代碼,並返回信息,如 5 所示:

    ' Visual Basic
    ThisApplication.Range("WorkbookName").Value = _
      ThisWorkbook.Name
    ThisApplication.Range("WorkbookPath").Value = _
      ThisWorkbook.Path
    ThisApplication.Range("WorkbookFullName").Value = _
      ThisWorkbook.FullName
    
    // C#
    ThisApplication.get_Range("WorkbookName", Type.Missing).
        Value2 = ThisWorkbook.Name;
    ThisApplication.get_Range("WorkbookPath", Type.Missing).
        Value2 = ThisWorkbook.Path;
    ThisApplication.get_Range("WorkbookFullName", Type.Missing).
        Value2 = ThisWorkbook.FullName;
    

    5. 使用 Workbook 屬性檢索名稱的有關信息。
  • Password (字符串):獲取或者設置和工作簿相關的密碼。如果您指定了一個非空的密碼,工作簿的 HasPassword 屬性也會返回 True。您可以檢索 Password 屬性,但是它的值總是“********”。單擊示例工作簿中的 Set Password 鏈接來運行以下代碼,它可以根據您是提供文本還是隻提供空字符串來設置或清除工作簿密碼。這個示例使用示例項目中名爲 Password 的窗體,它提供一個文本框和一個 Password 屬性:

    ' Visual Basic
    Private Sub SetPassword()
        Dim frm As New Password
    
        If frm.ShowDialog = DialogResult.OK Then
            ThisWorkbook.Password = frm.Password
        End If
        frm.Dispose()
    End Sub
    
    // C#
    private void SetPassword()
    {
        Password frm = new Password();
    
        if (frm.ShowDialog() == DialogResult.OK)
            ThisWorkbook.Password = frm.Value;
        frm.Dispose();
    }
    
  • PrecisionAsDisplayed (布爾值):如果爲 True,則 Excel使用以十進制顯示的數字進行計算。如果爲 False(默認值),則 Excel 使用所有可用的十進制數進行計算,甚至包括那些根本沒有顯示的部分。 6 顯示此屬性設置成 True 的示例工作簿。第 C 列中的每個值都是第 B 列中值的副本,但是第 C 列中的數字格式已經設置成了只顯示兩個十進制位。要注意到,如果將 PrecisionAsDisplayed 屬性設置成 True,求和就會不一樣,因爲經過四捨五入後實際值會不同。。如果您單擊 PrecisionAsDisplayed = False 鏈接,求和又一樣了。單擊會調用以下過程,傳遞 True 或 False 值(取決於您所單擊的鏈接):

    ' Visual Basic
    Private Sub TestPrecisionAsDisplayed( _
      ByVal IsPrecisionAsDisplayedOn As Boolean)
        ThisWorkbook.PrecisionAsDisplayed = IsPrecisionAsDisplayedOn
    End Sub
    
    // C#
    private void TestPrecisionAsDisplayed(
        bool IsPrecisionAsDisplayedOn)
    {
        ThisWorkbook.PrecisionAsDisplayed = 
            IsPrecisionAsDisplayedOn;
    }
    

    6. 將PrecisionAsDisplayed 屬性設置成 True,Excel 只使用顯示的十進制數進行計算。
  • ReadOnly (布爾值,只讀):如果工作簿以只讀的方式打開,則此屬性返回 True 值。此時如果您無法將數據保存到工作簿,那麼您可以在應用程序中採取其他不同操作。

  • Saved (布爾值):用來獲取或設置工作簿的保存狀態。如果用戶已經對工作簿的內容或結構進行了修改,則 Saved 屬性就爲 True。如果試圖關閉工作簿或者退出 Excel,將會出現一個警報提示您保存工作簿(除非您已經將 Application.DisplayAlerts 屬性設置成 False)。如果您在代碼中將 Saved 屬性值設置成 False,Excel 就會認爲您的工作簿已經保存,並且不會再次提醒您保存。

使用 Document 屬性

正如其他的 Office 應用程序一樣,Excel 允許您在保存工作簿的同時保存文檔屬性。Excel 提供了許多內置屬性,並且您也可以添加自己的屬性。選擇“文件|屬性”來顯示如圖 7 所示的對話框,並且您也可以選擇“自定義”選項卡來創建和修改自定義屬性。

7. 使用此對話框設置文檔屬性。

通過 Workbook 類的 BuiltInDocumentProperties 屬性來使用內置屬性,並通過 CustomDocumentProperties 屬性來使用自定義屬性。這些屬性都返回一個 DocumentProperties 對象,它是 DocumentProperty 對象的一個集合。通過集合內的名稱或者索引可以使用集合的 Item 屬性來檢索特定的屬性。在 Excel 文檔中有全部的屬性名列表,但是有一個檢索列表的簡單方法:當您單擊示例工作簿中的 Document Properties 鏈接時會運行下面的過程(參見 圖 8)。該過程調用 DumpPropertyCollection 方法列出所有內置屬性和它們的當前值,然後對自定義屬性也重複進行這一過程。此外,該過程還單獨修改 Revision Number 屬性,並且創建一個新的自定義屬性:

' Visual Basic
Private Sub DisplayDocumentProperties()
    Dim prp As Office.DocumentProperty
    Dim prps As Office.DocumentProperties

    Dim rng As Excel.Range = _
      ThisApplication.Range("DocumentProperties")
    Dim i As Integer

    Try
        ThisApplication.ScreenUpdating = False

        Try
            prps = DirectCast( _
              ThisWorkbook.BuiltinDocumentProperties, _
              Office.DocumentProperties)

            ' Set the Revision Number property:
            prp = prps.Item("Revision Number")
            prp.Value = CType(prp.Value, Integer) + 1

            ' Dump contents of the collection:
            DumpPropertyCollection(prps, rng, i)

        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try

        ' Work with custom properties:
        Try
            prps = DirectCast( _
              ThisWorkbook.CustomDocumentProperties, _
             Office.DocumentProperties)
            DumpPropertyCollection(prps, rng, i)
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try

        ' Add a custom property:
        Try
            ' Delete the property, if it exists.
            prp = prps.Item("Project Name")
            prp.Delete()
        Catch
            ' Do nothing if you get an exception.
        End Try

        Try
            ' Add a new property.
            prp = prps.Add("Project Name", False, _
             Office.MsoDocProperties.msoPropertyTypeString, _
              "White Papers")
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    Finally
        ThisApplication.ScreenUpdating = True
    End Try
End Sub

Private Sub DumpPropertyCollection( _
  ByVal prps As Office.DocumentProperties, _
  ByVal rng As Excel.Range, ByRef i As Integer)
    Dim prp As Office.DocumentProperty

    For Each prp In prps
        rng.Offset(i, 0).Value = prp.Name
        Try
            If Not prp.Value Is Nothing Then
                rng.Offset(i, 1).Value = _
                   prp.Value.ToString
            End If
        Catch
            ' Do nothing at all.
        End Try
        i += 1
    Next
End Sub

// C#
private void DisplayDocumentProperties()
{
    Office.DocumentProperty prp = null;
    Office.DocumentProperties prps = 
        (Office.DocumentProperties) 
        ThisWorkbook.BuiltinDocumentProperties;

    Excel.Range rng = ThisApplication.
        get_Range("DocumentProperties", Type.Missing);
    int i = 0;

    try 
    {
        ThisApplication.ScreenUpdating = false;

        try 
        {
            // Set the Revision Number property:
            prp = prps["Revision Number"];
      prp.Value = Convert.ToInt32(prp.Value) + 1;

            // Dump contents of the collection:
            i = DumpPropertyCollection(prps, rng, i);
        } 
        catch (Exception ex) 
        {
            MessageBox.Show(ex.Message, ThisApplication.Name);
        }

        // Work with custom properties:
        try 
        {
      prps = (Office.DocumentProperties)
        ThisWorkbook.CustomDocumentProperties;
            DumpPropertyCollection(prps, rng, i);
        } 
        catch (Exception ex) 
        {
            MessageBox.Show(ex.Message, ThisApplication.Name);
        }

        // Add a custom property:
        try 
        {
            // Delete the property, if it exists.
            prp = prps["Project Name"];
            prp.Delete();
        } 
        catch
        {
            // Do nothing if you get an exception.
        }
        try 
        {
            // Add a new property.
            prp = prps.Add("Project Name", false, 
                Office.MsoDocProperties.msoPropertyTypeString,         
                "White Papers", Type.Missing);
        } 
        catch (Exception ex) 
        {
            MessageBox.Show(ex.Message, ThisApplication.Name);
        }
    } 
    finally 
    {
        ThisApplication.ScreenUpdating = true;
    }
}

private int DumpPropertyCollection(
    Office.DocumentProperties prps, Excel.Range rng, int i) 
{
    foreach (Office.DocumentProperty prp in prps)
    {
        rng.get_Offset(i, 0).Value2 = prp.Name;
        try 
        {
            if (prp.Value != null ) 
            {
                rng.get_Offset(i, 1).Value2 = 
                    prp.Value.ToString();
            }
        } 
        catch
        {
            // Do nothing at all.
        }
        i += 1;
    }
    return i;
}

提示 前面的代碼示例 DisplayDocumentProperties 使用了 Microsoft.Office.Core 程序集中的幾個枚舉和類型。示例代碼包含一個 Imports/using 語句,它將文本“Office”設置成這個命名空間的縮寫,就像設置“Excel”縮寫一樣。項目模板會自動設置“Excel”縮寫。而您需要自己添加 “Office”語句。

8 . 內置的文檔屬性

儘管在這裏您使用的是 Excel 及其對象,但是實際上是 Office 提供了可用的內置文檔屬性列表,並且 Excel 沒必要實現所有的屬性 — 如果試圖訪問一個未定義屬性的 Value 屬性,就會觸發一個異常。示例過程包含應付這種情況(如果會出現的話)的簡單異常處理。

使用樣式

和 Word 文檔很相似,Excel 工作簿允許您將指定的樣式應用於工作簿內的區域,並且 Excel 提供了許多預定義的(儘管並不非常引人注目)樣式。使用“格式|樣式”菜單項,會顯示一個對話框,它允許您交互式地修改樣式,如 9 所示。

9. 利用此對話框交互式地修改樣式。

單擊“樣式”對話框中的 Modify 會顯示“單元格格式”對話框,如圖 10 所示。

10. 使用“單元格格式”對話框修改樣式。

“單元格格式”對話框顯示了您在格式化單元格時可以使用的所有選項,該對話框中可用的所有選項同樣可以在代碼中使用。您可以使用 Workbook 對象的 Styles 屬性來與工作簿交互,並對工作簿內的範圍應用樣式。

通過使用 Workbook 對象的 Styles 屬性,您可以創建、刪除和修改樣式。單擊示例工作簿中的 Apply Style 來運行以下過程,它創建了一個新的樣式(如果您已經運行了這段代碼,則使用已有的樣式),設置該樣式的各個方面,並將其應用到一個區域:

' Visual Basic
Private Sub ApplyStyle()
    Const STYLE_NAME As String = "PropertyBorder"
    Dim rng As Excel.Range
    ' Get the range containing all the document properties.
    rng = GetDocPropRange()

    Dim sty As Excel.Style
    Try
        sty = ThisWorkbook.Styles(STYLE_NAME)
    Catch
        sty = ThisWorkbook.Styles.Add(STYLE_NAME)
    End Try

    sty.Font.Name = "Verdana"
    sty.Font.Size = 12
    sty.Font.Color = ColorTranslator.ToOle(Color.Blue)
    sty.Interior.Color = ColorTranslator.ToOle(Color.LightGray)
    sty.Interior.Pattern = XlPattern.xlPatternSolid
    rng.Style = STYLE_NAME

    rng.Columns.AutoFit()
End Sub

// C#
private void ApplyStyle()
{
    const String STYLE_NAME = "PropertyBorder";
    // Get the range containing all the document properties.
    Excel.Range rng = GetDocPropRange();
    Excel.Style sty;
    try
    {
        sty = ThisWorkbook.Styles[STYLE_NAME];
    }
    catch
    {
        sty = ThisWorkbook.Styles.Add(STYLE_NAME, Type.Missing);
    }

    sty.Font.Name = "Verdana";
    sty.Font.Size = 12;
    sty.Font.Color = ColorTranslator.ToOle(Color.Blue);
    sty.Interior.Color = ColorTranslator.ToOle(Color.LightGray);
    sty.Interior.Pattern = Excel.XlPattern.xlPatternSolid;
    rng.Style = STYLE_NAME;
    rng.Columns.AutoFit();
}

GetDocPropRange 方法返回一個由文檔屬性填充的範圍。這個過程使用 Range.End 方法來查找由文檔屬性填充的範圍的結尾,並且基於這個範圍的左上角和右下角創建一個新的範圍:

' Visual Basic
Private Function GetDocPropRange() As Excel.Range
    Dim rng As Excel.Range = _
      ThisApplication.Range("DocumentProperties")

    Dim rngStart As Excel.Range = _
      DirectCast(rng.Cells(1, 1), Excel.Range)
    Dim rngEnd As Excel.Range = _
      rng.End(Excel.XlDirection.xlDown).Offset(0, 1)

    Return ThisApplication.Range(rngStart, rngEnd)
End Function

// C#
private Excel.Range GetDocPropRange()
{
    Excel.Range rng = 
        ThisApplication.get_Range("DocumentProperties", Type.Missing);
    Excel.Range rngStart = 
        (Excel.Range) rng.Cells[1, 1];
    Excel.Range rngEnd = 
        rng.get_End(Excel.XlDirection.xlDown).get_Offset(0, 1);
    return ThisApplication.get_Range(rngStart, rngEnd);
}

提示 要想知道關於檢索和使用 Range 對象的更多信息,請參看本文後面標題爲“使用 Range”的章節。

一旦您運行了這段代碼,在示例工作簿中包含文檔屬性的區域會改變底紋和字體,如 11 所示。

11. 應用自定義樣式之後

單擊 Clear Style 運行以下過程,它清除同一區域的樣式:

' Visual Basic
Private Sub ClearStyle()
    ' Get the range containing all the document properties, and
    ' clear the style.
    GetDocPropRange().Style = "Normal"
End Sub

// C#
private void ClearStyle()
{
    // Get the range containing all the document properties, and
    // clear the style.
    GetDocPropRange().Style = "Normal";
}

使用表

Workbook 類提供了一個 Sheets 屬性,它返回一個 Sheets 對象。這個對象包含 Sheet 對象集合,其中每個對象既可以是 Worksheet 對象,也可以是 Chart 對象。單擊示例工作簿的 List Sheets 鏈接來運行下面的過程,它列出工作簿中的所有現有的表:

' Visual Basic
Private Sub ListSheets()
    Dim sh As Excel.Worksheet
    Dim rng As Excel.Range
    Dim i As Integer

    rng = ThisApplication.Range("Sheets")
    For Each sh In ThisWorkbook.Sheets
        rng.Offset(i, 0).Value = sh.Name
        i = i + 1
    Next sh
End Sub

// C#
private void ListSheets()
{
    int i = 0;

    Excel.Range rng = 
        ThisApplication.get_Range("Sheets", Type.Missing);
    foreach (Excel.Worksheet sh in ThisWorkbook.Sheets)
    {
        rng.get_Offset(i, 0).Value2 = sh.Name;
        i = i + 1;
    }
}

您可能還會發現下面的 Sheets 類的成員會很有用。

  • Visible 屬性可以讓您在不刪除和重建表的情況下顯示或隱藏一個現有的表。可將 Visibility 屬性設置成 XlSheetVisibility 枚舉值(XlSheetHiddenXlSheetVeryHiddenxlSheetVisible )中的一個值。使用 XlSheetHidden 可以讓用戶通過 Excel 界面隱藏表;使用 XlSheetVeryHidden ,則要求您運行代碼來取消隱藏表:

    ' Visual Basic
    DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet).Visible = _
      Excel.XlSheetVisibility.xlSheetVeryHidden
    
    // C#
    ((Excel.Worksheet) ThisWorkbook.Sheets[1]).Visible = 
        Excel.XlSheetVisibility.xlSheetVeryHidden;
    
  • Add 方法允許您將一個新表添加到工作簿中的表集合中,並且可以接受四個可選參數,這些參數可以指明表的位置、要添加的表數和表的類型(工作表、圖表等):

    ' Visual Basic
    Dim sh As Excel.Sheet = ThisWorkbook.Sheets.Add()
    
    // C#
    Excel.Sheet sh = ThisWorkbook.Sheets.Add(
        Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    
  • Copy 方法創建一個表的副本,並且將表插入到指定的位置。您可以隨意指定將新表插入到現有表的前面或者後面。如果您不指定位置,Excel 會創建一個新的工作簿來存放這個新表。下面的代碼片段複製當前工作簿中的第一個表,並且將副本放在第三個表的後面:

    ' Visual Basic
    DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _
      Copy(After:=ThisWorkbook.Sheets((3)))
    
    // C#
    ((Excel.Worksheet) ThisWorkbook.Sheets[1]).
        Copy(Type.Missing, ThisWorkbook.Sheets[3]);.
    
  • Delete 方法刪除一個指定的表:

    ' Visual Basic
    DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet).Delete
    
    // C#
    ((Excel.Worksheet) ThisWorkbook.Sheets[1]).Delete();
    
  • FillAcrossSheets 方法將工作簿內一個表的範圍中的數據複製到所有其他表。您需要指定一個範圍,以及是否要複製數據、進行格式化,或全部,剩下的工作 Excel 會去處理。下面的代碼片段在工作簿中,將一個表中名稱爲 Data 的範圍的數據和複製到所有表的同一區域中,並對這些數據進行格式化:

    ' Visual Basic
    ThisWorkbook.Sheets.FillAcrossSheets( _
      ThisApplication.Range("Data"), Excel.XlFillWith.xlFillWithAll)
    
    // C#
    ThisWorkbook.Sheets.FillAcrossSheets(
        ThisApplication.get_Range("Data", Type.Missing), 
        Excel.XlFillWith.xlFillWithAll);
    
  • Move 方法和 Copy 方法很類似,只不過最終您得到的是表的一個實例。您可以指定將表放到您的表的前面,或者放到後面(但不可以同時指定)。同樣的,如果您沒有爲移動的表指定一個位置,Excel 會創建一個新的工作簿來包含它。下面的代碼片段將第一個工作表移到最後:

    ' Visual Basic
    Dim shts As Excel.Sheets = ThisWorkbook.Sheets
    DirectCast(shts(1), Excel.Worksheet).Move(After:=shts(shts.Count))
    
    // C#
    Excel.Sheets shts = ThisWorkbook.Sheets;
    ((Excel.Worksheet)shts[1]).Move(Type.Missing, shts[shts.Count]);
    

    提示 如果出於某種原因您要對工作簿中的表列表進行排序,您可以使用 Move 方法來進行一個低效的冒泡排序。當然,您不可能有太多的表,所以排序速度不是個問題。

  • PrintOut 方法允許您打印選擇的對象(這個方法適用於多個不同的對象)。您可以指定許多可選的參數,包括:要打印的頁數(起始頁和終止頁)、副本數量、打印前是否進 行預覽、要使用的打印機的名稱、是否打印到一個文件、是否進行逐份打印以及您要打印到的文件名。下面的例子使用默認的打印機打印指定的表、只打印第一頁、 打印兩份副本,並且在打印前預覽文檔:

    ' Visual Basic
    DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _
      PrintOut(From:=1, To:=1, Copies:=2, Preview:=True)
    
    // C#
    ((Excel.Worksheet)ThisApplication.Sheets[1]).
        PrintOut(1, 1, 2, true, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing);
    
  • PrintPreview 方法允許您在 PrintPreview 窗口顯示指定的對象,並且可以選擇禁止更改頁面佈局:

    ' Visual Basic
    DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _
      PrintPreview(False)
    
    // C#
    ((Excel.Worksheet)ThisApplication.Sheets[1]).PrintPreview(false);
    
  • Select 方法選擇指定的對象,並且改變用戶的選擇(可使用 Activate 方法使對象獲得焦點,而不需改變用戶的選擇。)您可以有選擇地提供一個被當前選擇取代的對象的引用。下面的代碼片段選擇第一個工作表:

    ' Visual Basic
    ActiveWorkbook.Sheets(1).Select()
    
    // C#
    ((Excel.Worksheet)ThisApplication.Sheets[1]).Select(Type.Missing);
    

    提示 在這一部分中列出的許多方法也適用於其他的類。例如,PrintOut 方法是由以下類提供的:ChartChartsRangeSheetsWindowWorkbookWorksheetWorksheets 。這些方法的具體使用是相同的,只不過是作用在不同的對象上而已。Select 方法幾乎適用於任何一種可選擇的對象(並且這樣的對象有很多)。

Workbook 類的方法

Workbook 類提供了大量的方法,其中有許多方法處理一些非常特殊的情況。這一部分探討一些在每個應用程序中您都可能會用到的方法,而不是詳細介紹各種方法,並將一些難理解的方法放在後面的章節中介紹,下面的列表描述了一些您最可能使用的方法:

  • Activate 方法激活一個工作簿,並且選擇工作簿中的第一個工作表:

    ' Visual Basic
    ThisApplication.Workbooks(1).Activate
    
    // C#
    ThisApplication.Workbooks[1].Activate;
    
  • Close 方法關閉一個指定的工作簿,並且(可選)指定是否保存修改。如果工作簿從未保存過,則可以指定一個文件名。此外,如果您要將工作簿發送給其他用戶,您可以指定您是否想將工作簿發送給下一個用戶。下面的代碼片段關閉工作簿,並且不保存修改:

    ' Visual Basic
    ThisApplication.Workbooks(1).Close(SaveChanges:=False)
    
    // C#
    ThisApplication.Workbooks(1).Close(false, 
      Type.Missing, Type.Missing);
    
  • ProtectUnprotect 方法允許您保護一個工作簿,從而不能添加或者刪除工作表,以及再次取消保護工作簿。您可以指定一個密碼(可選),並且指明是否保護工作簿的結構(這樣用戶 就不能移動工作表)以及工作簿的窗口(可選)。保護工作簿用戶仍可以編輯單元格。要想保護數據,您必須保護工作表。調用 Unprotect 方法(如果需要,還要傳遞一個密碼)可以取消保護工作簿。以下示例假定一個名稱爲 GetPasswordFromUser 的過程,它要求用戶輸入密碼,並且會返回輸入的值:

    ' Visual Basic
    ThisApplication.Workbooks(1).Protect(GetPasswordFromUser())
    
    // C#
    ThisApplication.Workbooks[1].Protect(
      GetPasswordFromUser(), Type.Missing, Type.Missing);
    
  • 正如您所認爲的,Save 方法保存工作簿。如果您還未保存過工作簿,則應該調用 SaveAs 方法,這樣您可以指定一個路徑(如果還未保存過工作簿,Excel 會將其保存在當前文件夾中,並以創建工作簿時所給的名稱命名):

    ' Visual Basic
    ' Save all open workbooks.
    Dim wb As Excel.Workbook
    For Each wb in ThisApplication.Workbooks
        wb.Save
    Next wb
    
    // C#
    // Save all open workbooks.
    foreach (Excel.Workbook wb in ThisApplication.Workbooks)
    {
        wb.Save();
    } 
    
  • SaveAs 方法要比 Save 方法複雜的多。這個方法允許您保存指定的工作簿,並且指定名稱、文件格式、密碼、訪問模式和其他更多的選項(可選)。查看聯機幫助可以獲得所有選項列表。下面的代碼片段將當前工作簿保存到一個指定位置,並且存成 XML 格式:

    ' Visual Basic
    ThisApplication.ActiveWorkbook.SaveAs("C:/MyWorkbook.xml", _
     FileFormat:=Excel.XlFileFormat.xlXMLSpreadsheet)
    
    // C#
    ThisApplication.ActiveWorkbook.SaveAs("C://MyWorkbook.xml", 
        Excel.XlFileFormat.xlXMLSpreadsheet, Type.Missing, 
      Type.Missing, Type.Missing, Type.Missing, 
      Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    

    提示 由於保存成某些格式需要一些交互,您也許想在調用 SaveAs 方法之前將 Application.DisplayAlerts 屬性設置成 False。例如,在將一個工作表保存成 XML 格式時,Excel 會提醒您不能隨工作簿保存 VBA 項目。如果將 DisplayAlerts 屬性設置成 False,就不會出現這種警告。

  • SaveCopyAs 方法將工作簿的一個副本保存到文件中,但不會修改在內存中打開的工作簿。當您想創建工作簿的備份,同時不修改工作簿的位置時,這個方法非常有用:

    ' Visual Basic
    ThisApplication.ActiveWorkbook.SaveCopyAs("C:/Test.xls")
    
    // C#
    ThisApplication.ActiveWorkbook.SaveCopyAs("C://Test.xls");
    

    警告 交互式地取消任何保存或者複製工作簿的方法會在您的代碼中觸發一個運行時異常。例如,如果您的過程調用 SaveAs 方法,但是沒有禁用 Excel 的提示功能,並且您的用戶在提示後單擊“取消”,則 Excel 會將運行時錯誤返回給代碼。

Worksheet 類

當您閱讀到文章的此處時,您已經瞭解了使用一個單獨的工作表需要掌握的大多數概念。儘管 Worksheet 類提供了大量的成員,但是其大多數的屬性、方法和事件與 Application 和(或) Workbook 類提供的成員是相同的或相似的。這一部分將集中探討 Worksheet 類的重要成員及特定問題,這些問題是您在本文的其他部分所沒有接觸過的內容。(您可以在示例工作簿中的 Worksheet Object 工作表看到這一部分中的例子。)

不存在 Sheet 類

儘管 Excel 提供了一個 Sheets 集合作爲 Workbook 對象的屬性,但是在 Excel 中您找不到 Sheet 類。相反,Sheets 集合的每個成員都是 WorksheetChart 對象。您可以以這種方式考慮它:把 WorksheetChart 類看成內部 Sheet 類的特定實例(並且對無法訪問源代碼的人來說,將無法知道這種看法是否和實際的實現相符),但是 Sheet 類對外部不可用。

使用保護

通常,Excel 中的保護功能可以防止用戶和(或)代碼修改工作表內的對象。一旦您啓用了對工作表保護功能,除非您預作安排,否則用戶不能編輯或者修改工作表。在用戶界面內,您可以使用 Tools|Protection|Protect Sheet 菜單項啓用保護功能。當選擇此項後會顯示“保護工作表”對話框,如圖 12 所示。您可以在此設置密碼或者允許用戶執行特定的操作。默認情況下,一旦啓用保護功能,所有的單元格都會被鎖定。此外,通過使用 Tools|Protection|Allow Users to Edit Ranges 菜單項(它會顯示如圖 13 所示的對話框),您可以讓用戶編輯特定區域。將這兩個對話框結合使用,您可以鎖定工作表,然後可以讓用戶編輯特定的功能和區域。

12. 在用戶界面中,使用此對話框的控制保護。

a

13. 使用此對話框,您可以允許用戶編輯特定的區域。

您可以使用工作表的 Protect 方法通過編程方法控制對工作表的保護。這個方法的語法如下面的例子所示,其中的每個參數都是可選的:

' Visual Basic
WorksheetObject.Protect(Password, DrawingObjects, Contents, _
  Scenarios, UserInterfaceOnly, AllowFormattingCells, _
  AllowFormattingColumns, AllowFormattingRows, _
  AllowInsertingColumns, AllowInsertingRows, _
  AllowInsertingHyperlinks, AllowDeletingColumns, _
  AllowDeletingRows, AllowSorting, AllowFiltering, _
  AllowUsingPivotTables)

// C#
WorksheetObject.Protect(Password, DrawingObjects, Contents, 
  Scenarios, UserInterfaceOnly, AllowFormattingCells, 
  AllowFormattingColumns, AllowFormattingRows, 
  AllowInsertingColumns, AllowInsertingRows, 
  AllowInsertingHyperlinks, AllowDeletingColumns, 
  AllowDeletingRows, AllowSorting, AllowFiltering, 
  AllowUsingPivotTables);

下面的列表描述了 Protect 方法的參數:

  • 設置 Password 參數來指定一個區分大小寫的字符串,這是取消保護工作表所需要的。如果您不指定這個參數,任何人都可以取消保護工作表。

  • DrawingObjects 參數設置爲 True 來保護工作表的形狀。默認值爲 False。

  • Contents 參數設置爲 True 來保護工作表的內容(單元格)。默認值爲 True,您可能永遠不會改變它。

  • Scenarios 參數設置爲 True 來保護工作表中的方案。默認值爲 True。

  • UserInterfaceOnly 參數設置爲 True 可以允許通過代碼修改,但是不允許通過用戶界面修改。默認值爲 False,這意味着通過代碼和用戶界面項都不可以修改受保護的工作表。這個屬性設置只適用於當前會話。如果您想讓代碼可以在任何會話中都可以操作工作 表,那麼您需要每次工作簿打開的時候添加設置這個屬性的代碼。

  • AllowFormattingCells 參數、AllowFormattingColumns 參數和前面方法語法的完整列表中所示的其餘參數允許特定的格式化功能,對應於對話框中(如圖 12 所示)的選項。默認情況下,所有這些屬性都是 False。

可以調用工作表的 Protect 方法來保護工作表,如下面的代碼片段所示,這段代碼設置了密碼,並且只允許排序:

' Visual Basic
DirectCast(ThisApplication.Sheets(1), Excel.Worksheet). _
  Protect("MyPassword", AllowSorting:=True)

// C#
((Excel.Worksheet)ThisApplication.Sheets[1]).Protect(
    "MyPassword", Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, true, Type.Missing, Type.Missing);

提示 很明顯,在您的代碼中硬編碼密碼並不是一個很好的主意。最常見的情況是,您需要從用戶那裏得到密碼,然後將這個密碼應用於工作簿,但不保存。通常,在源代碼中您是不會看到硬編碼密碼的。

爲了取消對工作表的保護,您可以使用下面的代碼。這段代碼假定有一個名稱爲 GetPasswordFromUser 的過程,這個過程要求用戶輸入一個密碼,並且返回輸入的密碼值:

' Visual Basic
DirectCast(ThisApplication.Sheets(1), Excel.Worksheet). _
  Unprotect(GetPasswordFromUser())

// C#
((Excel.Worksheet)ThisApplication.Sheets[1]).
  Unprotect(GetPasswordFromUser());

Unprotect 方法將取消對工作表的保護,並讓您提供一個可選的密碼。

Excel 也提供其他兩個對象,您將會發現,當使用保護的時候它們很有用:ProtectionAllowEditRange 對象。Protection 對象封裝了您調用 Protect 方法時指定的所有信息,及未保護區域的信息。通過調用 Protect 方法設置共享的 Protection 對象的屬性,這些對象提供了以下對應於 Protect 方法的參數的 Boolean 屬性:

  • AllowDeletingColumns, AllowDeletingRows

  • AllowFiltering

  • AllowFormattingCells, AllowFormattingColumns, AllowFormattingRows

  • AllowInsertingColumns, AllowInsertingHyperlinks, AllowInsertingRows

  • AllowSorting

  • AllowUsingPivotTables

此外,Protection 類提供 AllowEditRanges 屬性,它允許您指定工作表上的可編輯區域,對應於在 13 中所示的對話框中指定的信息。 AllowEditRanges 屬性包含一個 AllowEditRange 對象集合,其中的每個對象都提供許多有用的屬性,包括:

  • Range :獲取或者設置對應於可編輯區域的範圍

  • Title :獲取或者設置可編輯區域的標題(用於在如 13 所示的對話框中顯示)。

  • Users :獲取或者設置 UserAccess 對象集合(有關 UserAccess 對象的更多信息,請參考聯機文檔)。

在示例工作簿上的 Worksheet Object 工作表(見 14 )中,您可以試驗一下通過編程實現的保護功能。單擊 Protect 保護工作表,這樣您就只能編輯處於陰影區域的內容(名稱爲 Information 和 Date 的兩個範圍)。單擊 Unprotect 取消保護工作表。

14. 測試工作表的保護功能。

在示例工作表中的鏈接會運行以下過程:

' Visual Basic
Private Sub ProtectSheet()
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)

    With ws.Protection.AllowEditRanges
        .Add("Information", ThisApplication.Range("Information"))
        .Add("Date", ThisApplication.Range("Date"))
    End With
    ws.Protect()
End Sub

Private Sub UnprotectSheet()
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisApplication.Sheets("Worksheet Class"), _
      Excel.Worksheet)

    ' Unprotect the sheet.
    ws.Unprotect()

    ' Delete all protection ranges, just to clean up.
    ' You must loop through this using the index, 
    ' backwards. This collection doesn't provide 
    ' an enumeration method, and it doesn't handle
    ' being resized as you're looping in a nice way.
    Dim i As Integer
    With ws.Protection.AllowEditRanges
        For i = .Count To 1 Step -1
            .Item(i).Delete()
        Next i
    End With
End Sub

// C#

private void ProtectSheet()
{     
    Excel.Worksheet ws =
      (Excel.Worksheet)ThisApplication.ActiveSheet;

    Excel.AllowEditRanges ranges = ws.Protection.AllowEditRanges;
    ranges.Add("Information", 
        ThisApplication.get_Range("Information", Type.Missing), 
        Type.Missing);
    ranges.Add("Date", 
        ThisApplication.get_Range("Date", Type.Missing), Type.Missing);

    ws.Protect(Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing,Type.Missing, Type.Missing, 
        Type.Missing,Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing,Type.Missing,Type.Missing,
        Type.Missing,Type.Missing);
}

private void UnprotectSheet()
{
    Excel.Worksheet ws = 
        (Excel.Worksheet) ThisApplication.Sheets["Worksheet Class"];
    ws.Unprotect(Type.Missing);

    // Delete all protection ranges, just to clean up.
    // You must loop through this using the index, 
    // backwards. This collection doesn't provide 
    // an enumeration method, and it doesn't handle
    // being resized as you're looping in a nice way.
    Excel.AllowEditRanges ranges = ws.Protection.AllowEditRanges;
    for (int i = ranges.Count; i >= 1; i--)
    {
        ranges[i].Delete();
    }
}

對象屬性

Worksheet 類提供了幾個返回對象的屬性。下面的章節將介紹這些對象,並提供使用這些對象的例子。

批註

使用 Insert|Comment 菜單項,您可以在工作表的一個範圍中插入附加的文本批註(見圖 15)。在代碼中使用 Range 對象的 AddComment 方法也可以達到相同目的。下面的代碼刪除與名爲 Date 的範圍相關聯的批註(若存在批註),然後創建一個新的批註。最後,代碼通過調用下一個代碼示例所描述的 ShowOrHideComments 方法來顯示工作表中的所有批註(見圖 16):

' Visual Basic
Dim rng As Excel.Range = ThisApplication.Range("Date")
If Not rng.Comment Is Nothing Then
    rng.Comment.Delete()
End If
rng.AddComment("Comment added " & DateTime.Now)

' Display all the comments:
ShowOrHideComments(Show:=True)

// C#
Excel.Range rng = ThisApplication.get_Range("Date", Type.Missing);
if (rng.Comment != null ) 
{
    rng.Comment.Delete();
}
rng.AddComment("Comment added " + DateTime.Now);

// Display all the comments:
ShowOrHideComments(true);

15. 在用戶界面中您可以方便地將一個新的批註插入到工作表中。

16. 在示例工作表中顯示所有批註後

Worksheet 類提供了它的 Comments 屬性,這個屬性返回一個 Comments 對象。這個 Comment 對象集合允許您循環訪問和 Worksheet 相關的所有 Comment 對象。 Comment 類並沒有提供很多成員。可以使用 Comment 類的 Visible 屬性來顯示或者隱藏批註,或者使用 Delete 方法刪除批註。此外,您可能發現 Text 方法很有用:這個方法允許您將文本添加到批註中,可以添加到現有文本的後面,也可以覆蓋現有的文本。

添加一個批註後,您可能想要顯示工作表中的批註。示例項目包含一個過程 ShowOrHideComments,這個過程會顯示或者隱藏所有在活動工作表中的批註:

' Visual Basic
Private Sub ShowOrHideComments(ByVal Show As Boolean)
    ' Show or hide all the comments:
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisApplication.Sheets("Worksheet Class"), _
      Excel.Worksheet)

    Dim i As Integer
    For i = 1 To ws.Comments.Count
        ws.Comments(i).Visible = Show
    Next
End Sub

// C#
private void ShowOrHideComments(bool show)
{
    // Show or hide all the comments:
    Excel.Worksheet ws = 
        (Excel.Worksheet) ThisApplication.Sheets["Worksheet Class"];

    for (int i = 1; i <= ws.Comments.Count; i++)
    {
        ws.Comments[i].Visible = show;
    }
}

與 Excel 中的許多輔助集合類相似,Comments 集合沒有提供一個默認的枚舉器。也就是說,您將不能使用一個 For Each 循環來訪問這個集合的所有元素。對於類似 Comment 集合的集合,您必須使用一個索引的循環來循環訪問這個集合。

提綱

Excel通過使用提綱功能支持將不同行的數據進行分組。您也可以在代碼中利用相同的功能。例如,給定如 17 所示的一組行,您可以添加提綱功能(在所示的圖中已添加),這樣您就能夠將這些行進行摺疊(如 18 所示),摺疊的組如 19 所示。

17 . 創建這些組

18. 摺疊的組

19. 完全摺疊的組

Worksheet 類提供了 Outline 屬性,它本身就是一個 Outline 對象。 Outline 類並沒有提供太多成員,下面的列表描述了您可能會使用到的成員:

  • AutomaticStyles (Boolean) 向 Excel 指示是否對提綱應用自動樣式。

  • SummaryColumn (XlSummaryColumn) 獲取或設置摘要列的位置。 XlSummaryColumn 枚舉有兩個可能使用的值:xlSummaryOnLeftxlSummaryOnRight

  • SummaryRow (XlSummaryRow) 獲取或設置摘要行的位置。 XlSummaryRow 枚舉具有兩個可能使用的值:xlSummaryAbovexlSummaryBelow

  • ShowLevels 允許您摺疊提綱組或者將其擴展到您想要的行級和(或)列級。您可以給這個方法傳遞兩個參數,如下面的代碼所示:

    ' Visual Basic
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)
    ' Specify RowLevels and/or ColumnLevels parameters:
    ws.Outline.ShowLevels(RowLevels:=3)
    
    // C#
    Excel.Worksheet ws = 
        (Excel.Worksheet) ThisApplication.ActiveSheet;
    
    // Specify RowLevels and/or ColumnLevels parameters:
    ws.Outline.ShowLevels(3, Type.Missing);
    

示例工作表包含對應於 2001 (Data2001) 和 2002 (Data2001) 年及整個行集 (AllData) 的數據的命名範圍。這些命名範圍覆蓋工作表的整個範圍;要想進行分組,您必須使用包含所有行的範圍。對於 2003 的數據,沒有一個和其關聯的命名範圍以便示例代碼演示如何將所有的行作爲範圍使用。

創建組是很簡單的:可以調用與一個或多個完整行相對應的一個範圍的 Group 方法來創建組。(您可以指定 4 個可選的分組參數,包括:被分組的開始和終止值、按值分組和一個表明分組週期的 Boolean 值數組。該示例中沒有使用這些可選參數,因爲您很少會使用這些參數。)調用 Ungroup 方法可以取消分組。例如,單擊示例工作表上的 Work with Groups 鏈接來運行下面的代碼:

' Visual Basic
Private Sub WorkWithGroups()
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)

    ' Set worksheet-level features for the outline.
    ' In this case, summary rows are below
    ' the data rows (so Excel knows where to put
    ' the summary rows), and we don't want Excel
    ' to format the summary rows--that's already been done.
    ws.Outline.SummaryRow = Excel.XlSummaryRow.xlSummaryBelow
    ws.Outline.AutomaticStyles = False

    ' Group the two named ranges. Each of these
    ' ranges extends across entire rows.
    ThisApplication.Range("Data2001").Group()
    ThisApplication.Range("Data2002").Group()
    ThisApplication.Range("AllData").Group()

    ' The range of rows from 24 to 27 doesn't have 
    ' a named range, so you can work with that 
    ' range directly.
    Dim rng As Excel.Range = _
      DirectCast(ws.Rows("24:27"), Excel.Range)
    rng.Group()

    ' Collapse to the second group level.
    ws.Outline.ShowLevels(RowLevels:=2)
End Sub

// C#
private void WorkWithGroups()
{
    Excel.Worksheet ws = 
        (Excel.Worksheet) ThisApplication.ActiveSheet;

    // Set worksheet-level features for the outline.
    // In this case, summary rows are below
    // the data rows (so Excel knows where to put
    // the summary rows), and we don't want Excel
    // to format the summary rows--that's already been done.
    ws.Outline.SummaryRow = Excel.XlSummaryRow.xlSummaryBelow;
    ws.Outline.AutomaticStyles = false;

    // Group the two named ranges. Each of these
    // ranges extends across entire rows.
    ThisApplication.get_Range("Data2001", Type.Missing).
        Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    ThisApplication.get_Range("Data2002", Type.Missing).
        Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    ThisApplication.get_Range("AllData", Type.Missing).
        Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);

    // The range of rows from 24 to 27 doesn't have 
    // a named range, so you can work with that 
    // range directly.
    Excel.Range rng = (Excel.Range)ws.Rows["24:27", Type.Missing];
    rng.Group(Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing);

    // Collapse to the second group level.
    ws.Outline.ShowLevels(2, Type.Missing);
}

爲了對三個命名範圍分組,代碼只是簡單的調用相應範圍的 Group 方法:

' Visual Basic
ThisApplication.Range("Data2001").Group()

// C#
ThisApplication.get_Range("Data2001", Type.Missing).
    Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);

爲了對未命名的範圍分組,代碼使用了工作表的 Rows 屬性,給定行範圍。這個屬性返回一個對應於要使用的行的範圍:

' Visual Basic
Dim rng As Excel.Range = _
  DirectCast(ws.Rows("24:27"), Excel.Range)
rng.Group()

// C#
Excel.Range rng = (Excel.Range)ws.Rows["24:27", Type.Missing];
rng.Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing);

單擊示例工作表中的 Clear Groups 鏈接來運行類似代碼,這樣可以清除組:

' Visual Basic
Private Sub ClearGroups()
    Dim ws As Excel.Worksheet = _
      DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)

    ' Specify RowLevels and/or ColumnLevels parameters:
    ws.Outline.ShowLevels(RowLevels:=3)

    Dim rng As Excel.Range = _
      DirectCast(ws.Rows("24:27"), Excel.Range)
    rng.Ungroup()

    ThisApplication.Range("Data2001").Ungroup()
    ThisApplication.Range("Data2002").Ungroup()
    ThisApplication.Range("AllData").Ungroup()
End Sub

// C#
private void ClearGroups()
{
    Excel.Worksheet ws = 
        (Excel.Worksheet) ThisWorkbook.Sheets["Worksheet Class"];

    // Specify RowLevels and/or ColumnLevels parameters:
    ws.Outline.ShowLevels(3, Type.Missing);

    Excel.Range rng = (Excel.Range) ws.Rows["24:27", Type.Missing];
    rng.Ungroup();

    ThisApplication.get_Range("Data2001", Type.Missing).Ungroup();
    ThisApplication.get_Range("Data2002", Type.Missing).Ungroup();
    ThisApplication.get_Range("AllData", Type.Missing).Ungroup();
}

通過使用這些方法,您可以創建和刪除組,並且可以控制工作表中顯示的組級。

Range 對象

Range 對象是您在 Excel 應用程序中最經常使用的對象;在您可以操作 Excel 內的任何區域之前,您需要將其表示爲一個 Range 對象,然後使用該 Range 對象的方法和屬性。Range 類是很重要的,目前爲止,本篇文章中的每個示例中在某種程度上都使用了一個 Range 對象。基本上來說,一個 Range 對象代表一個單元格、一行、一列、包含一個或者更多單元塊(可以是連續的單元格,也可以式不連續的單元格)的選定單元格,甚至是多個工作表上的一組單元格。

由於不可能討論 Range 這個大類的所有成員,所以這一部分集中探討三個主要的問題:

  • 在代碼中引用範圍。

  • 在代碼中操作範圍。

  • 使用Range 對象達到特定目的。

換句話說,由於 Range 對象在衆多不同場合下有衆多不同用途,所有本節集中回答“我如何……”這樣的問題,而不是提供對所有成員全面的列表。

管理選擇區域

盡 管使用當前選擇區域作爲修改一個範圍的屬性和行爲的做法很具有吸引力,但是您最好避免這樣做。就像任何其他共享資源一樣,在 Excel 內的選擇區域代表用戶的選擇。如果您在代碼中修改該選擇區域,將會導致用戶失去對當前選擇區域的控制。經驗法則是:只有在您想改變用戶的選擇區域時,纔可 以調用對象的 Select 方法。作爲一個開發人員,您不能只是爲了方便就去調用 Select 方法。如果您的目的只是設置一個範圍的屬性,總會有其他替代方法。總之,避免使用 Select 方法不但可以使您的代碼運行得更快,還可以使您的用戶免受干擾。

如下代碼清除用戶當前單元格相鄰區域,編寫這樣的代碼是很簡單的:

' Visual Basic
ThisApplication.ActiveCell.CurrentRegion.Select
DirectCast(ThisApplication.Selection, Excel.Range).ClearContents

// C#
ThisApplication.ActiveCell.CurrentRegion.Select();
((Excel.Range)ThisApplication.Selection).ClearContents();

這樣做會取消用戶的選擇。如果最初只選擇一個單元格,那麼當運行前面的代碼片段後,單元格附近的整大塊將會被選定。實際上,除非您的目的是選擇所有的單元格區域,否則使用如下所示代碼是更好的解決方案:

' Visual Basic
ThisApplication.ActiveCell.CurrentRegion.ClearContents

// C#
ThisApplication.ActiveCell.CurrentRegion.ClearContents();

爲什麼任何人都會想到使用第一個代碼片段呢?之所以會使用這樣的代碼,是因爲 Excel 開發人員在嘗試發現如何使用 Excel 內的各種對象及其方法的一開始都會傾向於使用 Excel 宏記錄器。這個一個好主意,但是宏記錄器編寫 的代碼實在很糟糕。通常,宏記錄器使用了選擇區域,並在記錄任何任務的時候修改選擇區域。

提示 當使用一個或一組單元格時,儘可能使用描述您想使用的單元格的範圍,而不是修改選擇區域。如果您的目的是更改用戶的選擇區域,則使用 Range.Select 方法。

在代碼中引用 Range

Range 類是很靈活的,您在編程使用範圍的時候會發現它給您提供太多的選擇。有時 Range 對象是單個的對象,而有時它代表對象的一個集合。它具有 Item 和 Count 成員,儘管 Range 對象通常指單個的對象,這使得有時如何準確使用 Range 對象成爲一件很棘手的事情。

提示 下面的幾個示例獲得一個範圍的 Address 屬性。這個屬性返回一個包含範圍座標的字符串,座標以下面幾種格式之一表示,包括:“$A$1”(單元格在位置 A1)、“$1”(在工作表的第一行)和“$A$1:$C$5”(範圍包括介於 A1 和 C5 之間矩形內的所有單元格)。“$”表示絕對座標(而非相對座標)。使用 Address 屬性是找到您要檢索的範圍的準確位置的最簡單方法。有關引用範圍的各種方法的更多信息,請參考 Excel 聯機幫助。

以其最簡單的方式,您可以編寫如下程序清單所示的代碼來使 Range 對象引用單個單元格或者一組單元格。所有示例都假定具有下面的設置代碼:

' Visual Basic
Dim ws As Excel.Worksheet = _
  DirectCast(ThisWorkbook.Worksheets(1), Excel.Worksheet)
Dim rng, rng1, rng2 As Excel.Range

// C#
Excel.Worksheet ws = (Excel.Worksheet)ThisWorkbook.Worksheets[1];
Excel.Range rng, rng1, rng2;

您可以使用下面的任何一種方法來引用一個特定範圍(也有其他幾種取得 Range 對象引用的方法):

  • 引用 Application 對象的 ActiveCell 屬性:

    ' Visual Basic
    rng = ThisApplication.ActiveCell
    
    // C#
    rng = ThisApplication.ActiveCell;
    
  • 使用對象的 Range 屬性指定一個區域。由於 C# 不支持參數化的非索引屬性,作爲替代,您必須調用 get_Range 方法,這個方法需要兩個參數:

    ' Visual Basic
    rng = ws.Range("A1")
    rng = ws.Range("A1:B12")
    
    // C#
    rng = ws.get_Range("A1", Type.Missing);
    rng = ws.get_Range("A1:B12", Type.Missing);
    
  • 使用工作表的 Cells 屬性,指定單個行和列值:

    ' Visual Basic
    ' The Cells collection returns an Object--
    ' Convert it to a Range object explicitly:
    rng = DirectCast(ws.Cells(1, 1), Excel.Range)
    
    // C#
    rng = (Excel.Range)ws.Cells[1, 1];
    
  • 指定一個範圍的“角落”;您也可以直接引用範圍的 CellsRowsColumns 屬性;每種情況下,屬性都返回一個範圍:

    ' Visual Basic
    rng = ws.Range("A1", "C5")
    rng = ws.Range("A1", "C5").Cells
    rng = ws.Range("A1", "C5").Rows
    rng = ws.Range("A1", "C5").Columns
    
    // C#
    rng = ws.get_Range("A1", "C5");
    rng = ws.get_Range("A1", "C5").Cells;
    rng = ws.get_Range("A1", "C5").Rows;
    rng = ws.get_Range("A1", "C5").Columns;
    
  • 引用一個命名範圍。您可以看到本文廣泛使用這種技術。注意:由於 C# 的 get_Range 方法需要兩個參數,而範圍名只需要其中的一個參數,所以您必須爲第二個參數指定 Type.Missing

    ' Visual Basic
    rng = ThisApplication.Range("SomeRangeName")
    
    // C#
    rng = ThisApplication.Range("SomeRangeName", Type.Missing);
    
  • 引用特定行或特定列或行和列的範圍;注意:RowsColumns 屬性都返回一個 Object ,如果您將 Option Strict 設置成 On ,則需要類型轉換:

    ' Visual Basic
    rng = DirectCast(ws.Rows(1), Excel.Range)
    rng = DirectCast(ws.Rows("1:3"), Excel.Range)
    rng = DirectCast(ws.Columns(3), Excel.Range)
    
    // C#
    rng = (Excel.Range)ws.Rows[1, Type.Missing];
    rng = (Excel.Range)ws.Rows["1:3", Type.Missing];
    rng = (Excel.Range)ws.Columns[3, Type.Missing];
    

    警告 Columns 屬性的 IntelliSense 功能是容易誤解的 — 它表明您必須指定行值,然後指定列值。在實際應用中,Columns 屬性的值是取倒數的。對於 RowsColumns 屬性,不使用第二個參數。

  • 使用 Application 對象的 Selection 屬性返回與選定單元格對應的範圍;在如 20 所示的情況下,下面的代碼片段返回字符串“$C$3”(使用“$”表示絕對座標):

    ' Visual Basic
    Debug.WriteLine( _
      DirectCast(ThisApplication.Selection, Excel.Range).Address)
    
    // C#
    System.Diagnostics.Debug.WriteLine(
        ((Excel.Range)ThisApplication.Selection).
        get_Address(Type.Missing, Type.Missing, 
        Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing));
    

    提示 Address 屬性是 C# 不能直接處理的另一個參數化屬性。調用 get_Address 方法來取得對應於 Range 對象的地址。 Address 屬性的所有參數都是可選的,但是 get_Address 方法要取得 5 個參數 — 您可能只需要關心第三個參數,它允許您指定地址格式。

  • 創建一個包含其他兩個合併範圍的範圍(在引號內指定兩個範圍,並用逗號隔開):

    ' Visual Basic
    rng = ThisApplication.Range("A1:D4, F2:G5")
    ' You can also use the Application object's Union
    ' method to retrieve the intersection of two ranges:
    rng1 = ThisApplication.Range("A1:D4")
    rng2 = ThisApplication.Range("F2:G5")
    rng = ThisApplication.Union(rng1, rng2)
    
    // C#
    rng = ThisApplication.get_Range("A1:D4, F2:G5", Type.Missing);
    // You can also use the Application object's Union
    // method to retrieve the intersection of two ranges, but this
    // is far more effort in C#:
    rng1 = ThisApplication.get_Range("A1", "D4");
    rng2 = ThisApplication.get_Range("F2", "G5");
    // Note that the Union method requires you to supply thirty
    // parameters: 
    rng = ThisApplication.Union(rng1, rng2, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    
  • 創建一個引用其他兩個範圍重疊部分的範圍(在引號內指定兩個範圍,並不使用分隔符):

    ' Visual Basic
    rng = ThisApplication.Range("A1:D16 B2:F14")
    ' You can also use the Application object's Intersect
    ' method to retrieve the intersection of two ranges:
    rng1 = ThisApplication.Range("A1:D16")
    rng2 = ThisApplication.Range("B2:F14")
    rng = ThisApplication.Intersect(rng1, rng2)
    
    // C#
    rng = ThisApplication.get_Range("A1:D16 B2:F14", Type.Missing);
    // You can also use the Application object's Intersect
    // method to retrieve the intersection of two ranges. Note
    // that the Intersect method requires you to pass 30 parameters:
    rng1 = ThisApplication.get_Range("A1", "D16");
    rng2 = ThisApplication.get_Range("B2", "F14");
    rng = ThisApplication.Intersect(rng1, rng2, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing,
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing);
    
  • 使用範圍的 Offset 屬性取得相對於初始範圍的一個範圍;以下示例在位於第一行、第一列的單元格範圍中添加內容:

    ' Visual Basic
    rng = DirectCast(ws.Cells(1, 1), Excel.Range)
    
    Dim i As Integer
    For i = 1 To 5
        rng.Offset(i, 0).Value = i.ToString
    Next
    
    // C#
    rng = (Excel.Range) ws.Cells[1, 1];
    for (int i = 1; i <= 5; i++)
    {
        rng.get_Offset(i, 0).Value2 = i.ToString();
    }
    

    提示 Range.Offset 屬性是一個參數化的屬性,因此 C# 代碼不能直接取得這個屬性值。作爲替代,C# 開發人員必須調用 get_Offset 方法。

  • 使用範圍的 CurrentRegion 屬性取得一個代表當前區域的範圍,這個當前區域由最近的空行和列限定;例如,在 20 中,以下表達式將當前區域的字體設置成粗體:

    ' Visual Basic
    ThisApplication.Range("C3").CurrentRegion.Font.Bold = True
    
    // C#
    ThisApplication.get_Range("C3", Type.Missing).
        CurrentRegion.Font.Bold = True;
    
     
    

    20. 請求 C3 單元格的 CurrentRegion 屬性返回 A1:E5 範圍。
  • 使用範圍的 Areas 屬性取得範圍集合,其中每個範圍對應於範圍內容的一個區域。例如,下面的代碼片段顯示了名稱爲 Test 的範圍內兩個區域的地址,“$B$1:$E$5”和“$C$7:$G$11”(使用“$”代表絕對座標),如 21 所示:

    ' Visual Basic
    rng = ThisApplication.Range("Test")
    Dim i As Integer
    For i = 1 To rng.Areas.Count
      Debug.WriteLine(rng.Areas(i).Address)
    Next
    
    // C#
    rng = ThisApplication.get_Range("Test", Type.Missing);
    for (int i = 1; i <= rng.Areas.Count; i++)
    { 
        System.Diagnostics.Debug.WriteLine(
            rng.Areas[i].get_Address(Type.Missing, Type.Missing, 
            Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing));
    }
    

    21. 範圍包含的區域可以不連續,並且可以使用 Areas 屬性分別取得這些區域。
  • 使用 End 屬性,以及一個 XlDirection 枚舉值(xlUpxlToRightxlToLeftxlDown )來取得一個代表區域末端單元格的範圍,如同您按下了枚舉值所描述的鍵一樣;使用如圖 22 所示的選定單元格,下面的代碼片段會得到四個定義的範圍(如代碼中的註釋所示):

    ' Visual Basic
    Dim rngLeft, rngRight, rngUp, rngDown as Excel.Range
    rng = DirectCast(ThisApplication.Selection, Excel.Range)
    ' E3
    rngRight = rng.End(Excel.XlDirection.xlToRight)
    ' A3
    rngLeft = rng.End(Excel.XlDirection.xlToLeft)
    ' C1
    rngUp = rng.End(Excel.XlDirection.xlUp)
    ' C5
    rngDown = rng.End(Excel.XlDirection.xlDown)
    
    // C#
    Excel.Range rngLeft, rngRight, rngUp, rngDown;
    rng = (Excel.Range) ThisApplication.Selection;
    // Note that the Range.End property is parameterized, so 
    // C# developers cannot retrieve it. You must call the 
    // get_End method, instead:
    // E3
    rngRight = rng.get_End(Excel.XlDirection.xlToRight);
    // A3
    rngLeft = rng.get_End(Excel.XlDirection.xlToLeft);
    // C1
    rngUp = rng.get_End(Excel.XlDirection.xlUp);
    // C5
    rngDown = rng.get_Down(Excel.XlDirection.xlDown);
    

    22. 使用 End 屬性返回對應於一個範圍的範圍。
  • 使用 EntireRowEntireColumn 屬性引用包含特定範圍的行或列。例如,下面的代碼片段使用圖 21 中所示的示例將第 7 行到第 11 行的字體設置成粗體:

    ' Visual Basic
    rng = ThisApplication.Range("Test")
    rng.Areas(2).EntireRow.Font.Bold = True
    
    // C#
    rng = ThisApplication.get_Range("Test", Type.Missing);
    rng.Areas[2].EntireRow.Font.Bold = true;
    

使用技術

開發人員通常要求具有這樣的能力:改變包含選定單元格的整行的字體,使文本變成粗體。Excel 中並沒有內置這個功能,但是添加它也不是非常困難。示例工作簿中的 Range 類的工作表包含一個特別處理的範圍:當您選擇一個條目,其所在行會變成粗體。 23 顯示了這一行爲。

23. 選擇一個條目使整行變成粗體。

示例工作簿包含以下過程來處理格式化:

' Visual Basic
Private Sub BoldCurrentRow(ByVal ws As Excel.Worksheet)
    ' Keep track of the previously bolded row.
    Static intRow As Integer

    ' Work with the current active cell.
    Dim rngCell As Excel.Range = _
      ThisApplication.ActiveCell

    ' Bold the current row.
    rngCell.EntireRow.Font.Bold = True

    ' Make sure intRow isn't 0 (meaning that 
    ' this is your first pass through here).
    If intRow <> 0 Then
        ' If you're on a different
        ' row than the last time through here,
        ' make the old row not bold.
        If rngCell.Row <> intRow Then
            Dim rng As Excel.Range = _
              DirectCast(ws.Rows(intRow), Excel.Range)
            rng.EntireRow.Font.Bold = False
        End If
    End If
    ' Store away the new row number 
    ' for next time.
    intRow = rngCell.Row
End Sub

// C#
private int LastBoldedRow = 0;
private void BoldCurrentRow(Excel.Worksheet ws) 
{
    // Keep track of the previously bolded row.

    // Work with the current active cell.
    Excel.Range rngCell = ThisApplication.ActiveCell;

    // Bold the current row.
    rngCell.EntireRow.Font.Bold = true;

    // Make sure intRow isn't 0 (meaning that 
    // this is your first pass through here).
    if (LastBoldedRow != 0) 
    {
        // If you're on a different
        // row than the last time through here,
        // make the old row not bold.
        if (rngCell.Row != LastBoldedRow) 
        {
            Excel.Range rng = 
                (Excel.Range)ws.Rows[LastBoldedRow, Type.Missing];
            rng.Font.Bold = false;
        }
    }
    // Store away the new row number 
    // for next time.
    LastBoldedRow = rngCell.Row;
}

這個示例採用如下步驟來使當前行變成粗體,並且使前一次變成粗體的行變回原來的狀態:

  • 聲明一個變量(在 Visual Basic 中,類型爲靜態)用來跟蹤前面選定的行:

    ' Visual Basic
    Static intRow As Integer
    
    // C#
    private int LastBoldedRow = 0;
    
  • 使用 Application.ActiveCell 屬性取得對當前單元格的引用:

    ' Visual Basic
    private int LastBoldedRow = 0;
    Dim rngCell As Excel.Range = ThisApplication.ActiveCell
    
    // C#
    Excel.Range rngCell = ThisApplication.ActiveCell;
    
  • 使用活動單元格的 EntireRow 屬性使當前行變成粗體:

    ' Visual Basic
    rngCell.EntireRow.Font.Bold = True
    
    // C#
    rngCell.EntireRow.Font.Bold = true;
    
  • 確保 intRow 的當前值不爲 0,如果爲 0,則表明這是第一次運行這段代碼:

    ' Visual Basic
    If intRow <> 0 Then
        ' Code removed here...
    End If
    
    // C#
    if (LastBoldedRow != 0)
    {
        // Code removed here...
    }
    
  • 確保當前行和前面的行不同。如果當前行和前面的行不同,代碼只需修改行的狀態。Row 屬性返回一個整數值來指明對應於範圍的行:

    ' Visual Basic
    If rngCell.Row <> intRow Then
        ' Code removed here...
    End If
    
    // C#
    if (rngCell.Row != LastBoldedRow)
    {
        // Code removed here...
    }
    
  • 檢索一個代表前面選定行的範圍的引用,並將那一行設置成不爲粗體:

    ' Visual Basic
    Dim rng As Excel.Range = _
      DirectCast(ws.Rows(intRow), Excel.Range)
    rng.Font.Bold = False
    
    // C#
    Excel.Range rng = 
        (Excel.Range)ws.Rows[LastBoldedRow, Type.Missing];
    rng.Font.Bold = false;
    

示例工作簿從它的 SheetSelectionChange 事件處理程序調用 BoldCurrentRow 過程。在這個過程中,代碼驗證新選擇的行是否位於正確範圍(使用 Application 對象的 Intersect 方法),如果是,就調用 BoldCurrentRow 過程:

' Visual Basic
Private Sub ThisWorkbook_SheetSelectionChange( _
  ByVal Sh As Object, ByVal Target As Excel.Range) _
  Handles ThisWorkbook.SheetSelectionChange

    If Not ThisApplication.Intersect(Target, _
      ThisApplication.Range("BoldSelectedRow")) Is Nothing Then
        ' The selection is within the range where you're making
        ' the selected row bold.
        BoldCurrentRow(DirectCast(Sh, Excel.Worksheet))
    End If
End Sub

// C#
protected void ThisWorkbook_SheetSelectionChange(
  System.Object sh, Excel.Range Target)
{
    // Don't forget that the Intersect method requires
    // thirty parameters.
    if (ThisApplication.Intersect(Target, 
        ThisApplication.get_Range("BoldSelectedRow", Type.Missing), 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
        Type.Missing, Type.Missing, Type.Missing, Type.Missing) 
        != null)
    {
        // The selection is within the range where you're making
        //the selected row bold.
        BoldCurrentRow((Excel.Worksheet) sh);
    }
}

使用 Range

一旦您得到了對一個範圍的引用,您能用它作什麼呢?可以列出的用途是無窮的,只要您能夠想象得到。這一節集中討論一些使用 Range 對象的技術,並且爲每種技術提供簡單的示例。這一部分中的所有示例都可以在示例工作簿的 Range Class 工作表中找到。

自動填充範圍

Range 類的 AutoFill 方法允許您使用值自動填充一個範圍。大多數情況下,AutoFill 方法用於將遞增或遞減的值存儲到一個範圍中。您可以通過提供可選的常量來指定此方法的行爲。這個常量來自 XlAutoFillType 枚舉(xlFillDaysxlFillFormatsxlFillSeriesxlFillWeekdaysxlGrowthTrendxlFillCopyxlFillDefaultxlFillMonthsxlFillValuesxlFillYearsxlLinearTrend )。如果您不指定一個填充類型,Excel 會假定您使用默認填充類型(xlFillDefault ),並且填充它認爲合適的指定範圍。

示例工作表(如 24 所示)包含四個將被自動填充的區域。列 B 包含五個工作日;列 C 包含五個月;列 D 包含五年內逐年遞增的日期;列 E 包含一系列數字,每行以二遞增。 25 顯示運行示例代碼後的相同區域。

24. 調用 AutoFill 方法之前的四個示例範圍。

25. 自動填充範圍後。

單擊 AutoFill 鏈接運行以下過程:

' Visual Basic
Private Sub AutoFill()
    Dim rng As Excel.Range = ThisApplication.Range("B1")
    rng.AutoFill(ThisApplication.Range("B1:B5"), _
      Excel.XlAutoFillType.xlFillDays)

    rng = ThisApplication.Range("C1")
    rng.AutoFill(ThisApplication.Range("C1:C5"), _
      Excel.XlAutoFillType.xlFillMonths)

    rng = ThisApplication.Range("D1")
    rng.AutoFill(ThisApplication.Range("D1:D5"), _
      Excel.XlAutoFillType.xlFillYears)

    rng = ThisApplication.Range("E1:E2")
    rng.AutoFill(ThisApplication.Range("E1:E5"), _
      Excel.XlAutoFillType.xlFillSeries)
End Sub

// C#
private void AutoFill()
{
    Excel.Range rng = ThisApplication.get_Range("B1", Type.Missing);
    rng.AutoFill(ThisApplication.get_Range("B1:B5", Type.Missing), 
        Excel.XlAutoFillType.xlFillDays);

    rng = ThisApplication.get_Range("C1", Type.Missing);
    rng.AutoFill(ThisApplication.get_Range("C1:C5", Type.Missing), 
        Excel.XlAutoFillType.xlFillMonths);

    rng = ThisApplication.get_Range("D1", Type.Missing);
    rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing), 
        Excel.XlAutoFillType.xlFillYears);

    rng = ThisApplication.get_Range("E1:E2", Type.Missing);
    rng.AutoFill(ThisApplication.get_Range("E1:E5", Type.Missing), 
        Excel.XlAutoFillType.xlFillSeries);
}

每種情況您都必須指定兩個範圍:

  • 調用 AutoFill 方法的範圍,它指定填充的“起點”。

  • 將要被填充的範圍,它作爲參數傳遞給 AutoFill 方法;目的範圍必須包含源範圍。

AutoFill 方法的第二個參數(XlAutoFillType 枚舉值)是可選的。通常,您需要提供該值才能得到您想要的行爲。例如,嘗試改變以下代碼:

' Visual Basic
rng.AutoFill(ThisApplication.Range("D1:D5"), _
  Excel.XlAutoFillType.xlFillYears)

// C#
rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing), 
    Excel.XlAutoFillType.xlFillYears);

使之看起來像這樣:

' Visual Basic
rng.AutoFill(ThisApplication.Range("D1:D5"))

// C#
rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing), 
    Excel.XlAutoFillType.xlFillDefault);

代碼經過修改後,日期將按天遞增,而不是按年遞增。

在範圍中查找

Range 類的 Find 方法允許您在範圍內搜索文本。這個靈活的方法模仿 Excel 中的查找和替換對話框的行爲,如圖 26 所示 - 實際上,這個方法直接和這個對話框交互。也就是說,Range.Find 方法或者使用您傳遞給它的參數來決定它的搜索行爲,或者如果您沒有傳遞參數,它就使用其在查找和替換對話框中的值來進行查找。 4 列出了 Range.Find 方法的參數,除了第一個參數外,其他所有參數都是可選的。

26. 在這個對話框上的選擇會影響 Find 方法的行爲。

警告 因爲 Range.Find 的幾乎所有參數都是可選的,同時因爲用戶可能通過“查找和替換”對話框改變值,所以您要確保真正將所有值傳給了 Find 方法,除非您想將用戶的選擇也考慮在內。當然,C# 開發人員不需要擔心這個問題,因爲他們在每個方法調用時都必須提供所有參數。

表 4. Range.Find 方法的參數

參數

類型

說明

What(必需的)

對象

要查找的數據;可以是一個字符串或者任何 Excel 數據類型。

After

範圍

您想從這個範圍的後面開始搜索(在搜索中,不包括這個單元格);如果不指定單元格,則從範圍的左上角開始搜索。

LookIn

XlFindLookin(xlValue、xlComments、xlFormulas)

要搜索的信息類型;不能用 Or 運算符組合查詢。

LookAt

XlLookAt(xlWhole、xlPart)

確定搜索匹配所有單元格,還是部分單元格。

SearchOrder

XlSearchOrder(xlByRows、xlByColumns)

決定搜索順序;xlByRows(默認值)將橫向搜索,然後縱向搜索;xlByColumns 將縱向搜索,然後橫向搜索。

SearchDirection

XlSearchDirection(xlNext、xlPrevious)

確定搜索的方向;默認值是 xlNext。

MatchCase

布爾值

確定搜索是否區分大小寫。

MatchByte

布爾值

確定是否雙字節字符只和雙字節匹配 (True) 或者也可以和單字節字符匹配 (False);只有當您安裝了對雙字節支持時才適用。

以下示例來自示例工作簿,它搜索一個範圍(名稱爲“Fruits”),並更改含有單詞“apples”的單元格的字體( 27 顯示了搜索結果)。這個過程也使用了 FindNext 方法,它使用前面設好的搜索設置重複搜索。(Range.FindPrevious 方法和 Range.FindNext 方法的使用幾乎一樣,但這個示例沒用到。)您要指定在哪個單元格後搜索,而剩下的就由 FindNext 方法處理。

27. 包含單詞“apples”的單元格的搜索結果

提示 FindNext (和 FindPrevious )方法一旦搜索到範圍的末端,就會重新回到搜索範圍的開始位置。要確保搜索不會成爲無限循環,永遠不休,您需要在代碼中設定。示例過程演示了處理這種情況的一種方法。如果您想完全避免這種無限循環,或者您想進行一個比 Find/FindNext/FindPrevious 方法更加複雜的搜索,那麼您也可以使用一個 For Each 循環在一個範圍內對所有單元格進行循環查找。

單擊示例工作簿的 Range Class 工作表中的 Find 鏈接來運行以下過程:

' Visual Basic
Private Sub DemoFind()
    Dim rng As Excel.Range = ThisApplication.Range("Fruits")
    Dim rngFound As Excel.Range

    ' Keep track of the first range you find.
    Dim rngFoundFirst As Excel.Range

    ' You should specify all these parameters
    ' every time you call this method, since they
    ' can be overriden in the user interface.
    rngFound = rng.Find( _
      "apples", , _
      Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, _
      Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, 
      False)
    While Not rngFound Is Nothing
        If rngFoundFirst Is Nothing Then
            rngFoundFirst = rngFound
        ElseIf rngFound.Address = rngFoundFirst.Address Then
            Exit While
        End If
        With rngFound.Font
            .Color = ColorTranslator.ToOle(Color.Red)
            .Bold = True
        End With
        rngFound = rng.FindNext(rngFound)
    End While
End Sub

// C#
private void DemoFind()
{
    Excel.Range rng = ThisApplication.
        get_Range("Fruits", Type.Missing);
    Excel.Range rngFound;

    // Keep track of the first range you find.
    Excel.Range rngFoundFirst = null;

    // You should specify all these parameters
    // every time you call this method, since they
    // can be overriden in the user interface.
    rngFound = rng.Find("apples", Type.Missing, 
        Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, 
        Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, 
        false, Type.Missing, Type.Missing);
    while (rngFound != null)
    {
        if (rngFoundFirst == null ) 
        {
            rngFoundFirst = rngFound;
        }
        else if (GetAddress(rngFound) == GetAddress(rngFoundFirst))
        {
            break;
        }
        rngFound.Font.Color = ColorTranslator.ToOle(Color.Red);
        rngFound.Font.Bold = true;
        rngFound = rng.FindNext(rngFound);
    }
}

這段代碼採取這些步驟來實現其目的:

  • 聲明 Excel.Range 變量來跟蹤整個範圍、第一個被發現的範圍和當前發現的範圍:

    ' Visual Basic
    Dim rng As Excel.Range = ThisApplication.Range("Fruits")
    Dim rngFound As Excel.Range
    Dim rngFoundFirst As Excel.Range
    
    // C#
    Excel.Range rng = ThisApplication.
        get_Range("Fruits", Type.Missing);
    Excel.Range rngFound;
    Excel.Range rngFoundFirst = null;
    
  • 搜索第一個匹配值,指定所有的參數(要在以後搜索的單元格除外) — 默認情況下,搜索從範圍左上角的單元格開始 — 然後在單元格值中搜索“apples”,匹配部分值,逐行向前搜索,並且不區分大小寫:

    ' Visual Basic
    rngFound = rng.Find( _
      "apples", , _
      Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, _
      Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, _
      False)
    
    // C#
    rngFound = rng.Find("apples", Type.Missing, 
        Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, 
        Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, 
        false, Type.Missing, Type.Missing);
    
  • 只要還能發現匹配值搜索就會繼續下去:

    ' Visual Basic
    While Not rngFound Is Nothing
        ' Code removed here...
    End While
    
    // C#
    while (rngFound != null)
    {
        // Code removed here...
    }
    
  • 將第一個發現的範圍 (rngFoundFirst) 和 Nothing 進行比較,如果代碼只發現第一個匹配值,rngFoundFirst 就會爲 Nothing,也只有在這種情況下它纔會爲 Nothing。在這種情況下,代碼將找到的範圍保存起來;否則,如果找到的範圍地址和第一個找到的範圍地址一致,代碼會退出循環。

    ' Visual Basic
    If rngFoundFirst Is Nothing Then
        rngFoundFirst = rngFound
    ElseIf rngFound.Address = rngFoundFirst.Address Then
        Exit While
    End If
    
    // C#
    if (rngFoundFirst == null ) 
    {
        rngFoundFirst = rngFound;
    }
    else if (GetAddress(rngFound) == GetAddress(rngFoundFirst))
    {
        break;
    }
    
  • 設置找到的範圍的外觀:

    ' Visual Basic
    With rngFound.Font
        .Color = ColorTranslator.ToOle(Color.Red)
        .Bold = True
    End With
    
    // C#
    rngFound.Font.Color = ColorTranslator.ToOle(Color.Red);
    rngFound.Font.Bold = true;
    
  • 執行另一次搜索:

    ' Visual Basic
    rngFound = rng.FindNext(rngFound)
    
    // C#
    rngFound = rng.FindNext(rngFound);
    

單擊示例工作表的 Reset Find 鏈接來運行這個簡單的過程,開始運行時將會重新設置範圍:

' Visual Basic
Private Sub ResetFind()
    Dim rng As Excel.Range = ThisApplication.Range("Fruits")
    With rng.Font
        .Color = ColorTranslator.ToOle(Color.Black)
        .Bold = False
    End With
End Sub

// C#
private void ResetFind()
{
    Excel.Range rng = ThisApplication.
        get_Range("Fruits", Type.Missing);
    rng.Font.Color = ColorTranslator.ToOle(Color.Black);
    rng.Font.Bold = false;
}

提示 如果您想在一個範圍內查找和替換,請使用 Range.Replace 方法。這個方法的使用類似於 Find 方法,但是可以讓您指定要替換的值。 Replace 方法返回一個指示是否執行替換的 Boolean 值。即使只替換一個值,它也會返回 True。

在範圍中對數據進行排序

就如通過 Excel 用戶界面對一個範圍內的數據進行排序一樣,您也可以採用編程方式使用 Range.Sort 方法對數據進行排序。您指出要被排序的範圍,要進行排序的至多三行或三列(可選),以及其他可選的參數,剩下的則由 Excel 來處理。 5 列出了 Sort 方法的所有參數。(Visual Basic .NET 開發人員很可能只會用到其中的一部分,而 C# 開發人員則必須爲每個參數賦予值。)

表 5. Sort 方法的參數

參數

類型

說明

Key1

Object(String 或 Range)

首要排序字段,可以是一個範圍名稱 (String),或是一個 Range 對象,確定了要排序的值。

Order1

XlSortOrder(xlAscending、xlDescending)

爲 Key1 中指定的值決定排序順序。

Key2

Object(String 或 Range)

第二個排序字段,排序透視表時無法使用。

Type

Object

當對透視表進行排序時,指定對哪些元素排序;對一個普通範圍則沒有影響。

Order2

XlSortOrder

爲在 Key2 中指定的值決定排序順序。

Key3

Object(String 或 Range)

第三個排序字段,不能使用於透視表。

Order3

XlSortOrder

爲在 Key3 中指定的值決定排序順序。

Header

XlYesNoGuess(xlGuess、xlNo、xlYes)

指定第一行是否包含頭信息,默認值爲 xlNo;如果想讓 Excel 自己去推測,就指定爲 xlGuess。

OrderCustom

Integer

爲自定義排序順序列表指定一個基於 1 的索引;如果不指定這個參數,則使用默認排序順序。圖 28 顯示了一種創建自定義排序順序的技術。對於這個例子,將這個參數指定爲 6 將基於“fruits”自定義順序進行排序。

MatchCase

Boolean

設置成 True 就會進行區分大小寫的排序,設置成 False 則進行不區分大小寫的排序;不能用於透視表。

Orientation

XlSortOrientation (xlSortRows, xlSortColumns)

排序方向。

SortMethod

XlSortMethod(xlStroke、xlPinYin)

指定排序方法;不能適用於所有語言(當前值只適用於對漢字進行排序,而不適用於對其他語言排序)。

DataOption1

XlSortDataOption (xlSortTextAsNumbers, xlSortNormal)

指定如何對 Key1 中指定的範圍進行文本排序;不能用於透視表排序。

DataOption2

XlSortDataOption

指定如何對 Key2 中指定的範圍進行文本排序;不能用於透視表排序。

DataOption3

XlSortDataOption

指定如何對 Key3 中指定的範圍進行文本排序;不能用於透視表排序。

提示 當 調用像這樣的方法時,Visual Basic .NET 開發人員相對於 C# 開發人員來說,有着明顯的優勢。因爲您不太可能會用到所有參數,Visual Basic .NET 開發人員能夠使用命名的參數,只須指定他們需要的參數即可。而爲了接受默認行爲,C# 開發人員必須將所有不使用的參數傳遞 null 值。

圖 28. 您可以創建自己的自定義排序列表,然後在代碼中引用這些特定的排序順序。

單擊 Range Class 示例工作表中的 Sort 鏈接運行以下過程,它首先根據第一列中的數據來對“Fruits”範圍排序,然後根據第二列中的數據排序:

' Visual Basic
Private Sub DemoSort()
    Dim rng As Excel.Range = ThisApplication.Range("Fruits")
    rng.Sort( _
      Key1:=rng.Columns(1), Order1:=Excel.XlSortOrder.xlAscending, _
      Key2:=rng.Columns(2), Order2:=Excel.XlSortOrder.xlAscending, _
      Orientation:=Excel.XlSortOrientation.xlSortColumns, _
      Header:=Excel.XlYesNoGuess.xlNo)
End Sub

// C#
private void DemoSort()
{
        Excel.Range rng = ThisApplication.
            get_Range("Fruits", Type.Missing);

        rng.Sort(rng.Columns[1, Type.Missing], 
            Excel.XlSortOrder.xlAscending, 
            rng.Columns[2, Type.Missing],Type.Missing, 
            Excel.XlSortOrder.xlAscending,
            Type.Missing, Excel.XlSortOrder.xlAscending,
            Excel.XlYesNoGuess.xlNo, Type.Missing, Type.Missing,
            Excel.XlSortOrientation.xlSortColumns, 
            Excel.XlSortMethod.xlPinYin,
            Excel.XlSortDataOption.xlSortNormal, 
            Excel.XlSortDataOption.xlSortNormal,
            Excel.XlSortDataOption.xlSortNormal);
}

單擊同一個工作表中的 Reset Sort 鏈接來運行以下過程,它根據自定義排序方法對第二列進行排序,如 28 所示:

' Visual Basic
Private Sub ResetSort()
    Dim rng As Excel.Range = ThisApplication.Range("Fruits")
    rng.Sort(rng.Columns(2), OrderCustom:=6, _
    Orientation:=Excel.XlSortOrientation.xlSortColumns, _
    Header:=Excel.XlYesNoGuess.xlNo)
End Sub

// C#
private void ResetSort()
{
    Excel.Range rng = ThisApplication.
        get_Range("Fruits", Type.Missing);
        rng.Sort(rng.Columns[2, Type.Missing],
            Excel.XlSortOrder.xlAscending, 
            Type.Missing, Type.Missing, Excel.XlSortOrder.xlAscending,
            Type.Missing, Excel.XlSortOrder.xlAscending,
            Excel.XlYesNoGuess.xlNo, 6, Type.Missing,
            Excel.XlSortOrientation.xlSortColumns, 
            Excel.XlSortMethod.xlPinYin,
            Excel.XlSortDataOption.xlSortNormal, 
            Excel.XlSortDataOption.xlSortNormal,
            Excel.XlSortDataOption.xlSortNormal);
}

下期內容

儘管本文看似冗長,但是它只是涉及到了由 Excel 對象模型提供的大量內容的表面而已。本文介紹了最重要的類 — ApplicationWorkbookWorksheetRange — 但是沒有介紹其他可能對您有用的類。您也許需要研究由 Excel 對象模型提供的第二“層”類,例如,PivotTableChart 類。只要您肯費心尋找您需要的確切類,那麼 Excel 對象模型的完整性使得您可以完成任何您所要求的自動化任務。只要掌握本文的內容、Object Browser 和 Excel VBA 聯機幫助,您就應該能夠勝任在 Excel 中所能想象到的任何任務了。

轉到原英文頁面

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章