How to record problem records when deleting Table rows by『On Error』statement - vba

Sub FilterRows(Field As String, Values As Collection)
Dim FinalRow As Long
Dim KeepValue As Variant
Dim Table As ListObject
Dim TableRange As Range
Dim i As Integer
With Worksheets("原始資料")
FinalRow = .Cells(Rows.Count, 1).End(xlUp).row
On Error GoTo ErrorHandler
Set Table = .ListObjects("Table1")
Set TableRange = Table.Range
For i = TableRange.Rows.Count To 1 Step -1
For Each KeepValue In Values
If Intersect(TableRange.Rows.Item(i), Table.ListColumns(Field).Range).Value <>
CStr(KeepValue) Then
TableRange.Rows.Item(i).Delete
End If
Next KeepValue
Next
End With
Exit Sub
ErrorHandler:
Debug.Print Err.Number; ":" & Err.Description
If Table Is Nothing Then
Worksheets("原始資料").ListObjects.Add(xlSrcRange, Worksheets("原始資料").Range("A1:AK" &
FinalRow), , xlYes).Name = "Table1"
Set Table = Worksheets("原始資料").ListObjects("Table1")
Else
Debug.Print "Error Record ID:" & Intersect(TableRange.Rows.Item(i),
Table.ListColumns("Record ID").Range).Value<===I'm not able to do This Line...
End If
Resume Next
End Sub
Hi Folks,
the purpose of this code is to delete table row based on a condition.
Somehow I got Error in certain table rows in the process of deletion, and I would like to know which row is fail.so I wrote this line :
『Debug.Print "Error Record ID:" & Intersect(TableRange.Rows.Item(i), Table.ListColumns("Record ID").Range).Value』.
There may be variable scope issue but I don't know how to pass varaible to Errorhandler. Or there is another good way to achieve what I want to do?
Thanks!

While testing your code, it's usually a good idea not to let the error handler handle you problem but let the runtime engine stop immediately at the line throwing the error. You can achieve this by either comment out the line On Error Goto, or go to the VBA Options and select "Break on all errors" (in the General Tab).
Scope of variables is the whole subroutine, so that shoudn't be a problem.
You should use the error handler to catch only unexpected errors. The logic of defining the table if it doesn't exist should be done at the point where you assign the variable:
On Error Resume Next
Set Table = .ListObjects("Table1")
On Error GoTo ErrorHandler
If table Is Nothing Then
With Worksheets("原始資料")
Set table = .ListObjects.Add(xlSrcRange, .Range("A1:AK" & FinalRow), , xlYes)
End With
table.Name = "Table1"
End If
Set TableRange = Table.Range
I am almost sure your problematic row is 1: You cannot delete the header row of a table.

Related

Check for the current error handling in VBA

In VBA error handling is done by on error statement.
I want to temporarily change the error handling and then go back to the previous behavior afterward. How would it be possible to check the current error handling and store it in a variable (I couldn't find anything in the references)?
'set the error handling to s.th. "on error... "
'some code with the regular error handling
'change the error handling to "on error ..." (regardless of what it was before)
'some code with the new error handling
'change back to the previous error handling
'some code with the regular error handling
Background: I needed to do a is nothing check on a Variant array to exclude empty object indexes from being used, but is nothing applied to an array index that holds a value throws an exception, so I temporary wanted to change the error handling to on error resume next. Eventually is solved this using a different approach but I'm still wondering if I can determine the current error handling somehow during runtime Here's the question and answer to my original problem.
EDIT: I know I can check my previous code manually to find out what type of error handling has been used. However I want to avoid that (to save time).
I suppose as a workaround I could set an additional variable with the state which I can then check for the current state, although this will result in quite a bit of overhead. Something like this:
Dim errorHandling as String
errorHandling = "resumeNext"
on error resume next
'some code
'changing the error handling temp.
'some other code
'changing the error handling to it's previous state
if errorhandling = "resumeNext" then
On Error Resume Next
elseif errorhandling = "GoToErrorhandler" then
On Error GoTo errorhandler
End If
'Rest of the code
Read/Write to Array
Option Explicit
Sub ReadWriteArrayExample()
Dim myArray() As Variant: ReDim myArray(1 To 10)
Dim i As Long
Dim n As Long
' Fill the array.
For i = 1 To 10
n = Application.RandBetween(0, 1)
If n = 1 Then ' write a random number between 1 and 10 inclusive
myArray(i) = Application.RandBetween(1, 10)
'Else ' "n = 0"; leave the element as-is i.e. 'Empty';do nothing
End If
Next i
' Debug.Print the result.
Debug.Print "Position", "Value"
For i = 1 To 10
If Not IsEmpty(myArray(i)) Then ' write the index and the value
Debug.Print i, myArray(i)
'Else ' is empty; do nothing
End If
Next i
End Sub
Error Handling
Sub ErrorHandling()
Const ProcName As String = "ErrorHandling"
On Error GoTo ClearError ' enable error trapping
' Some code
On Error Resume Next ' defer error trapping
' Some tricky code
On Error GoTo ClearError ' re-enable error trapping
' Some Code
ProcExit:
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Sub

Subtract two ranges and clear the contents from result

I'm trying to subtract RangeA - RangeA+offset to get a new range. After this i need to clear all the values within it. My problem is that the variable columnrange is empty and i'm unable to realize what i'm doing wrong.
Dim rng1 As String
Dim rangeA As Range
Dim columnrange As Range
Dim clearrange As Range
rng1 = TextBoxA.Value
If Not IsNull(RangeboxA.Value) Then
On Error Resume Next
Set rangeA = Sheets("Plan1").Range(RangeboxA.Value)
rangeA.Select
Selection.Copy
rangeA.Offset(0, rng1).Select
ActiveSheet.Paste
columnrange = rangeA.Resize(rangeA.Rows.Count, rangeA.Columns.Count + rng1).Value
columnrange.Select
On Error Resume Next
If rangeA Is Nothing Then MsgBox "Verificar informação A"
End If
This code moves a user-defined range by a user-defined amount.
Sub RemoveRangeOverlap()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Plan1")
Dim rngOffset As Integer
Dim rangeA As Range, rangeB As Range
Dim cellRange() As String
On Error GoTo ErrHandle
rngOffset = CInt(TextBoxA.Value)
If RangeBoxA.Value <> "" Then
Set rangeA = ws.Range(RangeBoxA.Value) 'Set old range
cellRange = Split(CStr(RangeBoxA.Value), ":") 'Set start/ending cells
ReDim Preserve cellRange(LBound(cellRange) To UBound(cellRange))
Set rangeB = ws.Range(ws.Range(cellRange(0)).Offset(0, rngOffset), _
ws.Range(cellRange(1)).Offset(0, rngOffset)) 'set new range
rangeA.Copy rangeB 'copy new range
Application.CutCopyMode = xlCopy 'remove marching ants
If rangeA.Columns.Count <= rngOffset Then 'remove old values
rangeA.Clear
Else: ws.Range(ws.Range(cellRange(0)), _
ws.Range(cellRange(1)).Offset(0, rngOffset - rangeA.Columns.Count)).Clear
End If
Else: MsgBox "Missing target range input.", vbCritical, "Insufficient Data"
End If
ErrHandle:
If Err.Number = 438 Then
MsgBox "Invalid range format in range input box." & vbNewLine & _
"Proper range format example: A1:A1", vbCritical, "Error 438"
ElseIf Err.Number = 13 Then
MsgBox "Only numbers may be input as the range offset amount", _
vbCritical, "Error 13: Type Mis-match"
ElseIf Err.Number = 5 Then Exit Sub
Else: Err.Raise Err.Number
End If
End Sub
How the code works:
The first thing we have set up is information control from user-defined values. To accomplish this (which can also be done with If Then statements to prevent the errors from ever occurring in the first place) I've included an error handling line at the end. We know what 3 errors we expect to get depending on what the user provides us with.
Error 438 will occur if the user tries to set RangeBoxA's value as a non-range value.
Error 13 will occur if the user tries to input anything that isn't a number as the offset value.
Error 5 will occur because I'm bad at error handling and I'm not sure why it's occuring.. It loops my error statement at the end after whichever error is thrown (being a non-vba error).
Next we split up the range supplied by the user into two 'cells'. Using this we can apply some simple math to show where the copy destination will be as well as delete the proper amount of old range values.
If the number of columns is greater than the user supplied offset, then the new and old ranges will overlap. Some simple math will remove the old cells while preserving the new one's
If the number of columns is less than the user supplied offset, delete all of the old cells because they won't be overlapping.
Let me know if this works for you.

Unable to get worksheet the VLookup property of the WorksheetFunction Class error

Private Sub TextBox2_AfterUpdate() 'Badge Number
On Error GoTo Err
Dim tbl As ListObject, fndStr As String
Set tbl = Sheet9.ListObjects("EmployeeList")
fndStr = Trim(Me.TextBox2.Value)
MsgBox fndStr
If fndStr <> "" Then
Me.TextBox3.Value = Application.WorksheetFunction.VLookup(fndStr, tbl, 2, False) '<-- Error Line
End If
Exit Sub
Err:
MsgBox Err.Description
End Sub
I have a table named as "EmployeeList" and I am doing simple vlookup using Badge number but I am getting the error for unknown reason. I know there are similar questions asked before but I did read before posting this.
As you can clearly see the table name in the image and Entered value that is 10 for the first parameter on vlookup function but it doesn't returns any value but gives error. Don't know what's wrong.
'I tried this as well
Me.TextBox3.Value = Application.WorksheetFunction.VLookup(fndStr, Sheet9.Range("A1:F" & Rows.Count), 2, False) '<-- Error Line
'And this
Me.TextBox3.Value = Application.WorksheetFunction.VLookup(fndStr, Sheet9.Range("EmployeeList"), 2, False) '<-- Error Line
Also for unknown reason I can't do
Application.Vlookup as well
Like when I do Application.V
Vlookup doesn't shows up in the list.
There are two issues.
The first, you have tried to solve, is that you need a Range as Arg2in Vlookup. Since your tbl is a ListObject. you could simply use tbl.Range, see ListObject.Range Property.
The second is, that Vlookup will not find strings in a column of numbers. And your first column is a column of numbers. So you need to convert the string into number.
Me.TextBox3.Value = Application.WorksheetFunction.VLookup(CDbl(fndStr), tbl.Range, 2, False)
should work.
Please find the code below, i have used evaluate method to get vlookup result.
Private Sub TextBox2_AfterUpdate()
Dim fndStr As String
On Error GoTo Err_Desc
fndStr = Trim(Me.TextBox2.Value)
MsgBox fndStr
If fndStr <> "" Then
'// Using Eval method
Me.TextBox3.Value = Evaluate("=VLOOKUP(" & fndStr & ",EmployeeList[#All],2,0)")
End If
Exit Sub
Err_Desc:
MsgBox Err.Description
End Sub

How to know if cell exist

I searched but could not find the way to do this.
I want to know if this is possible
if ActiveDocument.Range.Tables(1).Cell(i, 2) present
do some stuff
end if
This can work:
Dim mycell as cell
On Error Resume Next 'If an error happens after this point, just move on like nothing happened
Set mycell = ActiveDocument.Range.Tables(1).Cell(1, 1) 'try grabbing a cell in the table
On Error GoTo 0 'If an error happens after this point, do the normal Error message thingy
If mycell Is Nothing Then 'check if we have managed to grab anything
MsgBox "no cell"
Else
MsgBox "got cell"
End If
If you want to test for multiple cells in a loop, don't forget to set mycell=nothing before trying again.
(Instead of the mycell variable way, you could also check to see if an error has happened when you tried to use the cell. You could use If err > 0 Then to do that. But that way is a bit more unstable in my experience.)
Specific answer to OP's specific question:
If .Find.Found Then 'this is custom text search, has nothing to do with specified cell exist.
Set testcell = Nothing
On Error Resume Next
Set testcell = tbl.Cell(i, 6)
On Error GoTo 0
If Not testcell Is Nothing Then
tbl.Cell(i, 2).Merge MergeTo:=tbl.Cell(i, 3)
End If
End If
This means:
If your .find does whatever... then
Try grabbing the cell in question (the 4 rows: Set...Nothing, On error..., Set..., On Error...)
If we could grab the cell, then merge cells
Read up a bit on the error handling in VBA, the On Error statement. In VBA, there is no Try...Catch. This is what we can do instead.
I hope this clears it up.
For reference, I'll post a full code here:
Sub test()
Dim tbl As Table
Dim testcell As Cell
Set tbl = ActiveDocument.Range.Tables(1)
For i = 1 To 6
Set testcell = Nothing
On Error Resume Next
Set testcell = tbl.Cell(i, 6)
On Error GoTo 0
If Not testcell Is Nothing Then
tbl.Cell(i, 2).Merge MergeTo:=tbl.Cell(i, 3)
End If
Next i
End Sub
Posting the solution as a function for reference...
Function cellExists(t As table, i As Integer, j As Integer) As Boolean
On Error Resume Next
Dim c As cell
Set c = t.cell(i, j)
On Error GoTo 0
cellExists = Not c Is Nothing
End Function

Determine if ActiveCell is in a specific table

I am trying to determine by VBA in Excel 2013 if the ActiveCell is not just in any table, but in a specific table.
Below is the code as is, but only detects ActiveCell being in any table. The commented out line is what I'm looking for, but obviously it doesn't work.
...
Set rng = Intersect(.EntireRow, ActiveCell.ListObject.DataBodyRange)
'Set rng = Intersect(.EntireRow, ActiveCell.ListObjects("myTable").DataBodyRange)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "Please select the cell of a row within the consensus input table.", vbCritical, "Delete Evaluator"
Else
...
Any suggestions on the right syntax for this?
Thanks!
To test if ActiveCell is in the body of Table1:
Sub qwerty()
If Intersect(ActiveCell, ActiveSheet.ListObjects("Table1").DataBodyRange) Is Nothing Then
MsgBox "activecell not in Table1"
Else
MsgBox "activecell in Table1"
End If
End Sub
Generally, we're interested in work being performed within the DataBodyRange of a table and Excel provides us a shortcut for that area of a Table. For a table named "myTable", you directly access the DataBodyRange in code using [myTable].
Thus, for inclusive table location testing of the ActiveCell one could test as follows:
If Not Intersect(ActiveCell, [myTable]) Is Nothing Then
A more general solution, adaptable to other tables
Sub Demo()
Dim r As Range
Dim lo As ListObject
Set r = ActiveCell
Set lo = r.ListObject
If Not lo Is Nothing Then
Select Case lo.Name
Case "Table1"
If r.Row = lo.Range.Row Then
MsgBox "In Table1 Header"
Else
MsgBox "In Table1 Body"
End If
Case "SomeOtherTable"
'...
End Select
Else
MsgBox "Not in any table"
End If
End Sub
A Range object has a ListObject property that will return the table of the Range. All you have to do is to test if the cell is in any table:
If ActiveCell.ListObject Is Nothing Then
...
and see if it is in your specific table:
If ActiveCell.ListObject.Name = "MyTable" Then
...
and you're done!
Much cleaner than using Application.Intersect(...). chris neilsen's answer alludes to this as well.
I use the following line of code:
ActiveCell.ListObject.Name
or sub:
Sub IsActiveCellInTable()
'If active cell in table then get name'
Dim strTblName As String
'Disable error checking'
On Error Resume Next
strTblName = ActiveCell.ListObject.Name
'Reset error checking'
On Error GoTo 0
If strTblName <> "" Then
MsgBox "Cell (" & ActiveCell.Address & ") is included in: " & strTblName
Else
MsgBox "Cell (" & ActiveCell.Address & ") is not included in table."
End If
End Sub