确定用户是否添加或删除行

我有一个VBAmacrosvalidation用户input的数据(我没有使用数据validation/有条件的格式)。

我正在使用Worksheet_Change事件来触发代码,我现在面临的问题是,当有行更改。 我不知道是否是删除/插入行。

有没有区分这两个?

您可以定义一个范围名称,例如RowMarker =$A$1000

然后,您的更改事件中的此代码将存储此标记的位置与其之前的位置,并报告任何更改(然后存储新的位置)

 Private Sub Worksheet_Change(ByVal Target As Range) Static lngRow As Long Dim rng1 As Range Set rng1 = ThisWorkbook.Names("RowMarker").RefersToRange If lngRow = 0 Then lngRow = rng1.Row Exit Sub End If If rng1.Row = lngRow Then Exit Sub If rng1.Row < lngRow Then MsgBox lngRow - rng1.Row & " rows removed" Else MsgBox rng1.Row - lngRow & " rows added" End If lngRow = rng1.Row End Sub 

试试这个代码:

 Private Sub Worksheet_Change(ByVal Target As Range) Dim lNewRowCount As Long ActiveSheet.UsedRange lNewRowCount = ActiveSheet.UsedRange.Rows.Count If lOldRowCount = lNewRowCount Then ElseIf lOldRowCount > lNewRowCount Then MsgBox ("Row Deleted") lOldRowCount = lNewRowCount ElseIf lOldRowCount < lNewRowCount Then MsgBox ("Row Inserted") lOldRowCount = lNewRowCount End If End Sub 

在ThisWorkBook模块中添加这个:

 Private Sub Workbook_Open() ActiveSheet.UsedRange lOldRowCount = ActiveSheet.UsedRange.Rows.Count End Sub 

然后在它自己的模块中:

 Public lOldRowCount As Long 

代码假定你有行1中的数据。注意第一次运行它时,你会得到一个错误的结果,这是因为代码需要将lRowCount设置为正确的variables。 一旦完成,应该没问题。

如果您不想使用公共variables和工作表打开事件,那么您可以在工作表的某处使用命名范围,并在那里存储行计数(lRowCount)。

假设 :“区分这两个”意味着区分添加/删除行与其他任何types的变化。 如果你的意思是,如何判断这个更改是添加行还是删除行,那么在下面忽略我的答案。

在插入或删除一行的情况下,target.cells.count将是该行中的所有单元格。 所以你可以使用这个If语句来捕获它。 注意我使用了cells.columns.count,因为每个文件可能都不一样。 它也会触发,如果用户select一个完整的行,并点击“删除”(擦除值),所以你需要编码解决scheme,虽然…

 Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range) If Target.Cells.Count = Cells.Columns.Count Then MsgBox "Row added or deleted" End If End Sub 

找了一点后决定自己解决。 在您的工作表模块中(例如,VBA编辑器中的Microsoft Excel对象下的Sheet1)插入以下内容:

 Private usedRowsCount As Long 'use private to limit access to var outside of sheet 'Because select occurs before change we can record the current usable row count Private Sub Worksheet_SelectionChange(ByVal Target As Range) usedRowsCount = Target.Worksheet.UsedRange.rows.count 'record current row count for row event detection End Sub 'once row count recorded at selection we can compare the used row count after change occurs 'with the Target.Address we can also detect which row has been added or removed if you need to do further mods on that row Private Sub Worksheet_Change(ByVal Target As Range) If usedRowsCount < Target.Worksheet.UsedRange.rows.count Then Debug.Print "Row Added: ", Target.Address ElseIf usedRowsCount > Target.Worksheet.UsedRange.rows.count Then Debug.Print "Row deleted: ", Target.Address End If End Sub 

一些区分插入和删除的最终目的最终将决定一旦确定了插入或删除之后如何继续。 以下可能大大减less,但我试图涵盖每一个可能的情况。

 Private Sub Worksheet_Change(ByVal Target As Range) On Error GoTo bm_Safe_Exit Application.EnableEvents = False Application.ScreenUpdating = False Dim olr As Long, nlr As Long, olc As Long, nlc As Long With Target.Parent.Cells nlc = .Find(what:=Chr(42), after:=.Cells(1), LookIn:=xlValues, lookat:=xlPart, _ SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column nlr = .Find(what:=Chr(42), after:=.Cells(1), LookIn:=xlValues, lookat:=xlPart, _ SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row Application.Undo 'undo the last change event olc = .Find(what:=Chr(42), after:=.Cells(1), LookIn:=xlValues, lookat:=xlPart, _ SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column olr = .Find(what:=Chr(42), after:=.Cells(1), LookIn:=xlValues, lookat:=xlPart, _ SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row Application.Repeat 'redo the last change event End With If nlr <> olr Or nlc <> olc Then Select Case nlr Case olr - 1 Debug.Print "One (1) row has been deleted" Case Is < (olr - 1) Debug.Print (olr - nlr) & " rows have been deleted" Case olr + 1 Debug.Print "One (1) row has been inserted" Case Is > (olr + 1) Debug.Print (nlr - olr) & " rows have been inserted" Case olr Debug.Print "No rows have been deleted or inserted" Case Else 'don't know what else could happen End Select Select Case nlc Case olc - 1 Debug.Print "One (1) column has been deleted" Case Is < (olc - 1) Debug.Print (olc - nlc) & " columns have been deleted" Case olc + 1 Debug.Print "One (1) column has been inserted" Case Is > (olc + 1) Debug.Print (nlc - olc) & " columns have been inserted" Case olc Debug.Print "No columns have been deleted or inserted" Case Else 'don't know what else could happen End Select Else 'deal with standard Intersect(Target, Range) events here End If bm_Safe_Exit: Application.EnableEvents = True Application.ScreenUpdating = True End Sub 

本质上,这个代码按行确定最后一个单元格和最后一个单元格。 然后解除最后的操作并再次检查。 通过比较这两个结果,可以确定是否已经插入/删除了行/列。 一旦进行了四次测量,它就会重做上一次操作,以便可以处理任何其他更标准的Worksheet_Change操作。

有两种基于以下模板的另一种方法。

  1. 定义Rangetypes的模块或类模块variables。
  2. 通过使用绝对地址将其分配给variables来“固定”一个特殊范围,并保存其地址或大小(取决于方法)。
  3. 要确定用户操作的子types,请在工作表更改事件处理程序中使用variables进行操作。

在第一种方法中,将整个感兴趣的范围分配给variables并且保存范围的大小。 然后在工作表更改事件处理程序中,必须处理以下情况:

  • 访问Address属性=>固定范围不再存在时发生exception;
  • 更改单元格的地址低于固定范围=>插入=>更新variables
  • 固定范围新大小与保存的不同 (更小=>删除了某些东西,更大=>插入了某些东西)。

在第二种方法中,将“标记”范围分配给variables(参见下面的示例),并保存范围地址以确定任何方向的移动或移动 。 然后在表单更改事件处理程序中,必须处理以下情况::

  • 访问Address属性=>固定的“标记”范围不再存在时发生exception;
  • 改变的单元格的地址低于“标记”范围=>插入=>更新variables
  • 任何方向都有差异,例如abs(new_row - saved_row) > 0 or abs(new_col-saved_col) > 0 =>固定范围已移动或移位。

优点:

  • 用户定义的名称不被使用
  • UsedRange属性不被使用
  • 固定范围根据用户操作进行更新,而不是假定用户操作不会发生在第1000行以下。

缺点:

  • variables必须在工作簿打开事件处理程序中分配,以便在工作表更改事件处理程序中使用它。
  • 在工作簿closures事件处理程序中,必须将variables和WithEventsvariables的对象分配给Nothing才能取消订阅事件。
  • 由于它们改变范围的值而不是交换行,所以不可能确定sorting操作。

以下示例显示这两种方法都可以工作。 在模块中定义:

 Private m_st As Range Sub set_m_st() Set m_st = [$A$10:$F$10] End Sub Sub get_m_st() MsgBox m_st.Address End Sub 

然后运行set_m_st (只需在游标中放置一个光标,然后调用Run action)来引脚范围$A$10:$F$10 。 插入或删除上面的行或单元格(不要混淆改变单元格的值)。 运行get_m_st来查看固定范围的更改地址。 删除固定范围,以在get_m_st获得“Object required”exception。

捕获worksheet_change事件中的行添加和删除。

我创build了一个名为“CurRowCnt”的命名范围; 公式:= ROWS(Table1)。 在VBA代码中访问:

 CurRowCnt = Evaluate(Application.Names("CurRowCnt").RefersTo) 

这个命名的范围将始终保持“插入或删除行”之后的行数。 我发现它比使用全局或模块级别的variables提供更稳定的CurRowCnt,更适合编程,testing和debugging。

我将CurRowCnt保存到一个自定义文档属性,再次出于稳定的目的。

 ThisWorkbook.CustomDocumentProperties("RowCnt").Value = Evaluate(Application.Names("CurRowCnt").RefersTo) 

我的Worksheet_Change事件结构如下所示:

 Dim CurRowCnt as Double CurRowCnt = Evaluate(Application.Names("CurRowCnt").RefersTo) Select Case CurRowCnt '' ########## ROW(S) ADDED Case Is > ThisWorkbook.CustomDocumentProperties("RowCnt").Value Dim r As Range Dim NewRow as Range ThisWorkbook.CustomDocumentProperties("RowCnt").Value = _ Evaluate(Application.Names("CurRowCnt").RefersTo) For Each r In Selection.Rows.EntireRow Set NewRow = Intersect(Application.Range("Table1"), r) 'Process new row(s) here next r '' ########## ROW(S) DELETED Case Is < ThisWorkbook.CustomDocumentProperties("RowCnt").Value ThisWorkbook.CustomDocumentProperties("RowCnt").Value = _ Evaluate(Application.Names("CurRowCnt").RefersTo) 'Process here '' ########## CELL CHANGE 'Case Is = RowCnt 'Process here '' ########## PROCESSING ERROR Case Else 'Should happen only on error with CurRowCnt or RowCnt 'Error msg here End Select