excel vba: Range gives Argument Not Optional error message - excel

In excel vba I'm tryin to select a range of values starting in Cell"O2", (O from Oyster) down to the end of the sheet,
I'm trying:
Range("O2", Range.End(xlDown))
But that fails with Argument Not Optional. What am i doing wrong?
I'm using Excel 2010.

Don't use xlDown Declare your Objects and then work with it.
Use this
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim LRow As Long
Dim rng As Range
'~~> Change this to the relevant sheet name
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
'~~> Find last row in Col O which has data
LRow = .Range("O" & .Rows.Count).End(xlUp).Row
'~~> This is your range
Set rng = .Range("O2:O" & LRow)
With rng
'~~> Whatever you want to do
End With
End With
End Sub

To select the range from O2 to the last filled cell in that column, you could use:
Range("O2", Range("O2").End(xlDown)).Select
But that has a few problems, including the fact that it will "stop" at any blanks, and that you should avoid using Select unless absolutely necessary. Also, you should get in the habit of qualifying your ranges, e.g., specifying which worksheet they're in. Given all that, I propose something like this, assuming you wanted to turn the cells in the range red:
Sub test()
Dim LastRow As Long
Dim ws As Excel.Worksheet
Set ws = ActiveSheet
With ws
LastRow = .Range("O" & .Rows.Count).End(xlUp).Row
.Range("O2:O" & LastRow).Interior.Color = vbRed
End With
End Sub

Maybe you need
Range("O2", Range("O2").End(xlDown)).Select
?

Related

Write a dynamic sum formula vba that takes range from another sheet

screenshot of code
I am trying to calculate sum in cell "I13" of sheet2 with inputs based on the dynamic range.
Formula
range("I13").formula= "=sum('sheet1'!A1:A3)"
works but the range can be dynamic. For this I have used lr to identify the last row in the range
lr=cells(rows.count,1).end(xlup).row
Now, I want to modify the above formula such that in place of A3, it takes last cell. i.e. A&lr
Have tried using range("I13").formula= "=sum('sheet1'!A1:A"&lr)", but it results in error
Sub MMM()
Windows("Template.xlsx").activate
sheets("sheet1").select
range("a1").select
lr=cells(rows.count,1).end(xlup).row
sheets("sheet2").select
'this code works. But want dynamic range
'range("I13").formula = "= SUM('sheet1'!A1:A3)"
range("I13").formula = "= sum('sheet1'!A1:A&lr)"
End Sub
you can try to define the variable:
Option Explicit ' It should be used when you define variable
Sub MMM()
Dim lr as Range ' Define variable
Windows("Template.xlsx").activate
sheets("sheet1").select
range("a1").select
lr=cells(rows.count,1).end(xlup).row
sheets("sheet2").select
range("I13").formula = "= sum('sheet1'!A1:A&lr)"
End Sub
You have to join the string for the formula like this:
"=SUM('Sheet1'!A1:A" & lastRow & ")"
Alternatively:
If you set the whole range to be summed then you can use the Address of this range. The External-parameter returns the sheet name as well.
Sub MMM()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsSource As Worksheet: Set wsSource = wb.Worksheets("Sheet1")
Dim wsTarget As Worksheet: Set wsTarget = wb.Worksheets("Sheet2")
Dim rgDataToSum As Range
With wsSource
Set rgDataToSum = .Range("A1", .Cells(.Rows.Count, 1).End(xlUp))
End With
wsTarget.Range("I13").Formula = "=SUM(" & rgDataToSum.Address(True, True, External:=True) & ")"
End Sub

Copy range based on a condition

I would like to write a macro to copy only a range of Cells that has data and ignore cells with a value of NA
I have built a helper tool to ensure data gathered from multiple sources are placed in the correct corresponding columns, then from there I copy and paste those columns into a master worksheet. The Range these values are populated in are A3 through R and In column R is a Vlookup.
I want to write a macro so I can press a macro enabled button to copy the cells in that range and end where the Vlookup stops returning a value. So far I have been able to write it so it copies to the end of the Vlookup but it is still gathering results that include the formula in R.
Currently Written:
Sub Copy()
Dim ws As Worksheet
Set ws = Sheets("Sheet1")
ws.Range(ws.[A:R], ws.Cells(Rows.Count, "R").End(xlUp)).Copy
End Sub
One way.
Use Autofilter.
Filter out the values which are not #N/A
Copy the filtered range in one go.
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim rng As Range, filteredrng As Range
Dim lrow As Long
Set ws = Sheet1 '<~~ Change this to the relevant sheet
With ws
.AutoFilterMode = False
lrow = .Range("R" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A3:R" & lrow)
With rng
.AutoFilter Field:=18, Criteria1:="<>#N/A"
Set filteredrng = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
If Not filteredrng Is Nothing Then
'~~> COPY RANGE CODE HERE
End If
.AutoFilterMode = False
End With
End Sub

How can i apply remove alphabet function on active sheet loop?

Function StripChar(Txt As String) As String
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "\D"
StripChar = .Replace(Txt, "")
End With
End Function
So i am trying to apply this function on bottom range via loop through cells
Sub Alphabetremove()
Dim ws As Worksheet
Dim Lastrow As Integer
Set ws = ActiveSheet
Lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Range("F2:F" & Lastrow).Select
With Selection
.Value = StripChar(.Value)
End With
End Sub
The issue is that you cannot run the function .Value = StripChar(.Value) on a range at once (as you tried) but only on a single cell. Therfore you need to loop from row 2 to LastRow and apply the function to each single cell.
Also note that row counting variables need to be of type Long because Excel has more rows than Integer can handle. Actually I recommend always to use Long instead of Integer as there is no benefit in using Integer in VBA.
Also if you set your worksheet to a variable ws you need to use this variable for all .Cells and .Range objects otherwise this is useless.
Option Explicit
Public Sub Alphabetremove()
Dim ws As Worksheet
Set ws = ActiveSheet 'better define a workseet by name unless you use the code for multiple worksheets:
'Set ws = ThisWorkbook.Worksheets("MySheet")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim Cell As Range
For Each Cell In ws.Range("F2:F" & LastRow)
Cell.Value = StripChar(Cell.Value)
Next Cell
End Sub
Finally I highly recommend you to read How to avoid using Select in Excel VBA and apply this to all of your code.

VBA - Get name of last added sheet

I am looking for a code to get the name of the last added sheet to Excel.
I have tried this...
Sub test()
Dim lastAddedSheet As Worksheet
Dim oneSheet As Worksheet
With ThisWorkbook
Set lastAddedSheet = .Sheets(1)
For Each oneSheet In .Sheets
If Val(Mid(oneSheet.CodeName, 6)) > Val(Mid(lastAddedSheet.CodeName, 6)) Then
Set lastAddedSheet = oneSheet
End If
Next oneSheet
End With
MsgBox lastAddedSheet.Name & " was last added."
End Sub
But it does not really work.
You can't reliably know what sheet was last added, because a sheet can be inserted before or after any existing sheet in a workbook, see Sheets.Add documentation.
Unless you're the one adding it. In which case, all you need to do is capture the Worksheet object returned by the Add method:
Dim newSheet As Worksheet
Set newSheet = wb.Worksheets.Add
Debug.Print newSheet.Name
Extracting the digits from the CodeName isn't going to be reliable either - especially if you assume that every sheet's code name begins with 5 letters. On a German machine, the CodeName of what we see as Sheet1 would be Tabelle1 - but then again the role of that digit is strictly to ensure uniqueness of the names of the VBComponent items in the VBA project, and none of it says it has anything to do with any sort of ordering.
As per #MathieuGuindon his answer, I can't think of any "simple" way to safely return the name of the latest added sheet. However if you willing to sacrifice some designated space in your project to store CodeNames you could try to utilize the Workbook_NewSheet event.
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Dim lr As Long
With Sheets("Blad1")
lr = .Cells(.Rows.Count, "A").End(xlUp).Row + 1
.Cells(lr, 1) = ActiveSheet.CodeName
End With
End Sub
Obviously you need to optimize this to add names when adding sheets during runtime. In this simplified example I manually added the existing sheet "Blad1", and upon adding new sheets, the list grew.
When deleting you can utilize the SheetBeforeDelete event, like so:
Private Sub Workbook_SheetBeforeDelete(ByVal Sh As Object)
Dim ws As Object
Dim lr As Long, x As Long
Dim rng1 As Range, rng2 As Range, cl As Range
With Sheets("Blad1")
lr = .Cells(.Rows.Count, "A").End(xlUp).Row + 1
Set rng1 = .Range("A2:A" & lr)
For Each ws In ActiveWindow.SelectedSheets
For Each cl In rng1
If cl = ws.CodeName Then
If Not rng2 Is Nothing Then
Set rng2 = Union(rng2, cl)
Else
Set rng2 = cl
End If
End If
Next cl
Next ws
End With
If Not rng2 Is Nothing Then
rng2.Delete
End If
End Sub
Now to get the latest added sheet we can refer to the last cell in our designated range:
Sub LastAdded()
Dim lr As Long
With ThisWorkbook.Sheets("Blad1")
lr = .Cells(.Rows.Count, "A").End(xlUp).Row
Debug.Print "Last added sheet is codenamed: " & .Cells(lr, 1)
End With
End Sub
My take on it is that it would be safest to use the CodeName since they are least likely to get changed and are unique. We can also safely keep using our rng variable since there will always be at least one worksheet in your project (and that might just be the designated one if you protect it). Working in this project will now keep track of latest added worksheet.
Sheets could be a Chart or a Worksheet.
You could try use Worksheets instead of Sheets in your code.
sub test()
Dim lastAddedSheet As Worksheet
Dim oneSheet As Worksheet
With ThisWorkbook
Set lastAddedSheet = .WorkSheets(1)
For Each oneSheet In .WorkSheets
If Val(Mid(oneSheet.CodeName, 6)) > Val(Mid(lastAddedSheet.CodeName, 6)) Then
Set lastAddedSheet = oneSheet
End If
Next oneSheet
End With
MsgBox lastAddedSheet.Name & " was last added."
End Sub

Creating and sorting a dynamic table

I have a sub where i want to create a table so that i can sort it afterwards (one of the columns must be in descending order).
The sub works when i have the workbook open and on the correct worksheet.
When i try to run the code without having to look at the worksheet at the same time, the following problem pops up:
"Method 'Range' of object_global' failed"
"Can't execute code in break mode"
This is what my sub looks like. The row is static but the column length changes from time to time.
Sub create_the_table_investeringsforeninger()
' Best used when row length is static
Dim sht As Worksheet
Dim LastRow As Long
Dim Lrow As Long
Set sht = ThisWorkbook.Worksheets(2)
'Refresh UsedRange
ThisWorkbook.Worksheets(2).UsedRange
Lrow = Range("D" & Rows.Count).End(xlUp).Row
sht.ListObjects.Add(SourceType:=xlSrcRange, Source:=sht.Range("A2:F2" & Lrow)).Name = "Investeringsforeninger"
'Sort Range "Investeringsforeninger"
Range("investeringsforeninger").Sort Key1:=Range("F2"), order1:=xlDescending, Header:=xlYes
End Sub
The whole of my code is supposed to run without me having to open the document first (for this i am using a vbs document).
You are missing references to your Worksheet, therefore it only works when it is the active sheet. I deleted your UsedRange line as it does nothing.
Sub create_the_table_investeringsforeninger()
' Best used when row length is static
Dim sht As Worksheet
Dim LastRow As Long
Dim Lrow As Long
Set sht = ThisWorkbook.Worksheets(2)
Lrow = sht.Range("D" & Rows.Count).End(xlUp).Row
sht.ListObjects.Add(SourceType:=xlSrcRange, Source:=sht.Range("A2:F2" & Lrow)).Name = "Investeringsforeninger"
'Sort Range "Investeringsforeninger"
sht.Range("investeringsforeninger").Sort Key1:=sht.Range("F2"), order1:=xlDescending, Header:=xlYes
End Sub

Resources