我有一个VBA宏来验证用户输入的数据(我没有故意使用数据验证/条件格式化)。
我正在使用Worksheet_Change
事件来触发代码,我现在遇到的问题是,当有行更改时。我不知道它是否是删除/插入行。
无论如何要区分这两者吗?
答案 0 :(得分:17)
您可以定义范围名称,例如
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
答案 1 :(得分:4)
假设:“区分两者”意味着区分添加/删除行与任何其他类型的更改。如果您的意思是,如何判断更改是添加行还是删除行,请忽略我的答案。
在插入或删除行的情况下,target.cells.count将是该行中的所有单元格。因此,您可以使用此If语句来捕获它。请注意,我使用cells.columns.count,因为每个文件可能有所不同。如果用户选择整行并点击“删除”(删除值),它也会触发,因此您需要为此编写解决方法,但是......
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
答案 2 :(得分:4)
试试这段代码:
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设置为正确的变量。一旦完成,从那时起就应该没问题。
如果您不想使用Public变量和工作表打开事件,那么您可以在工作表上的某处使用命名范围并在那里存储行计数(lRowCount)。
答案 3 :(得分:2)
搜索了一下后决定自己解决。 在您的工作表模块(例如,VBA编辑器中的Microsoft Excel对象下的Sheet1)中插入以下内容:
- (void)loadHomeView
{
if(_isDataLoaded && !_isInterstitialDisplay && [MFPreferencesUtils firstLaunch] != nil)
{
[self performSelectorOnMainThread:@selector(displayHome) withObject:nil
waitUntilDone:NO];
}
}
答案 4 :(得分:1)
区分插入和删除的最终目的的一些最终目的将决定在识别插入或删除后您希望如何继续。以下内容可能会大幅减少,但我试图涵盖所有可能的情况。
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操作。
答案 5 :(得分:0)
根据以下模板,有两种方法可供选择。
Range
类型的模块或类模块变量。在第一种方法中,将整个感兴趣的范围分配给变量,并保存范围的大小。然后在工作表更改事件处理程序中,必须处理以下情况:
Address
property =&gt;时发生异常固定范围不再存在; 在第二种方法中,为变量分配“标记”范围(参见下面的示例),并保存范围地址,以便确定任何方向的移动或移位。然后在工作表更改事件处理程序中,必须处理以下情况::
Address
property =&gt;时发生异常固定的“标记”范围不再存在; abs(new_row - saved_row) > 0 or abs(new_col-saved_col) > 0
=&gt;固定范围被移动或移动。<强>优点:强>
UsedRange
属性未使用<强>缺点:强>
WithEvents
- 对象变量分配给Nothing
,以便取消订阅事件。以下示例显示两种方法都可行。在模块中定义:
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
操作)即可固定范围$A$10:$F$10
。插入或删除其上方的行或单元格(不要与更改单元格值混淆)。运行get_m_st
以查看固定范围的已更改地址。删除固定范围以获得&#34;所需对象&#34; get_m_st
中的例外。
答案 6 :(得分:0)
在worksheet_change事件中捕获行添加和删除。
我创建一个名为&#34; CurRowCnt&#34;的命名范围;公式:= ROWS(表1)。 使用以下命令访问VBA代码:
CurRowCnt = Evaluate(Application.Names("CurRowCnt").RefersTo)
此命名范围将始终保持&#39;之后的行数。一行插入或删除。我发现它比使用全局或模块级变量提供了更稳定的CurRowCnt,更适合编程,测试和调试。
我将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