Selecting all data in other worksheet VBA - vba

I am new to VBA and I am trying to write some code that selects gets the range of all of the cells in a different worksheet than the current one, prints it in a message box (for testing purposes), and returns to the original sheet. I intend to use this range to create a pivot table in the new sheet, and so In the end it needs to be of the form "R1C1:R929C25" To do this, I used code that I found here:
How do you select the entire Excel sheet with Range using Macro in VBA?
This is the code that I have so far:
Sheets("My_Sheet_Name").Cells.Select
MsgBox (Str(Range(Cells.Address)))
Sheets(Sheets.Count).Select
However, this returns an error:
Run-time error '1004':
Select method of Range class failed.
Edit:
I have gotten the code to get the range, yet how do I make it into the form "R1C1:R929C25"?
COMPLETED WORKING CODE:
Dim rng As Range
Set rng = Sheets("My_Sheet").UsedRange
Dim rc_range
rc_range = "R" & Trim(Str(rng.Rows.Count)) & "C" & Trim(Str(rng.Columns.Count))
MsgBox (rc_range)
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _
"My_Sheet!R1C1:" & rc_range, Version:=xlPivotTableVersion10). _
CreatePivotTable TableDestination:=ActiveSheet.Name + "!R4C4",
TableName:=inputValue + "_PivotTable" _
, DefaultVersion:=xlPivotTableVersion10

You have two options. First, you can use a variable to store the range upon which you hope to act. If it's a one time procedure, variables aren't necessary, but if it's a repetitive task, or involves multiple references, use a variable.
Option two is to refer directly to the other range without a variable. Again, use this if it's only a one time action, such as showing a messagebox.
First example:
Set rng = Sheets("My_Sheet").Cells
msgbox(rng.address)
Second example:
msgbox(Sheets("My_Sheet").Cells.Address)
One thing to note, the Sheet.Cells property returns all of the cells on the applicable sheet. So it would always be $A$1:$XFD$1048576
If you only want cells that are actually storing data, try using the UsedRange property, such as:
Set rng = Sheets("My_Sheet").UsedRange
msgbox(rng.address)

Per the updates and the changes in the comments, I'm including the full set of code to declare a range, and use that range in a Pivot Table to be used in a new sheet. Please note, I didn't perform full error checking, so that should be added to the code. For example, check if the new sheet name already exists, or if the pivot table name already exists, and if so, handle accordingly.
Sub CreatePivotFromDynamicRange()
Dim wsNew As Worksheet
Dim rngData As Range
Dim rngDestination As Range
'Add a new worksheet to store the pivot table
'[NOTE] if sheet named "PivotSheet" already exists it will throw error
Set wsNew = Worksheets.Add()
wsNew.Name = "PivotSheet"
'Define the range with the data needed for the pivot
Set rngData = Sheets("My_Data").UsedRange
'define the pivot destination
Set rngDestination = wsNew.Range("A5")
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:=rngData, _
Version:=xlPivotTableVersion14).CreatePivotTable tabledestination:=rngDestination, _
tablename:="NewPivot", defaultVersion:=xlPivotTableVersion14
End Sub

You can't select cells from a sheet you are not actively on, but you can reference them. What do you want to do with the cells in "My_Sheet_Name"
If you want the range address from a different sheet and don't know the last row or last column then you can use this code.
Sub SelectLastCellInInSheet()
Dim sh As Worksheet
Dim Rws As Long, Col As Integer, r As Range, fRng As Range
Set sh = Sheets("My_Sheet_Name")
With sh
Set r = .Range("A1")
Rws = .Cells.Find(what:="*", after:=r, SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Col = .Cells.Find(what:="*", after:=r, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set fRng = .Range(.Cells(1, 1), .Cells(Rws, Col))
End With
MsgBox fRng.Address
End Sub

Related

VBA Includes empty cells in table formatting

Very new to VBA.
What I am trying to do:
1) Copy sheet from (source) workbook to active (master) workbook
2) Delete unnecessary columns of the copied data in the master workbook
3) Select cells with data in the master workbook and format as table (this is where I am stuck)
Sub first_sub()
'Open user Raw Data workbook
Workbooks.Open Filename:= _
"C:\Users\" & Environ("UserName") & "\Downloads\File.xls"
'Copy user Raw Data sheet to Data Master sheet
Workbooks("File.xls").Sheets("Records").Copy _
Before:=Workbooks("Macro Main.xlsm").Sheets(1)
'Close user Raw Data
Workbooks("File.xls").Close
End Sub
Sub Format()
Sheets("Records").Range("D:G,I:K,M:Y,AA:AA,AF:AM").EntireColumn.Clear
Sheets("Records").Range("D:G,I:K,M:Y,AA:AA,AF:AM").EntireColumn.Delete
End Sub
Sub MakeTable()
Dim tbl As ListObject
Dim rng As Range
Set rng = Range(Range("A1"), Range("A1").SpecialCells(xlLastCell))
Set tbl = ActiveSheet.ListObjects.Add(xlSrcRange, rng, , xlYes)
tbl.TableStyle = "TableStyleMedium3"
End Sub
The problem is in point 3. For some reasons, VBA selects columns up to "AM", even though I deleted them in previous sub, and as a result - I have the table full of empty columns all the way up to AM. How to solve this? Thank you in advance.
Check how .SpecialCells(xlLastCell) works: Range.SpecialCells Method (Excel)
I think your range variable rng is referencing from range Range("A1") to the last used cell in the column range AM
Set rng = Range(Range("A1"), Range("A1").SpecialCells(xlLastCell))
Try adding this line after to check if the range is actually targeting your desired range:
rng.select
To solve this problem you can try:
Set rng = Range("A1").CurrentRegion
Or finding the last cell in column A...
Code:
Sub MakeTable()
Dim tbl As ListObject
Dim rng As Range
Set rng = Range("A1").CurrentRegion
Set tbl = ActiveSheet.ListObjects.Add(xlSrcRange, rng, , xlYes)
tbl.TableStyle = "TableStyleMedium3"
End Sub

ActiveSheet.AutoFilter.Range sometimes returns table's AutoFilter based on selected cell

Edit: The best I can do is to find the last used cell and then offset it by one row and one column. This will fail if the user is using the entire sheet, but I'm willing to take that chance. I can select this cell temporarily and then switch back to the user's previous cell.
Public Function getCellOutsideUsedRange(ws As Worksheet) As Range
Dim rng As Range
Set rng = getLastCellOnSheet(ws)
If rng.Row = ws.Rows.CountLarge Then
If rng.Column = ws.Columns.CountLarge Then
Set getCellOutsideUsedRange = Nothing
Else
Set getCellOutsideUsedRange = rng.offset(0, 1)
End If
ElseIf rng.Column = ws.Columns.CountLarge Then
If rng.Row = ws.Rows.CountLarge Then
Set getCellOutsideUsedRange = Nothing
Else
Set getCellOutsideUsedRange = rng.offset(1, 0)
End If
Else
Set getCellOutsideUsedRange = rng.offset(1, 1)
End If
End Function
Public Function getLastCellOnSheet(ByRef ws As Worksheet) As Range
Set getLastCellOnSheet = ws.Cells.Find(What:="*", After:=Cells(1, 1), LookIn:=xlFormulas, LookAt:= xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
End Function
If you have a cell selected inside a table, ActiveSheet.AutoFilter.Range returns the autofilter applied to the table, not the worksheet. Example:
Sub test()
Dim af As AutoFilter
Range("C1").Select
Set af = ActiveSheet.AutoFilter
MsgBox af.Range(1).Value2
MsgBox af.Parent.Name
Range("C12").Select
Set af = ActiveSheet.AutoFilter
MsgBox af.Range(1).Value2
MsgBox af.Parent.Name
End Sub
The documentation doesn't really help me. How can I ensure that I can get to the sheet autofilter, even if the user happens to have a cell selected in a table, without having to cycle through cells until I get one outside a table?
Context: I have a short macro that returns a list of all the filters that have been applied to the sheet. In summary, it looks through the worksheet and any tables and, for each, uses the AutoFilter.Range property to get the range that is filtered, selects the top row of this range to get the filter headings, and then checks each cell in the filter headings for any criteria. If a user has a cell selected in a table, they won't get back any worksheet filters.
In general, using Select in VBA and depending on it is a bad practice. How to avoid using Select in Excel VBA
Concerning the AutoFilters - you are allowed to have only one AutoFilter per worksheet, which is outside a table. Thus, it is accessible through ActiveSheet.AutoFilter.Range, which could be Nothing or Not Nothing.
Concerning the autofilters in the tables, you may simply loop through the tables and check one by one whether the AutoFilter is there or not:
Sub TestMe()
Dim cntAutoFilters As Long
Dim cnt As Long
Dim wks As Worksheet
Dim tbl As ListObject
Set wks = Worksheets(1)
If Not wks.AutoFilter.Range Is Nothing Then
Debug.Print wks.AutoFilter.Range.Address
End If
For Each tbl In wks.ListObjects
If Not tbl.AutoFilter Is Nothing Then
Debug.Print tbl.Range.Address
End If
Next tbl
End Sub
Concerning the MSDN documentation:
AutoFilter.Range is a property of AutoFilter.
Range.AutoFilter is a method.
Edit:
It really seems that if you have a cell selected within a table in a worksheet, then the AutoFilter outside this table somehow cannot be referred.

I'm getting Subscript out of range (Error 9)

the below code is not working I'm getting Subscript out of range (Error 9)
Sub advnextract()
Sheets.Add(Before:=ActiveSheet).Name = "Resultado"
Set extractto = ThisWorkbook.Worksheets("Resultado").Range("A5:G5")
Selection.AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=Range( _
"J1:J2"), CopyToRange:=extractto, Unique:=False
End Sub
Need help this is supposed to run a advanced filter and paste the result in the newly created sheet, the original table in the selection has data ranging from A1 to G11
As stated in the comments, the creation of a worksheet causes that to gain focus. Also you need to copy the titles to the sheet so excel knows where to put the values:
Sub advnextract()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rng As Range
Dim extractto as range
Set rng = Selection 'It is better to set an actual range instead of Selection.
'Also Selection must have at least 7 columns or this will error.
'It also needs to include the column headers in the Selection.
Sheets.Add(Before:=ActiveSheet).Name = "Resultado"
Set extractto = ThisWorkbook.Worksheets("Resultado").Range("A5:G5")
extractto.Value = rng.Rows(1).Value
rng.AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=ws.Range( _
"J1:J2"), CopyToRange:=extractto, Unique:=False
End Sub
After trial and error and some insight into some logic here is the final result
Sub advnextract()
Dim rng As Range
Set rng = Selection
Sheets.Add(Before:=Sheets("Hoja1")).Name = "Resultado"
rng.AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=Sheets("Hoja1").Range("J1:J2"), _
CopyToRange:=Sheets("Resultado").Range("A1"), Unique:=False
Sheets("Resultado").Activate
Columns("A:G").EntireColumn.AutoFit
Range("A1").Select
End Sub
I know it can be improved to be more efficient but for some reason I can't explain this is the code that works for me.

"Extract range is not defined": advanced filter

I am trying to run this simple code in VBA but it keeps giving me "extract range is not defined" error:
Private Sub CommandButton1_Click()
'Dim rng As Range
Dim RowLast As Long
Dim perporig As Workbook
count = 0
Set perporig = Workbooks.Open("\\Etnfps02\vol1\DATA\Inventory\Daily tracking\perpetual.xlsx", , ReadOnly)
With perporig.Sheets("perpetual")
.AutoFilterMode = False
RowLast = .Cells(Rows.count, "A").End(xlUp).row
'Set rng = .Range("C4:C" & RowLast)
Range("A3:J" & RowLast).AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=Range("N1:N6"), Unique:=False 'have also tried Range("A3:J3"), doesn't work.
End With
ThisWorkbook.Sheets("myperpetual").Range("A5", "J5").PasteSpecial xlPasteValues
End Sub
I am trying to copy from a file with data starting from A4:J4 to some A16000:J16000. I have to filter the values in column C to a range I have specified in the worksheet in range N1:N6.
FYI: Table header A4 is empty, and B4:J4 have relevant headers in them.
Also please let me know if my copy-paste method is wrong, or for some reason wouldn't work as expected.
EDIT: I also tried adding a header in column A , i.e. cell A3. Still doesn't work.
My range N1:N6 is a list of numbers, but I am sure the mistake lies there. It doesn't specify which column to apply filter on.
Once you are inside the With perporig.Sheets("perpetual") ... End With block, preface all Range objects and Range.Cells property references with a period (aka . or full stop).
With perporig.Sheets("perpetual")
.AutoFilterMode = False
RowLast = .Cells(Rows.count, "A").End(xlUp).row
'Set rng = .Range("C4:C" & RowLast)
.Range("A3:J" & RowLast).AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=.Range("N1:N6"), Unique:=False
End With
Note .Range("C4:C"... and .Range("N1:N6") prefaced with a period. This indicates that the parent worksheet of the range(s) are defined by the worksheet named in the With ... End With statement. Without it, Excel is making a guess as to which worksheet the range belongs to; typically this would be the ActiveSheet property.

Effective Looping Checkup VBA

Summary: My company has two different spreadsheets with many policies on each. They want me to match up policies by a policy ID and transfer all the old notes from the old spreadsheet to the new spreadsheet.
Reasoning: my issue is not with not understanding how to do this, but the BEST way to do this. Since joining StackOverflow I've been told things I should and shouldn't do. I've been told different times it is better to use a For Each loop instead of a simple Do loop. Also, I've been told I shouldn't use .Select heavily (but I do).
How I Would Normally Do It: I would normally just use a Do Loop and go through the data just selecting the data with .Find and using ActiveCell and when I wanted to interact with other Columns in that current row I would just use ActiveCell.Offset(). I tend to love .Select and use it all the time, however on this project I'm trying to push myself out of the box and maybe change some bad coding habits and start using what may be better.
Question: How would I go about doing the equivalent of an ActiveCell.Offset() when I'm using a For Each loop?
My Code So Far: **Questions/Criticisms welcome
Sub NoteTransfer()
transferNotes
End Sub
Function transferNotes()
Dim theColumn As Range
Dim fromSheet As Worksheet
Dim toSheet As Worksheet
Dim cell As Range
Dim lastRow As Integer
Set fromSheet = Sheets("NotesFrom")
Set toSheet = Sheets("NotesTo")
With fromSheet 'FINDING LAST ROW
lastRow = .Range("B" & .Rows.Count).End(xlUp).Row
End With
Set theColumn = fromSheet.Range("B5:B" & lastRow)
For Each cell In theColumn 'CODE FOR EACH CELL IN COLUMN
If cell.Text = "" Then
'do nothing
Else
With toSheet 'WANT TO FIND DATA ON THE toSheet
Cells.find(What:=cell.Text, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
End With
End If
Next cell
End Function
Example
Bottom of the sheet
First, your question:
Question: How would I go about doing the equivalent of an ActiveCell.Offset() when I'm using a For Each loop?
Doesn't make much sense given the code you posted. It's a very general question, and would need some context to better understand. It really depends on your loop. If you are looping a contiguous range of cells from the ActiveCell then you could say ...
For each cel in Range
myValue = ActiveCell.Offset(,i)
i = i + 1
Next
To get the column next to each cell in the loop. But in general I wouldn't call that great programming. Like I said, context is important.
As far as your code goes, see if this makes sense. I've edited and commented to help you a bit. Oh yeah, good job not using Select!
Sub transferNotes() '-> first no need for a function, because you are not returning anything...
'and no need to use a sub to call a sub here as you don't pass variables,
'and you don't have a process you are trying to run
Dim theColumn As Range, cell As Range '-> just a little cleaner, INMHO
Dim fromSheet As Worksheet, toSheet As Worksheet '-> just a little cleaner, INMHO
Dim lastRow As Integer
Set fromSheet = Sheets("NotesFrom")
Set toSheet = Sheets("NotesTo")
With fromSheet ' -> put everything you do in the "fromSheet" in your With block
lastRow = .Range("B" & .Rows.Count).End(xlUp).Row 'FINDING LAST ROW
Set theColumn = .Range("B5:B" & lastRow)
theColumn.AutoFilter 1, "<>"
Set theColumn = theColumn.SpecialCells(xlCellTypeVisible) '-> now you are only looping through the cells are that are not blank, so it's more efficient
For Each cell In theColumn
'-> use of ActiveCell.Offset(), it's not ActiveCell.Offset(), but it uses Offset
Dim myValue
myValue = cell.Offset(, 1) '-> gets the cell value in the column to the right of the code
'WANT TO FIND DATA ON THE toSheet
toSheet.Cells.Find(What:=cell.Text, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Next cell
End With
End Sub
This is my suggestion so far.
Function transferNotes()
Dim SourceColumn As Range
Dim fromSheet As Worksheet
Dim toSheet As Worksheet
Dim cell As Range
Dim lastRow As Long '<--changed to Long
Set fromSheet = Sheets("NotesFrom")
Set toSheet = Sheets("NotesTo")
With fromSheet 'FINDING LAST ROW
lastRow = .Range("B" & .Rows.Count).End(xlUp).Row
End With
Set SourceColumn = fromSheet.Range("B5:B" & lastRow)
For Each cell In SourceColumn 'CODE FOR EACH CELL IN COLUMN
If cell.Value = "" Then 'the .Text property can
'make for some confusing errors.
'Try to avoid it.
'nothng to search for
Else
With toSheet 'WANT TO FIND DATA ON THE toSheet
Dim destRng As Range
Set destRng = .Range("A:A").Find(What:=cell.Value)
If Not destRng Is Nothing Then
.Cells(destRng.Row, <your mapped column destination>)
= fromSheet.Cells(cell.Row,<your mapped column source>)
' you can either repeat the above line for all of your non-contiguous
'sections of data you want to move from sheet to sheet
'(i.e. if the two sheets are not arranged the same)
'if the two sheets are aranged the same then change
'the .cells call to call a range and include
'the full width of columns
Else
'nothing was found
End If
End With
End If
Next cell
End Function