关于对象

第九篇、Excel VBA对象类型之:Application对象

  接上一篇:操控工作簿对象。①引用工作簿、②操控工作簿、③跨工作簿和工作表的组合应用,是我们通篇介绍的概括。到这里,关于Excel VBA对象所需要了解的知识点,已经完成了95%。

  沿着“对象层级结构图”继续往上,到了最顶级对象:Application

它代表整个Excel应用程序本身,提供对Excel全局设置、功能和其他子对象(工作簿、工作簿、单元格等)的访问入口,包含各种属性和方法来控制Excel的整体行为,比如设置屏幕更新、计算模式、访问其他工作簿等等。通过Application对象,开发者可以控制Excel整体行为

  在以往介绍Range单元格、WorkSheet工作表和Workbook工作簿对象时,都是从“引用”开始、然后是对象的“方法”和“属性”,整个过程都是介绍如何找到“某个具体对象”、再针对该“具体对象”进行系列操控和改变样式~~!Application对象,同样它有“方法”和“属性”,但是因为它是最顶级对象、代表整个应用程序本身,便不会有多个实例、也就不再有“引用”某一个实例的说法(也就是不再有我们常说的“引用对象”)。

  我们换个方式,从Application的核心功能出发,通过它能做的四个事情来了解它:

  • 1、控制Excel的显示和行为、获取Excel的属性和状态;
  • 2、和用户的交互;
  • 3、执行工作表内置函数;
  • 4、响应Application级别的事件。

  这四个核心功能的实现,也基本是对象“方法”和“属性”的各种糅合,毕竟任何对对象的“操控”,都离不开对它状态和特征的改变

对象层级结构

  通过Excel对象的层级结构图,看到Excel对象模型是一个树状结构,从顶层的 Application 开始,逐级向下延伸:

复制成功!
1

Application  '顶级对象,应用程序本身

├─ Workbooks (所有打开的工作簿集合)'Workbook父对象
│   │
│   └─ Workbook (单个工作簿)'Worksheets父对象
│       │
│       ├─ Worksheets (所有工作表集合) 'Worksheet父对象
│       │   │
│       │   └─ Worksheet (单个工作表) 'Range的父对象
│       │       │
│       │       ├─ Range (单元格及区域)'Worksheet子对象
│       │       │
│       │       ├─ ChartObjects (嵌入的图表集合)
│       │       │   └─ ChartObject → Chart (嵌入的图表)
│       │       │
│       │       └─ Shapes (形状/图形集合,如文本框、图片等)
│       │           └─ Shape (单个形状)
│       │
│       └─ Charts (独立的图表工作表集合)
│           └─ Chart (独立的图表工作表)

└─ Windows (所有窗口集合)

  上一篇了解🔗Workbook(工作簿)对象,这篇来看顶级对象Application能实现什么?

本篇视频:

|01 控制Excel的显示和行为、获取Excel的属性和状态

  在使用VBA时,遇到需要控制Excel程序本身的设置,比如关闭屏幕刷新,调整计算模式为手动、获取Excel的版本号和用户信息t等等。目的或是提示代码的运行效率、或是更好的和用户交互、或是提升计算性能,这些都是通过操控Application对象完成的!

对象的部分控制、属性和状态:
点击放大的图片

  ※:Application.EnableEvents

  • 在工作表对象中关于工作表的SelectionChange事件时,有用到过,是防止陷入第一次修改自动触发了某个操作,操作又触发了操作这样的递归死循环!

  ※:Application.Calculation

  • Excel的计算模式设置为手动计算,这意味着除非明确指定,否则Excel不会自动重新计算工作表中的公式。在处理大量数据或复杂公式时可以显著提高VBA代码的执行速度。
示例:
复制成功!
1

Sub 不关闭屏幕刷新()
    Dim i As Integer

    Dim startTime As Double
    Dim endTime As Double

    '不关闭屏幕刷新执行代码
    startTime = Timer   '记录开始时间
    For i = 1 To 10     '执行10次打开和关闭同一个工薄的操作
        Workbooks.Open ("C:\Users\MagicBook\Desktop\kk\工作薄对象\工作簿4.xlsx")
        ActiveWorkbook.Close
    Next i
    endTime = Timer     '记录结束时间

    MsgBox "执行时间: " & endTime - startTime & " 秒"  '观察执行耗费时长

End Sub
Sub 关闭屏幕刷新()
    Dim i As Integer

    Dim startTime As Double
    Dim endTime As Double

     '关闭屏幕刷新执行代码
    Application.ScreenUpdating = False
    startTime = Timer   '记录开始时间
    For i = 1 To 10     '执行10次打开和关闭同一个工薄的操作
        Workbooks.Open ("C:\Users\MagicBook\Desktop\kk\工作薄对象\工作簿4.xlsx")
        ActiveWorkbook.Close
    Next i
    endTime = Timer     '记录结束时间
    Application.ScreenUpdating = True

    MsgBox "执行时间: " & endTime - startTime & " 秒"  '观察执行耗费时长

End Sub
Sub 不关闭警告()
    '模拟一个打开工作簿、新建工作表、写入内容后在删除工作表的过程,没有关闭警告时,会弹出内容让你选择
    Workbooks.Open ("C:\Users\MagicBook\Desktop\kk\工作薄对象\工作簿4.xlsx")

    ActiveWorkbook.Sheets.Add After:=ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.Count)    '在新建工作薄增加一个工作表,位置在最后
    ActiveSheet.Range("A1") = "表1"
    ActiveWorkbook.Save
    ActiveSheet.Delete
    ActiveWorkbook.Close

End Sub
Sub 关闭警告()
    '模拟一个打开工作簿、新建工作表、写入内容后在删除工作表的过程,关闭警告后,好像一切都没有发生
    Workbooks.Open ("C:\Users\MagicBook\Desktop\kk\工作薄对象\工作簿4.xlsx")

    Application.DisplayAlerts = False
    ActiveWorkbook.Sheets.Add After:=ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.Count)    '在新建工作薄增加一个工作表,位置在最后
    ActiveSheet.Range("A1") = "表1"
    ActiveWorkbook.Save
    ActiveSheet.Delete
    ActiveWorkbook.Close
    Application.DisplayAlerts = True

End Sub
Sub 在状态栏显示执行过程()
    Dim i As Integer

    Dim startTime As Double
    Dim endTime As Double

     '关闭屏幕刷新执行代码
    Application.ScreenUpdating = False
    startTime = Timer
    For i = 1 To 10
        Application.StatusBar = "第" & i & "次打开"   '状态栏显示执行进度
        Workbooks.Open ("C:\Users\MagicBook\Desktop\kk\工作薄对象\工作簿4.xlsx")
        ActiveWorkbook.Close
    Next i
    endTime = Timer

    Application.ScreenUpdating = True
    Application.StatusBar = False  '关闭状态栏显示

    MsgBox "执行时间: " & endTime - startTime & " 秒"  '观察执行耗费时长

End Sub

  Application可控制的行为和获取的状态远不止这些,任何你用鼠标对Excel应用程序的操作,背后都有相对应的控制方法和状态设置~~!

|02 和用户的交互

  还记得之前的示例程序中程序运行时弹出的窗口吗(Inputbox和msgbox)

点击放大的图片 点击放大的图片 点击放大的图片

  有提示输入内容的弹窗、有告知消息的弹窗、有警告类的弹窗、还有打开文件夹供选择的弹窗等等,都可以把他们看着(和用户的交互行为

  程序中的交互行为非常有用,把用户的选择结果与控制语句相结合,能决定哪些程序的被执行。比如 MsgBox消息框让用户选择,当选择是时(vbYes)执行哪些语句?当选择否时(vbNo)执行哪些语句?。这都可以被当做是和用户交互行为~。

  VBA的交互行为,大部分是Application对象带来的(可以理解是它的方法),还有两个程序本身内置的函数Msgbox和Input(就是和表格函数一样,直接封装好的的、能实现某个功能的东西)。

Application对象的一些交互行为(方法):
点击放大的图片
Application对象的一些交互行为(方法):
点击放大的图片

  ※:Application.InputBox和VBA.Inputbox的区别:

  • Application.InputBox是VBA.Inputbox的增强版,它支持类型验证(Type:=0-文本,1-数字,4-逻辑值,8-单元格引用,type不指定时,系统根据输入智能判断数据类型),而VBA.InputBox返回纯文本。
  • 当用户取消时,Application.InputBox返回False,而VBA.InputBox返回空字符串

  ※:通过这些方法,实现丰富的用户交互场景。

示例
复制成功!
1

Sub 交互1_打开对话框并选择文件()
    Dim FileToOpen As Variant  '声明一个可以存储任何数据类型的Variant变量
    Dim i As Long

    '显示标准的“打开”对话框,并获取选择的文件名,包含完整的路径
    FileToOpen = Application.GetOpenFilename("Excel文件,*.xl*", , "请选择要合并的多个工作簿/表", , True)

    For i = 1 To UBound(FileToOpen)
        Debug.Print FileToOpen(i)
    Next i

End Sub
Sub 交互2_打开指定类型的对话框()
    Dim FileD As Variant  '声明一个FileDialog类型的变量
    Dim i As Long

    '显示标准的“打开”对话框,对话框类型是“选择文件”
    Set FileD = Application.FileDialog(msoFileDialogFilePicker)

    FileD.Title = "选择Excel文件"
    FileD.Filters.Add "Excel文件", "*.xl*"  '过滤器,过滤其他文件,只显示后缀包含.xl的文件
    FileD.AllowMultiSelect = True ' 允许多选
    FileD.Show  '显示对话框

    For i = 1 To FileD.SelectedItems.Count
        Debug.Print FileD.SelectedItems(i)
    Next i

End Sub
Sub 交互3_调用Excel内置对话框()

    Application.Dialogs(xlDialogSaveAs).Show '另存为
    Application.Dialogs(xlDialogOpen).Show '打开文件

End Sub
Sub 交互4_支持数据验证的输入框()
    Dim userInput As Variant

    userInput = Application.InputBox("请输入一个数字", Type:=1) 'Type:=1 表示数值
    MsgBox userInput
    userInput = Application.InputBox(Prompt:="输入内容", Title:="标题", Type:=2) 'Type:=2 表示文本字符串
    MsgBox userInput
    userInput = Application.InputBox(Prompt:="单元格", Title:="单元格引用", Type:=8) 'Type:=8 表示单元格引用
    MsgBox userInput

End Sub
Sub 交互5_指定时间触发程序()

    '5秒钟后在状态栏显示执行过程
    Application.OnTime Now + TimeValue("00:00:05"), "在状态栏显示"

End Sub
Sub 在状态栏显示()
    Dim i As Integer
    For i = 1 To 10
        Application.Wait Now + TimeValue("00:00:01") '停顿 1秒
        Application.StatusBar = "第" & i & "次打开"   '状态栏显示执行进度
    Next i
    Application.StatusBar = False  '关闭状态栏显示

End Sub

|03 执行工作表内置函数

  Excel作为一款强大的电子表格软件,函数是一个非常重要的功能。它预先定义好了特定的计算公式,按照这个特定公式对一个或多个参数计算,得到一个或多个计算结果。

  这里不对函数做过多介绍。只需要知道既然函数是预制的、特定的逻辑计算,就没必要在用编程的方式重新实现这个计算过程,遇到时直接调用多好,效率又高、又简洁明了!

  通过Application.WorksheetFunction属性,可以直接在VBA中使用Excel的内置函数(如VLOOKUP、SUMIF等)。这里列举几个简单的使用Excel内置函数的示例,知道该如何使用?

示例
复制成功!
1

Sub 执行工作表内置函数()

    Dim count_品牌_1 As Integer
    Dim count_品牌_2 As Integer

    Dim i As Integer
    Dim Title_1 As Long
    Dim Title_2 As Long

    With ThisWorkbook.Sheets("数据")
        count_品牌_1 = .Range("A1").End(xlDown).Row  '引用单元格引用的方法
        Debug.Print count_品牌_1 - 1
        count_品牌_2 = Application.WorksheetFunction.CountA(.Columns(1)) '用内置函数的方法
        Debug.Print count_品牌_2 - 1

        For i = 2 To count_品牌_2               '用循环的控制语句方法
            Title_1 = Title_1 + .Cells(i, 2)
        Next i
        Debug.Print "台次合计" & Title_1

        Title_2 = Application.WorksheetFunction.Sum(.Columns(2)) '用内置函数的方法
        Debug.Print "台次合计" & Title_2

    End With
End Sub

|04 响应Application级别的事件

  这个小节了解与否都可以,不会对日常工作带来任何影响,在我看来它是最晦涩难懂、也是日常工作中很难用到的知识点(单指我本人的日常工作),有兴趣也有时间的可以先了解下,知道它用它大致能完成什么样的工作即可。

  还记得工作表事件吗?🔗WorkSheet对象,它伴随着工作表内的指定操作而自动触发程序。当时还列举了两个“激活工作表时触发”和“更改工作表时触发”的示例,当指定操作被触发时,就会执行我们写好的程序!

遗漏一点:

  在上一节:🔗Workbook工作簿对象忘记介绍“工作簿事件”,这个很久没有在工作中被使用而被忘却了。它和Worksheet工作表对象十分相似,不同的有两点:

  • 1:、它的代码存储在工作簿里的“Thisworkbook”里,而不是在某个“Sheet”工作表里;
  • 2、它的作用域是整个工作簿而不是某个工作表。

  以上是最重要的两个差异,想了解的可以自行上查找相关资料即可。了解过工作表事件,对工作簿事件也不会觉得陌生。如果觉得不好理解的也可以在本篇留言,看是否需要专门写一篇对“工作簿事件”的介绍~

  同样Application这个顶级级别的对象也有事件,事件同样可以驱动编程以便在某个操作发生时自动执行编译好的代码。例如新建工作簿时触发“提醒”、关闭工作簿时触发“自动保存”。它的作用域不仅仅是特定的工作簿了,而是整个Excel应用程序,因此需要特别注意代码的影响范围,避免不必要的副作用。

  比如你在WorkbookBeforeClose事件中写了一个“取消关闭”的操作,本意是避免当前工作簿被关闭,结果它就影响所有工作簿不能被关闭。

  作用域发生变化,也决定了代码的存储位置不同,工作表事件的代码存储在特定的工作表里、工作簿事件存储在特定工作簿的Thisworkbook里,Application事件存储在类模块里。原因是Application对象的事件无法直接在普通模块中被捕获,所以它需通过“类模块”实现。

——怎么理解“类模块”呢?我们已相当了解“模块”的定义,“模块”是VBA代码的容器,包含声明和过程,通过创建“模块”可以将相关代码组织在一起,提高代码的可读性和科维护性。而“类模块”先从什么是“类”来说:“类”就像是一个设计好的模版,用来创建有特定属性和行为的对象。

  举一个家用汽车的例子:不管是什么类型的家用汽车共同拥有的特征是,①四个轮子、②一个方向盘。这两个通用特征我们可以把它看着一个类(class),任何的对于家用汽车的描述都可以继承这个类,都共同拥有四个轮子和一个方向盘的特征。这样用代码来描述不同类型的家用汽车时,就会很简洁。比如按驱动方式可以有传统燃油和新能源,在定义一个具体对象如“小米Su7"时,只要增加一些单属它的特征“③新能源④轿跑⑤辅助驾驶⑥小米科技”,其他的特征直接“继承类”就可以了,不用所有的都要有“四个轮子、一个方向盘”这样特征描述,继承了类,相当于拥有了这个类的所有特征!

  回到VBA的“类模块”中,新建一个类模块,通过关键字WithEvents声明一个变量作为应用程序对象本身、给这个类模块编写了3个事件,那么在其他模块中,只要声明了这个类模块的变量且这个变量完成了和应用程序的绑定(相当于继承了这个类),它就会拥有这3个事件。任何对Application的操作只要触发了这三个事件,就会得到事件的响应。

  这么说可能难以理解,也通过一个示例来观察下。

示例
复制成功!
1

'新建一个类模块,重命名-类名是clsapp,编写要响应的事件代码,代码如下
'关键字WithEvents声明“APP”作为一个Application应用程序对象的变量
Public WithEvents App As Application

'事件代码有三个,当工作簿被创建时、被打开时、被关闭时,提示不同的内容
' 当新工作簿被创建时触发
Private Sub App_NewWorkbook(ByVal Wb As Workbook)
    MsgBox "新工作簿已创建:" & Wb.Name, vbInformation
End Sub

' 当任意工作簿被打开时触发
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
    MsgBox "工作簿已打开:" & Wb.Name, vbInformation
End Sub

' 在Excel关闭前触发(可取消关闭)
Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
    If MsgBox("确认关闭 " & Wb.Name & " 吗?", vbYesNo + vbQuestion) = vbNo Then
        Cancel = True  ' 取消关闭
    End If
End Sub

’新建一个模块,完成初始化类,完成绑定
Dim myApp As clsapp  '声明myaApp是clsapp变量类型

'初始化类实例,绑定Application对象,运行后完成绑定
Sub startAppEvents()
    Set myApp = New clsapp        'myApp变量,被赋值为一个新的clsapp类型
    Set myApp.App = Application   '结合类模块中的Public WithEvents App As Application, 此时myAPP绑定了Applicaiton对象,对Excel的任何的操作都会先判断是否触发了clsapp的事件代码

End Sub
Sub open_workbook()
    Workbooks.Open ("C:\Users\MagicBook\Desktop\新建 Microsoft Excel 工作表.xlsx")
End Sub

'解除绑定,运行后对Excel的操作,不在触发clsapp事件代码
Sub stopAppEvents()
    Set myApp = Nothing  ' 解除绑定
End Sub

  切记在复杂场景中,显式释放类实例是最好的,避免潜在问题。虽然Excel关闭时,Application事件监听会自动解除,无需手动干预,但是最好还是显示释放的好,就是类似示例中的解除绑定。

下一篇:VBA核心控制语句:条件判断、错误处理和程序跳转

   至此,我们已经对Excel VBA可操控的关键对象做了全面了解,知道了如何用代码操控它们来完成大批量的数据整理工作。在遇到大量的存储在工作簿\工作表\单元格的数据时,会用编程思维去考虑如何处理:“我怎么才能做到,只要处理过一次,以后都按照相同的方式和步骤来自动处理呢?”“答案:VBA代码!”

   任何的程序语言都离不开控制语句!它用来控制代码的执行流程🔗“第三篇循环”🔗第四篇“循环结构”就是控制语句的一种。我们把目标文件按照编写好的程序代码去自动处理,就要告诉它先执行什么、后执行什么?如果遇到的情况A该怎么执行,遇到情况B又该如何去做?,每当遇到错误时该怎么办?执行完成后该给我什么样的反馈?等等,这些就是控制语句在发挥作用~~。

   VBA核心控制语句包括:条件判断和循序,错误处理、程序跳转。它们通过控制程序语言的走向,来帮助我们编写有赋有逻辑的代码。关于循环和循环结构,我们第三、四两篇已经做了详细介绍,“循环”经常和“条件判断"在一起,在介绍其他控制语句时,也能一并回顾下循环的使用。

  下一篇来了解Excel VBA核心的控制语句,看它是如何帮助我们编写有逻辑的代码。

请开发者喝杯咖啡 请开发者喝杯咖啡!