【问题标题】:Get row number of first empty cell in column and store that value in other cell获取列中第一个空单元格的行号并将该值存储在其他单元格中
【发布时间】:2020-05-04 04:08:37
【问题描述】:

我想查找列中第一个空单元格的行号并将该行号存储在单元格 Z1 中。

我尝试使用以下宏代码,但它永远进入循环。 一旦它尝试在 Cell Z1 中设置值,它就会再次进入 worksheet_change 事件,然后再次进入 for 循环。

Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
    Set ws = ActiveSheet
    For Each cell In ws.Columns(3).Cells
        If IsEmpty(cell) = True Then Range("$Z$1").Value = cell.Row: Exit For
      Next cell
End Sub

请帮助解决这个问题。

谢谢

【问题讨论】:

  • 只是对可读性和一般编码实践的建议,我会避免使用单行 If 并使用缩进的 If/EndIf 块并将每个操作分开到单独的行上。我不知道这是否有助于找到罪魁祸首,但它可能。我怀疑Exit For 没有执行。

标签: excel vba


【解决方案1】:

也许这段代码有任何帮助

    Option Explicit

    Function firstEmptyCell(col As Long, Optional ws As Worksheet) As Range

    If ws Is Nothing Then
        Set ws = ActiveSheet
    End If

    Dim rg As Range
    Set rg = ws.Cells(1, col)

    If Len(rg.Value) = 0 Then
        Set rg = rg.Offset
    Else
        If Len(rg.Offset(1).Value) = 0 Then
            Set rg = rg.Offset(1)
        Else
            Set rg = rg.End(xlDown)
            Set rg = rg.Offset(1)
        End If
    End If
    Set firstEmptyCell = rg

End Function

事件代码是

    Private Sub Worksheet_Change(ByVal Target As Range)

        On Error GoTo EH

        If Target.Column <> 12 Then
            Exit Sub
        End If

        Application.EnableEvents = False
        Range("Z1").Value = firstEmptyCell(12).Row

    EH:
        Application.EnableEvents = True
    End Sub

更新:根据 cmets 关于更改事件的缺陷,可以稍微更改 firstEmptyCell 并仅使用 UDF

Function firstEmptyCellA(col As Long, Optional ws As Worksheet) As Long

    On Error GoTo EH
    If ws Is Nothing Then
        Set ws = ActiveSheet
    End If

    Application.Volatile

    Dim rg As Range
    Set rg = ws.Cells(1, col)

    If Len(rg.Value) = 0 Then
        Set rg = rg.Offset
    Else
        If Len(rg.Offset(1).Value) = 0 Then
            Set rg = rg.Offset(1)
        Else
            Set rg = rg.End(xlDown)
            Set rg = rg.Offset(1)
        End If
    End If
    firstEmptyCellA = rg.Row
    Exit Function
EH:
    firstEmptyCellA = 0

End Function

【讨论】:

  • may 失败,如果列充满数据(不是那么重要),如果范围包括列12 和另一列被删除(Intersect 可以解决这个问题),如果行被隐藏( End(xlUp)) 的缺点。你能想象一个潜艇可能失败(严重)的场景吗?
  • 你是对的,函数不会覆盖完全填充的 L 列。但如果删除包含列 12 的范围,它不会失败。目前我没有看到其他案例。
  • 我指定错误:如果删除了包含12 列之前列中的第一个单元格并且在12 列中至少包含一个单元格的范围,则may 失败。 Target.Column 是范围内第一个(最左侧)列(单元格)的列号,您使用的是 If Target.Column &lt;&gt; 12 Then.,例如用值填充范围L1:L3Z1 中的结果是 4。现在选择例如范围K2:L3 并将其删除。 Z1 应该包含2,但它仍然是4
  • 啊,你说的是可能不起作用的事件。我指的是函数firstEmptyCell。是的,没错,事件可能会失败。但这篇文章的主要问题是获取列中第一个空单元格的行号并将该值存储在其他单元格中
  • @VBasic2008:您可以稍微修改firstEmptyCell 并将其用作UDF,而不是使用Worksheet_Change 事件。但这意味着您必须制作通常不推荐使用的函数 volatile
【解决方案2】:

棘手的启用事件

只有在第 12 列 (L) 的单元格发生更改时才会触发,否则不需要。如果那里有公式,那么这将不起作用,您必须使用 Worksheet_Calculate 事件。

列中第一个空单元格的行

Option Explicit

' Row of First Empty Cell in Column
Private Sub Worksheet_Change(ByVal Target As Range)

    Const TargetCell As String = "Z1"
    Const TargetColumn As Variant = 12   ' (or "L")
    Dim rng As Range

    If Intersect(Columns(TargetColumn), Target) Is Nothing Then Exit Sub

    Application.EnableEvents = False
        Set rng = Columns(TargetColumn).Find(What:="", _
          After:=Cells(Rows.Count, TargetColumn), LookIn:=xlValues)
        If rng Is Nothing Then
            Range(TargetCell).Value = 0  ' Full column. No empty cells.
        Else
            Range(TargetCell).Value = rng.Row
        End If
    Application.EnableEvents = True

End Sub

列中最后一个非空单元格后的第一个空单元格行

Option Explicit

' Row of First Empty Cell After Last Non-Empty Cell in Column
Private Sub Worksheet_Change(ByVal Target As Range)

    Const TargetCell As String = "Z1"
    Const TargetColumn As Variant = 12   ' (or "L")
    Dim rng As Range

    If Intersect(Columns(TargetColumn), Target) Is Nothing Then Exit Sub

    Application.EnableEvents = False
        Set rng = Columns(TargetColumn).Find(What:="*", LookIn:=xlFormulas, _
          SearchDirection:=xlPrevious)
        If rng Is Nothing Then           ' Empty column. No non-empty cells.
            Range(TargetCell).Value = 1
        Else
            If rng.Row = Rows.Count Then ' Last (bottom-most) cell is not empty.
                Range(TargetCell).Value = 0
            Else
                Range(TargetCell) = rng.Offset(1).Row
            End If
        End If
    Application.EnableEvents = True

End Sub

【讨论】:

  • 这几乎工作得很好,一个问题,它返回范围内的最后一个行号。我想先清空单元格行号。因此,如果 L1、L3、L4 不为空,则应返回 2(L2),而您的代码返回 5(L5)
  • @user1041627:给你。第一个代码应该满足您的需求。我将留下第二个代码,因为这是人们经常想知道的。
【解决方案3】:

不需要循环。将此粘贴到模块中而不是工作表事件中,除非您希望在每次工作表更改时都使用它。

Sub Macro1()
    ActiveSheet.Range("Z1") = ActiveSheet.Columns(3).SpecialCells(xlCellTypeBlanks)(1).Row
End Sub

如果您在每次更改后都需要它,请将其放入工作表中。此代码不会每次都运行。它将检查 Z1 是否为空,然后输入值。然后如果 Z1 不为空,它将检查目标单元格是否在 C 列中

Private Sub Worksheet_Change(ByVal Target As Range)
Dim Rng As Range
Set Rng = ActiveSheet.Columns(3)

If IsEmpty(Range("Z1")) Then
    Range("Z1") = Rng.SpecialCells(xlCellTypeBlanks)(1).Row
Else
If Not Intersect(Range("C1:C" & Range("Z1").Value), Target) Is Nothing Then
    Range("Z1") = Rng.SpecialCells(xlCellTypeBlanks)(1).Row
End If
End If
End Sub

【讨论】:

  • 如果您的数据量很大,并且您在每次更改时都跟踪空白单元格,那么这将需要时间。虽然它不是一个循环
  • 另外,您的代码不起作用。它没有在 Cell Z1 中设置任何值。
  • 改变了答案
猜你喜欢
  • 1970-01-01
  • 2020-08-24
  • 2017-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-31
  • 1970-01-01
相关资源
最近更新 更多