Copying, pasting and deleting codependent ranges to different sheets in VBA - 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.

Related

How to loop through only the worksheets included in a list?

I am trying to use VBA to loop through worksheets in my file but only those that are included in a list on a control worksheet, e.g.
Worksheet List
When I try to look up the worksheet name in this list, it does not recognise the worksheet name as a string.
Current code below:
I create a function to vlookup on the list:
Public Function IsInRunList(WsName As Variant, RunList As Range) As Boolean
If Application.VLookup(WsName, RunList, 1, False) = WsName Then
IsInRunList = True
End If
End Function
Then I call this function in my subroutine:
Dim Ws As Worksheet
For Each Ws In ThisWorkbook.Worksheets
If IsInRunList(Ws.Name, Range("Run_List").Columns(j)) Then
I get a mismatch error for Ws.Name here.
Any ideas?
Thanks.
Try the next approach, please:
Sub iterateBetweenSheetInList()
Dim sh As Worksheet
For Each sh In ActiveWorkbook.Worksheets
Select Case sh.Name
Case "Sheet1", "Sheet2", "Sheet4", "Sheet7"
Debug.Print sh.UsedRange.Rows.Count
'your code can do here whatever you need...
End Select
Next
End Sub
Or a version to take the sheets name from a range (in column X:X in the code example). You did not show us in which column the sheets list exists:
Sub iterateBetweenSheetInListBis()
Dim sh As Worksheet, ws As Worksheet, arrSh As Variant, El As Variant
Set sh = ActiveSheet
'adapt the next range with the lettr of your column where the sheets name exists:
arrSh = sh.Range("X2:X" & sh.Range("X" & Rows.Count).End(xlUp).row).Value
For Each El In arrSh
Set ws = Worksheets(El)
Debug.Print ws.UsedRange.Rows.Count
'do here whatever you need...
Next
End Sub
Application.VLookup returns a Range when successful and an error if not (same behavior as in Excel). An error is not a string, it's a special type that you can check with IsError.
Change your checking routine to something like:
Public Function IsInRunList(WsName As Variant, RunList As Range) As Boolean
Dim res As Variant
res = Application.VLookup(WsName, RunList, 1, False)
IsInRunList = Not IsError(res)
End Function

Split Worksheets

Currently this macro splits worksheets based on a cell.
It works well, however I am putting it as a button on a different page but this selects the active page, I want it to run this macro on a specific sheet.
Sub SplitToWorksheets_step4()
'Splits the workbook into different tabs
Dim ColHead As String
Dim ColHeadCell As Range
Dim icol As Integer
Dim iRow As Long 'row index on Fan Data sheet
Dim Lrow As Integer 'row index on individual destination sheet
Dim Dsheet As Worksheet 'destination worksheet
Dim Fsheet As Worksheet 'fan data worksheet (assumed active)
Again:
'ColHead = Worksheets("Diversion Report") 'this ask the user to enter a colunm name
ColHead = InputBox("Enter Column Heading", "Identify Column", [c1].Value) 'this ask the user to enter a colunm name
If ColHead = "" Then Exit Sub
Set ColHeadCell = Rows(1).Find(ColHead, LookAt:=xlWhole)
If ColHeadCell Is Nothing Then
MsgBox "Heading not found in row 1"
GoTo Again
End If
Set Fsheet = ActiveSheet
icol = ColHeadCell.Column
'loop through values in selected column
For iRow = 2 To Fsheet.Cells(65536, icol).End(xlUp).Row
If Not SheetExists(CStr(Fsheet.Cells(iRow, icol).Value)) Then
Set Dsheet = Worksheets.Add(after:=Worksheets(Worksheets.Count))
Dsheet.Name = CStr(Fsheet.Cells(iRow, icol).Value)
Fsheet.Rows(1).Copy Destination:=Dsheet.Rows(1)
Else
Set Dsheet = Worksheets(CStr(Fsheet.Cells(iRow, icol).Value))
End If
Lrow = Dsheet.Cells(65536, icol).End(xlUp).Row
Fsheet.Rows(iRow).Copy Destination:=Dsheet.Rows(Lrow + 1)
Next iRow
End Sub
Function SheetExists(SheetId As Variant) As Boolean
' This function checks whether a sheet (can be a worksheet,
' chart sheet, dialog sheet, etc.) exists, and returns
' True if it exists, False otherwise. SheetId can be either
' a sheet name string or an integer number. For example:
' If SheetExists(3) Then Sheets(3).Delete
' deletes the third worksheet in the workbook, if it exists.
' Similarly,
' If SheetExists("Annual Budget") Then Sheets("Annual Budget").Delete
' deletes the sheet named "Annual Budget", if it exists.
Dim sh As Object
On Error GoTo NoSuch
Set sh = Sheets(SheetId)
SheetExists = True
Exit Function
NoSuch:
If Err = 9 Then SheetExists = False Else Stop
End Function
Change your Sub to:
Sub SplitToWorksheets_step4(SheetName as String)
and in the line:
Set Fsheet = ActiveSheet
to:
Set Fsheet = Worksheets(SheetName)
on a different page but this selects the active page, I want it to run
this macro on a specific sheet.
Well that is simple enough.
Set your Worksheet Object to a specific Sheet.Name - eg:
Dim Fsheet As Worksheet: Set Fsheet = Sheets("Your sheet name")
In a more practical usage, you could for example pass the sheet name as a procedure argument:
Private Sub SplitToWorksheets_step4(ByVal sheetName as String)
Dim fsheet as Worksheet: Set fsheet = Sheets(sheetName)
' ... do something
End Sub
Last but not least a practical way to apply a macro for every Worksheet:
Private Sub for_every_ws()
Dim ws as Worksheet
For Each ws In ThisWorkbook.Sheets
ws.Range("A1") = "I was here!" ' i.e.
Next ws
End Sub

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)

Determining if a new table object overlaps an existing one

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.

Private Sub User-Defined Type Not Defined Range Sheet

First post. I have the relatively simple code below and am getting a
User-defined type not defined
error. I know that the stand alone code works when I place it into one Sub but for various reasons I want to split it out so that in my larger workbook I can just call on the second sub rather than having to copy and paste the whole loop multiple times. The purpose of the code is to autosize the specified range in excel.
Sub letsGo()
Dim rng As Range
Dim sht As Worksheet
Set rng = ThisWorkbook.Sheets("Sheet1").Range("Range1")
Set sht = ThisWorkbook.Sheets("Sheet1")
Call whyDoesntThisWork(sht, rng)
End Sub
Private Sub whyDoesntThisWork(rangeSheet As Sheet, rangeTable As Range)
Dim Col As Range
Dim reSize As Range
For Each Col In rangeTable.Columns
If Col.Hidden = False Then
Set reSize = rangeSheet.Range(rangeSheet.Cells(rangeTable.Row, Col.Column), rangeSheet.Cells(rangeTable.Rows.Count, Col.Column)) reSize.Columns.autoFit
End If
Next Col
End Sub
You have two different data types:
Private Sub whyDoesntThisWork(rangeSheet As Sheet, rangeTable As Range)
rangeSheet is a Sheet, but when you call it, you pass:
Call whyDoesntThisWork(sht, rng)
sht is of type WorkSheet
That's your inconsistency. I recommend you change your definition to:
Private Sub whyDoesntThisWork(rangeSheet As WorkSheet, rangeTable As Range)
Change rangeSheet As Sheet to rangeSheet As Worksheet