Determining if a new table object overlaps an existing one - vba

Take a look at the code below. When I add a new table object (ListObject) to my worksheet I would like to check if the specified range doesn't overlap another existing table. Can this be easily done, or do I need to iterate through all existing tables and verify their range coordinates?
Sub TableTest()
Dim TableObj As ListObject
Dim WS As Worksheet
Set WS = ActiveSheet
' How can I check if the range isn't overlapping another table before adding it?
Set TableObj = WS.ListObjects.Add(xlSrcRange, Range("C5:F8"))
End Sub
The code above will raise an error if there is an overlapping table object in the worksheet (e.g. at range A1:D6).

Something like this, checking the known range and the new range with Intersect():
Sub TableTest()
Dim TableObj As ListObject
Dim WS As Worksheet
Set WS = ActiveSheet
With WS
If Intersect(.Range("C5:F8"), .Range("C1")) Is Nothing Then
Set TableObj = WS.ListObjects.Add(xlSrcRange, .Range("C5:F8"))
Else
Debug.Print "They are intersecting"
End If
End With
End Sub
If you want to make the code a bit more flexible, with no predefined ranges for the tables, you may check for the intersect of the range of all tables and the new range:
Sub TestMe()
Dim tableObj As ListObject
Dim ws As Worksheet
Dim checkRange As Range
Set ws = ActiveSheet
With ws
For Each tableObj In ws.ListObjects
If checkRange Is Nothing Then
Set checkRange = tableObj.Range
Else
Set checkRange = Union(checkRange, tableObj.Range)
End If
Next tableObj
If Intersect(.Range("C5:F8"), checkRange) Is Nothing Then
Set tableObj = ws.ListObjects.Add(xlSrcRange, .Range("C5:F8"))
Else
Debug.Print "They are intersecting!"
End If
End With
End Sub
In the code above checkRange is the range, which unites all the ranges, over which there is a table.

Related

Can't define workheet in VBA

Going crazy here. I use this definition of worksheet all the time. Copied every string to avoid typing errors. Still, the code below produces "Nothing" when I try to set FR worksheet. Pls help!
Sub FindReplace()
Dim FRep As Worksheet
Dim c As Range
Dim cText As TextBox
Dim i As Integer
Set FRep = ThisWorkbook.Worksheets("FindReplace")
For i = 1 To 23
cText = FRep.Cells(i, 3).Text
FRep.Cells(i, 2).NumberFormat = "#"
FRep.Cells(i, 2).Value = cText
Next i
The code as is seems correct. Make sure that "FindReplace" worksheet is in ThisWorkbook.
Also, you can try to get "FindReplace" worksheet by CodeName instead of by the name of the sheet. The advantage is that if the user changes the name of the worksheet, the CodeName will remain the same and you won't need to update your code to the new worksheet name.
Public Function GetWsFromCodeName(codeName As String, wb As Workbook) As Worksheet
Dim ws As Worksheet
For Each ws In wb.Worksheets
If ws.CodeName = codeName Then
Set GetWsFromCodeName = ws
Exit For
End If
Next ws
End Function
Add this function in your code:
Sub FindReplace()
Dim FRep As Worksheet
Set FRep = GetWsFromCodeName("YourCodeName", ThisWorkbook)

How to get the newly inserted worksheet

So I have a pivottable and in column C there is field for which I am showing details for each record using this
For i=7 to 10
DATA.Range("C" & i).ShowDetail = True
Set wN = ThisWorkbook.Worksheets(1)
Next i
Now it works fine but the problem is Set wN = ThisWorkbook.Worksheets(1) assigns the wN the first worksheet but DATA.Range("C" & i).ShowDetail = True sometimes inserts the new worksheet which has the details at 1st or 2nd position. Now I want to know which was the new worksheet which was inserted and assign wN to it.
Do I have to make an array or list which keeps record of existing worksheets and then check which is the new one everytime? or there is an easy way to determine which is the newest worksheet in an workbook irrespective of the position.
Look at the Activesheet. ShowDetail creates the new sheet and activates it - so Set wn=ActiveSheet should work.
Sub Test()
Dim c As Range
Dim wrkSht As Worksheet
With ThisWorkbook.Worksheets("Sheet2").PivotTables(1)
For Each c In .DataBodyRange.Resize(, 1)
c.ShowDetail = True
Set wrkSht = ActiveSheet
Debug.Print wrkSht.Name
Next c
End With
End Sub
This link to Jon Peltiers page on Pivot Tables should be a massive help... https://peltiertech.com/referencing-pivot-table-ranges-in-vba/
The code shown does not add a worksheet, it sets wN to whatever sheet has index 1 (The second sheet created).
Try wN.Name = "C"& i & " field" to help figure out when each sheet is being created.
Open a new Workbook. Then run this code a few times:
Option Explicit
Public Sub TestMe()
Dim wsNew As Worksheet
Worksheets.Add After:=Worksheets(Worksheets.Count)
Set wsNew = Worksheets(Worksheets.Count)
Debug.Print wsNew.Name
End Sub
You would see, that wsNew is always the last one added. Thus with Worksheetes(Worksheets.Count) you may access it.
Edit:
If you want to know the name of the last added Worksheet, without adding After:, then use collection to remember all the worksheets you had before and simply compare them with the new collection. Run this code a few times:
Option Explicit
Public Sub TestMe()
Dim wsCollection As New Collection
Dim lngCounter As Long
Dim strName As String
Dim blnNameFound As Boolean
Dim ws As Worksheet
For Each ws In Worksheets
wsCollection.Add ws.Name
Next ws
Worksheets.Add
For Each ws In Worksheets
blnNameFound = False
For lngCounter = 1 To wsCollection.Count
If wsCollection.Item(lngCounter) = ws.Name Then
blnNameFound = True
End If
Next lngCounter
If Not blnNameFound Then Debug.Print ws.Name
Next ws
End Sub
The complexity is O².

Copying, pasting and deleting codependent ranges to different sheets in VBA

I have a sheet, let's call it sheetA. I have a range of fields in that sheet (rangeA) which has formulas that determine two more ranges in the same sheet. Let's call them rangeB and rangeC. Once these are determined, I want to copy rangeB and rangeC into sheets sheetB and sheetC respectively. Once that is done, I would like to delete rangeA. A reset of sorts so that I can enter new values in that range manually and repeat the process.
I want to have a function/button that can accomplish this. I have tried the following:
Private Sub TransferPuzzleButton1_Click()
FirstOperation
GetFirstEmptyCell1 "sht As Worksheet", "row As Long"
SecondOperation
GetFirstEmptyCell1 "sht As Worksheet", "row As Long"
ClearCell
End Sub
Sub FirstOperation()
Dim sourceSht As Worksheet: Set sourceSht = ThisWorkbook.Worksheets(1)
Dim destSht As Worksheet: Set destSht = ThisWorkbook.Worksheets(2)
GetFirstEmptyCell(destSht, 1).Resize(25).Value = sourceSht.Range("A1:A27").Value
End Sub
Function GetFirstEmptyCell1(sht As Worksheet, row As Long) As Range
Set GetFirstEmptyCell = sht.Cells(1, sht.Columns.Count).End(xlToLeft)
If Not IsEmpty(GetFirstEmptyCell) Then Set GetFirstEmptyCell = GetFirstEmptyCell.Offset(, 1)
End Function
Sub SecondOperation()
Dim sourceSht As Worksheet: Set sourceSht = ThisWorkbook.Worksheets(1)
Dim destSht As Worksheet: Set destSht = ThisWorkbook.Worksheets(3)
GetFirstEmptyCell(destSht, 1).Resize(2).Value = sourceSht.Range("C1:C2").Value
End Sub
Function GetFirstEmptyCell2(sht As Worksheet, row As Long) As Range
Set GetFirstEmptyCell = sht.Cells(1, 2).End(xlToLeft) '
If Not IsEmpty(GetFirstEmptyCell) Then Set GetFirstEmptyCell = GetFirstEmptyCell.Offset(, 1)
End Function
Sub ClearCell()
Dim sourceSht As Worksheet: Set sourceSht = ThisWorkbook.Worksheets(1)
sourceSht.Range("F7:I10").Clear
sourceSht.Range("C1:C2").Clear
End Sub
It seems I'm mangling the beginning Sub calls somehow
With GetFirstEmptyCell1 "sht As Worksheet", "row As Long" you are trying to call a sub that takes arguments. The argument typing is done in the definition of the sub, not in the statement that calls the sub.
When you call the sub you need to supply the parameters that it expects, in this case a worksheet and a long.
So, get your data ready before you call the sub. Abbreviated:
Private Sub TransferPuzzleButton1_Click()
Dim mySheet As Worksheet
dim myNumber as Long
Set mySheet = ThisWorkbook.Worksheets("DemoSheet")
MyNumber = 1000
' now call the function
GetFirstEmptyCell1 mySheet, myNumber
End Sub
Function GetFirstEmptyCell1(sht As Worksheet, row As Long) As Range
Set GetFirstEmptyCell = sht.Cells(1, sht.Columns.Count).End(xlToLeft)
If Not IsEmpty(GetFirstEmptyCell) Then Set GetFirstEmptyCell = GetFirstEmptyCell.Offset(, 1)
End Function
By the way, the names you use inside the function are not consistent. GetFirstEmptyCell1 vs GetFirstEmptyCell.

VBA: Setting a range object using Sheets("sheet name").Rows(1).Find

I have a set of code that looks something like this, where I am searching row 1 for a specific word and want to return the column number where it is found:
Dim MyRng As Range
Dim MySht As Worksheet
Set MySht = ThisWorkbook.Sheets("Numbers")
Set MyRng = MySht.Rows(1).Find("Header 3" etc...)
FindSeriesColumn = MyRng.Column
It seems logical that I should be able to eliminate the need for defining a worksheet object and should be able to do the following:
Dim MyRng As Range
Set MyRng = ThisWorkbook.Sheets("Numbers").Rows(1).Find("Header 3" etc...)
FindSeriesColumn = MyRng.Column
However, the Set MyRng ... line results in an error. What is improper about using ThisWorkbook.Sheets("Sheet Name").Rows(1).Find...?
Use this form:
Sub fhksadf()
Dim MyRng As Range
Set MyRng = ThisWorkbook.Sheets("Numbers").Rows(1).Find(what:="Header 3")
FindSeriesColumn = MyRng.Column
MsgBox FindSeriesColumn
End Sub
EDIT#1:
It is easier to define the range being examined, and start the Find() after the beginning of that range:
Sub fhksadf()
Dim MyRng As Range
Dim temp As Range
Set temp = ThisWorkbook.Sheets("Numbers").Rows(1).Cells
Set MyRng = temp.Find(what:="Header 3", After:=temp(1))
FindSeriesColumn = MyRng.Column
MsgBox FindSeriesColumn
End Sub

Looping though a list of sheets

I am trying to loop through a list of (randomly named) worksheets and for each calculate the last row and then loop though all the rows and execute some code.
I've tried the code below but I'm getting Invalid Procedure Call or Argument. I've modified in all kinds of ways but it's driving me crazy...thank you in advance.
Sub myloop()
Dim ws As Variant
Dim WsArray As Variant
Dim rcount As Integer
WsArray = Array("mysheet1", "mysheet2", "mysheet3", "mysheet4")
With ThisWorkbook
For Each ws In WsArray
rcount = .Worksheets(ws).Cells("A1").End(xlDown).Row
For i = 1 To rcount
If ...Then
End If
Next
Next
End With
End Sub
#Santosh makes a great point about finding the last row. As for the original question, in any Workbook there exists a Worksheets collection that might actually be easier to loop through depending on your structure:
Dim ws As Worksheet
For Each ws In Worksheets
'match worksheet names if necessary using ws.Name...
'
'do other cool stuff...
'
'wrap it all up
Next ws
Try this. Instead of going to cell A1 and going down to find the last row its better to go to last cell (rows.count) and then go up.
Sub myloop()
Dim ws As Variant
Dim WsArray As Variant
Dim rcount As Integer
WsArray = Array("mysheet1", "mysheet2", "mysheet3", "mysheet4")
With ThisWorkbook
For Each ws In WsArray
With .Worksheets(ws)
rcount = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For i = 1 To rcount
'If .. Then
End If
Next
Next
End With
End Sub