zoukankan      html  css  js  c++  java
  • Excel VBA 学习总结 实战之惑

       VBA中很多的功能可以用内置的函数完成,其它的可以自己写函数完成。下面几点就是我在实战中遇到比较多的,而且带有一定困惑性的典型问题。

    一、返回列的名字

      这是个最简单的问题,但是有时候还是很需要的,方法比较简单,就是通过Address获取列的名字,比如“A”,“AB”等。

    Private Function columnHeader(Target As Range) As String
        columnHeader = Left$(Right$(Target.Address, Len(Target.Address) - 1), InStr(1Right$(Target.Address, Len(Target.Address) - 1), "$") - 1)
    End Function

    二、寻找实际使用的最后一行

      这个问题在实际中经常遇到,而且实现的方式也多种多样。

    使用ExecuteExcel4Macro实现
      在Excel VBA中,内置函数ExecuteExcel4Macro用于执行一些Excel 4.0中的一些函数。其中有一个特殊的函数是返回Sheet使用的最后一行的,使用很简单,如下所示:

    Sub ShowLastLine()
        MsgBox ExecuteExcel4Macro("GET.DOCUMENT(10)")
    End Sub

    Application.ExecuteExcel4Macro的用法说明:
    作用:执行一个 Microsoft Excel 4.0 宏函数,然后返回此函数的结果。返回结果的类型取决于函数的类型。
    语法:ExecuteExcel4Macro(String)
    参数:String,一个不带等号的 Microsoft Excel 4.0 宏语言函数。所有引用必须是像 R1C1 这样的字符串。如果 String 内包含嵌套的双引号,则必须写两个。例如,要运行宏函数 =MID("sometext",1,4),String 必须为 “MID(""sometext"",1,4)”。
    返回值:Variant
    说明:Microsoft Excel 4.0 宏不在当前工作簿或工作表的环境中求值。也就是说所有的引用都应该是外部引用,而且需要明确指定工作簿名。例如,要在 Book1 中执行 Microsoft Excel 4.0 宏“My_Macro”,必须使用“Book1!My_Macro()”。如果不指定工作簿名,此方法将失效。
    示例:本示例对工作表 Sheet1 上的 C3 单元格执行 GET.CELL(42) 宏函数,然后在一个消息框中显示结果。GET.CELL(42) 宏函数返回当前窗口左边界到活动单元格的左边之间的水平距离。Visual Basic 中没有与此宏函数直接等价的函数。

    Worksheets("Sheet1").Activate
    Range("C3").Select
    MsgBox ExecuteExcel4Macro("GET.CELL(42)"

    使用End属性
      在ExcelVBA中,使用End(xlUp)查找最后一行是最常使用且最为简单的方法,它假设要有一列总包含有数据(数字、文本和公式等),并且在该列中最后输入数据的单元格的下一行不会包含数据,因此不必担心会覆盖掉已有数据。但该方法有两个缺点:
    (1) 仅局限于查找指定列的最后一行。
    (2) 如果该列中最后一行被隐藏,那么该隐藏行将被视作最后一行。因此,在最后一行被隐藏时,其数据可能会被覆盖。但该列中间的隐藏行不会影响查找的结果。

    Public Function LastRowInColumn(Column As StringAs Long
         LastRowInColumn = Range(Column & Rows.Count).End(xlUp).Row
    End Function

    使用Find方法
      Find方法在当前工作有数据中进行查找,不需要指定列,也可以确保不会意外地覆盖掉已有数据。其中,参数LookIn指定所查找的类型,有三个常量可供选择,即xlValues、xlFormulas和xlComments。
    (1) 常量xlFormulas将包含零值的单元格作为有数据的单元格。(当设置零值不显示时,该单元格看起来为空,但该参数仍将该单元格视为有数据的单元格)
    (2) 常量xlValues将包含零值的单元格(如果设置零值不显示时)作为空白单元格,此时,若该单元格在最后一行,则Find方法会认为该单元格所在的行为空行,因此,该单元格中的内容可能会被新数据所覆盖。(在Excel中,选择菜单“工具”——“选项”,在打开的“选项”对话框中,选择“视图”选项卡,将其中的“零值”前的复选框取消选中,则工作表中的零值都不会显示)
      如果在参数LookIn中使用常量xlValues的话,还存在一个问题是:如果您将最后一行隐藏,则Find方法会认为倒数第二行是最后一行,此时您在最后一行的下一行输入数据,则会将实际的最后一行的数据覆盖。

    Public Function LastRow() As Long
      '使用常量xlFormulas,因为常量xlValues会忽略隐藏的最后一行
      LastRow = Cells.Find("*", LookIn:=xlFormulas, SearchDirection:=xlPrevious).Row
    End Function

    Find方法中,参数LookIn的默认值为xlFormulas。

    使用UsedRange属性
      UsedRange方法可用于在工作表中已使用区域查找最后一行,该区域包括可能以前使用过的任何单元格,但现在其中的数据被删除了,比如目前的工作表中只有第1行至第5行共5行,其它行都无数据,但在第6行中有些单元格以前使用过(可能仅仅格式化或内容清除了,总之该行现在不含有数据),那么第6行也包含在该已使用的区域中。此外,如果最后一行被隐藏,那么会将因此,使用该方法查找最后一行是无规律且不可靠的,它通常可能会得到预料不到的结果。

    Public Function LastUsedRow() As Long
      With ActiveSheet
        LastUsedRow =.UsedRange.Rows.Count + .UsedRange.Row - 1
      End With
    End Function

    使用SpecialCells方法
      也可以用SpecialCells方法实现查找最后一行,其常量xlCellTypeLastCell代表在”已使用区域”中的最后一个单元格,与UsedRange属性稍有不同的是,当您在最后一行中输入数据后,又将其删除,则此数据所在的单元格也包含在已使用的区域中,并且如果最后的行被隐藏,则将可见行的最后一行当作最后一行。

    Public Function LastUsedRow() As Long
      LastUsedRow = Cells.SpecialCells(xlCellTypeLastCell).Row
    End Function

     SpecialCells方法用于查找指定类型的值,其语法为SpecialCells(Type,Value),有两种主要的使用方式:
    (1) 若参数Type仅考虑常量,则在查找时会忽略和覆盖由公式生成的任何数据。
    (2) 若参数Type仅考虑由公式生成的数据,则在查找时会忽略和覆盖任何常量数据。
      如果参数Type是xlCellTypeConstants或者是xlCellTypeFormulas,则Value参数可使用常量决定哪种类型的单元格将被包含在结果中,这些常量值能组合而返回多个类型,其缺省设置是选择所有的常量或公式,而不管是何类型,可使用下面四个可选的常量:
    1) xlTextValues(包含文本);     2) xlNumbers(包含数字);
    3) xlErrors(包含错误值);       4) xlLogical(包含逻辑值);
      我们可以使用SpecailCells方法去找到其它特定类型的单元格所在的最后一行,下面是这些常量的一个完整的列表:
    XlCellTypeAllFormatConditions (任何格式的单元格)
    XlCellTypeAllValidation (带有数据有效性的单元格)
    XlCellTypeBlanks (所使用区域中的空白单元格)
    XlCellTypeComments (包含有批注的单元格)
    XlCellTypeConstants (包含有常量的单元格)
    XlCellTypeFormulas (包含有公式的单元格)
    XlCellTypeLastCell (已使用区域中的最后一个单元格(看下面))
    XlCellTypeSameFormatConditions (有相同格式的单元格)
    XlCellTypeSameValidation (有相同数据有效性条件的单元格)
    XlCellTypeVisible (工作表中所有可见的单元格) 

    使用CurrentRegion属性
      Range对象的CurrentRegion属性返回代表单元格所在的当前区域,即四周有空行的独立区域,因此,可使用此属性查找当前区域的最后一行。但是使用其查找最后一行的一个缺点是,必须首先选取当前区域,然后进行查找。

    三、自定义类型扩展

      VBA中扩充基本类型的基本手段就是自定义类型,主要有两种方式。

    1. 定义Type

      使用Type关键字可以定义一些简单的自定义类型,这些类型使用起来就像基本类型一样使用,直接定义和赋值。例如:

    Public Type Employee
        Name As String
        Address As String
        Salary As Double
    End Type

    Dim Manager As Employee
    Manager.Name = "Joe Smith"
    Manager.Address = "123 Main Street"
    Manager.Salary = 40000

      Type定义和使用都很简单,但是它只是一种静态的数据结构,无法完成很多高级的功能;它的显著缺点如下:

    (1).无法创建Type的实例,可以存放到数组中,但是无法存放到Collection与Dictionary中。

    (2).无法控制成员的有效范围,也就是无法验证成员的值是合法的。

    (3).只有数据,无法去定义行为。

    2. 定义Class

      正是由于Type简单但是难以胜任复杂的情景,所以当需要一些负责的处理数据的场景时,往往需要定义Class这种更强大的扩展。定义Class的方式也很简单,直接在工程中插入一个Class Module就可以了,可以在属性窗口中设置Class的名字。下面是示例:

    Private pName As String
    Private pAddress As String
    Private pSalary As Double

    Public Property Get Name() As String 
        Name = pName 
    End Property 
    Public Property Let Name(Value As String
        pName = Value 
    End Property 

    Public Property Get Address() As String 
        Address = pAddress 
    End Property 
    Public Property Let Address(Value As String
        pAddress = Value 
    End Property 

    Public Property Get Salary() As Double 
        Salary = pSalary 
    End Property 
    Public Property Let Salary(Value As Double
        If Value > 0 Then
              pSalary = Value 
        Else
            ' appropriate error code here
        End If
    End Property 

    '使用例子
    Dim Emp As CEmployee
    Set Emp = New CEmployee
    Emp.Name = "Joe Smith"
    Emp.Address = "123 Main Street"
    Emp.Salary = 40000

    Class是更为高级的对象,它带来的良好的封装性,也带来了一定的复杂性。所以只应该对那些具有行为,重用性高的数据建模成Class。

    Class需要使用New关键字去创建新实例,而且需要使用Set关键字去赋值。

    使用Type经常会遇到(传递参数,存放到集合等等)的一个错误是"Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions."。这个时候,需要定义一个Class去完成相关功能,而不是去定义一个Type。

  • 相关阅读:
    H5游戏开发之抓住小恐龙
    jQuery.noConflict()
    H5游戏开发之多边形碰撞检测
    windows命令行快速启动软件
    Spring MVC系列[1]—— HelloWorld
    过马路与异常
    超简单!一步创建自己的wifi热点~
    朱熹、王阳明与面向对象
    jenkins SVN更改密码后出现的坑爹问题
    Jenkins权限控制
  • 原文地址:https://www.cnblogs.com/dxy1982/p/2215121.html
Copyright © 2011-2022 走看看