i have an excel/vba issue which seems to occur in excel2010 but not excel2016. for me it is a non comprehensible conversion between a1 and r1c1 notation.
i have a range that is dynamic
Dim rng As Range
rng = Application.Range("worksheet!A4:A" & _
Worksheets("worksheet").Range("A" & rows.Count).End(xlUp).Row
also i have a name-variable (called "Norm") i use as a dropdown option in cells and would like to update it according to the dynamic range using
With Application.Names("Norm")
.Name = "Norm"
.RefersTo = rng.Address
.Comment = ""
End With
both run on Workbook_BeforeSave.
when saving while in vba editing mode everything works as expected, the name-variable has the correct range in a1-notation and the content of Norm is according to the range.
but saving in pure excel-mode results in the range in r1c1-notation which can not be processed by the name-variable leaving it empty. unfortunately i can't find any explanation or solution for that. is this an excel2010 issue or what can i do about that?
Range is a member of worksheet.
Names us a member of workbook.
You Set a range object.
RefersTo should point to the range object, not its address.
Revised code:
Dim rng As Range
WITH THISWORKBOOK.WORKSHEETS("worksheet")
SET rng = .Range(.cells(4, "a"), .cells(.rows.count, "a").end(xlup))
end with
With thisworkbook.Names("Norm")
.Name = "Norm" 'totally redundant, it already has a name identified in the line above
.RefersTo = rng 'no address, just rng
.Comment = ""
End With
Name has two properties RefersTo and RefersToR1C1, which means that you should assign appropriate address style. If you want to be sure you get correct notation, you should use ReferenceStyle parameter:
Names("Norm").RefersTo = "=" & Range("A1").Address(ReferenceStyle:=xlA1)
Names("Norm").RefersToR1C1 = "=" & Range("A1").Address(ReferenceStyle:=xlR1C1)
First, you are not setting your rng object correctly:
rng = Application.Range("worksheet!A4:A" & _
Worksheets("worksheet").Range("A" & rows.Count).End(xlUp).Row
should give you an error, you need to Set your rng object, see code below:
Dim Sht As Worksheet
Dim Rng As Range, LastRow As Long
' set the worksheet object
Set Sht = ThisWorkbook.Worksheets("worksheet")
With Sht
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row ' get last row with data in column A
' set the Range object
Set Rng = .Range("A4:A" & LastRow)
End With
' updating the range that "Norm" refres to
With ThisWorkbook.Names("Norm")
.RefersTo = Rng
End With
Related
Project Outline: The project I'm working on consists of a file with 2 sheets. The first sheet is a Dashboard of Reports with inputs about who worked it, what department it was for, and the timeframe of each report. This information is then transferred to a second sheet via a Button.
Right now the button copies the data from Dashboard to Data, adding the new information, starting in the first blank row (counted up from the bottom) of Column B. It then requests a Date input for that data from the user.
What I want to happen next:
I need to find the Range based on where the last input from Column A is, to where the last input of Column B is.
Example: Say there is Data from A1:A345. Say there is also Data from B1:B764. I need the VBA script to pull the range A346:A764 so I can then tell it to apply the Date from the input box in Column A for that range. (The dates may be historical and/or out of order so the input from the user is important. )
I'm currently using :
sh2.Cells(Rows.Count, 1).End(xlUp) - to Find the range of Column A
sh2.Cells(Rows.Count, 2).End(xlUp) - to Find the range of Column B
I'm having trouble figuring out a way to compare on range to the other in order to return the correct range for the data.
I've attempted using:
DO UNTIL (Excel crashed, it seems to loop continuously and I'm having trouble finding a way to get it to recognize when to stop)
DO UNTIL Attempt
`sh2.Cells(Rows.Count, 1).End(xlUp)(2).Select
Do Until IsEmpty(ActiveCell.Offset(, 1))
sh2.Cells(Rows.Count, 1).End(xlUp)(2).Value = myDate
Loop`
LOOP UNTIL (Excel crashed, same as above)
FOR EACH with IF NOT (I can't quite figure out how to compare the ranges to return a usable value)
FOR EACH Attempt
`Dim AColLR As Long
Dim BColLR As Long
Dim rngA As Range
Dim rngB As Range
Dim rngC As Range
Dim cell As Range
AColLR = sh2.Cells(Rows.Count, 1).End(xlUp).Row
BColLR = sh2.Cells(Rows.Count, 2).End(xlUp).Row
'Set rngB = sh2.Range("B2:B" & BColLR)
Set rngC = sh2.Range(BColLR - AColLR)
For Each cell In rngC
If Not IsEmpty(cell.Value) Then
cell.Offset(, -1).Value = myDate
End If
Next cell`
FUNCTION (I wasn't able to figure out how to call the function in the Sub, also Function might be broken?)
FUNCTION Attempt
`Function SetDifference(rngA As Range, rngB As Range) As Range
Dim sh1 As Worksheet, sh2 As Worksheet
Set sh1 = Sheets("Dashboard")
Set sh2 = Sheets("Data")
AColLR = sh2.Cells(Rows.Count, 1).End(xlUp).Row
BColLR = sh2.Cells(Rows.Count, 2).End(xlUp).Row
rngA = sh2.Range("A2:A" & AColLR)
rngB = sh2.Range("B2:B" & BColLR)
On Error Resume Next
If Intersect(rngA, rngB) Is Nothing Then
'if there is no common area then we will set both areas as result
Set SetDifference = Nothing
'alternatively
'set SetDifference = Nothing
Exit Function
End If
On Error GoTo 0
Dim aCell As Range
For Each aCell In rngA
Dim Result As Range
If Application.Intersect(aCell, rngB) Is
Nothing Then
If Result Is Nothing Then
Set Result = aCell
Else
Set Result = Union(Result, aCell)
End If
End If
Next aCell
Set SetDifference = Result
End Function`
I'm not sure which method is actually the correct one to use for this type of referencing.
Any assistance would be most appreciated!!
Something like this should work:
Dim cA As Range, cB As Range, ws As Worksheet, rng As Range
Set ws = ActiveSheet 'or some specific sheet
With ws
Set cA = .Cells(.Rows.Count, "A").End(xlUp).Offset(1)
Set cB = .Cells(.Rows.Count, "B").End(xlUp).Offset(0, -1)
Set rng = .Range(cA, cB)
End With
rng.Value = "dateHere"
I extract a date from sheet "A" to find in a sheet "B".
I have the same type of date in each sheet (type 7) and the date exists.
With the code below I have
Error 91 : Object variable or With undefined block variable
Sub SearchDate()
lastColTraining = ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell).Column
lastLetterTraining = Split(Cells(1, lastColTraining).Address, "$")(1)
Set allTraining = Range("K3:" & lastLetterTraining & "7")
For Each training In allTraining.Columns
trainingDate = training.Rows(4)
With Worksheets("B")
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
allDate = .Range("A2:A" & lastRow)
firstRowDate = allDate.Find(What:=trainingDate, After:=.Range("A" & lastRow)).Row
End With
Next training
End Sub
I scoured many forums and tried different solutions without finding an answer.
There are a few things going wrong here:
First: Declare your variables, it's even best to use Option Explicit on top of your module to actually make you not forget any. Otherwise VBA will try to make an educated guess which will be a Variant type of the date type.
Second I would try to avoid ActiveSheet but instead use a CodeName. For example Sheet1.Range("..."). This post on SO can clarify a thing or two on this matter.
Third, UsedRange is not the most reliable way to return a last used column. Instead I would go with something like:
With Sheet1 'The explicit sheet reference from the first point
lastColTraining = .Cells(1, .Columns.Count).End(xlToLeft).Column
End with
Fourth: You don't really need the column letter to refer to the column. There are other ways, for example using .Cells within a range. You could use:
With Sheet1 'The explicit sheet reference from the first point
lastColTraining = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set allTraining = .Range(.Cells(3,11),.Cells(lastColTraining,11))`
End with
Fifth: If you have a Range object, you want to (most likely) Set it as a Range object. Otherwise (as per my first point) Excel will make an educated guess and in your case will return an array when you write: allDate = .Range("A2:A" & lastRow), instead use: Set allDate = .Range("A2:A" & lastRow)
Sixth: As per #SiddharthRout his comment, you'll recieve an error once your value isn't found. You can test that first trying to Set a FoundRange and check if it's not nothing.
Considering all the above, your code would run smoother using:
Option Explicit
Sub SearchDate()
Dim lastColTraining As Long, lastRow As Long, firstRowDate
Dim allTraining As Range, training As Range, allDate As Range, FoundCell As Range
Dim trainingDate As Variant
With Sheet1 'Change according to your sheets CodeName
lastColTraining = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set allTraining = .Range(.Cells(3, 11), .Cells(7, lastColTraining))
For Each training In allTraining.Columns
trainingDate = training.Rows(4)
With Worksheets("B")
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set allDate = .Range("A2:A" & lastRow)
Set FoundCell = allDate.Find(What:=trainingDate, AFter:=.Range("A" & lastRow))
If Not FoundCell Is Nothing Then firstRowDate = FoundCell.Row
End With
Next training
End With
End Sub
I'm just not sure what you want with trainingDate = training.Rows(4). If you just interested in the 7th row of each column, then refer to that Range instead. Neither am I sure what your goal is with the code, but hopefully you can get it to work now.
I'm trying to add data to a worksheet, and while I know I've set up the Range function "correctly" (for a value of correctly that includes "worked every other time"), instead of labeling row 1 it's labeling row 2, and shifting the calculations in row 2 down to row 3. This is a problem only with this module in this worksheet.
I've tried the debugging tools, but they don't change the outcome or break anywhere. The first row hidden, nor is it frozen. And this happens whether or not I append ".value" after the range calls.
Weirdly, the second set of labels and calculations (starting at "With Calcs.UsedRange") are ending up in the correct cells. Code below:
Sub CalcGDD()
'Initialize
Dim nb As Workbook
Dim Clim, Vars, CC, Calc As Worksheet
Dim LRow, RowL As Long
'Setup
Set nb = ActiveWorkbook
Application.ScreenUpdating = False
Set Vars = nb.Sheets("Variables")
Set CC = nb.Sheets("CoverCrop")
Set Calcs = nb.Sheets("Summary")
Set Clim = nb.Sheets("Climate")
'Calculations in Climate Sheet
With Clim.UsedRange
LRow = Cells(Rows.Count, "A").End(xlUp).Row
.Range("H1") = "Plant Day"
.Range("I1") = "Harv Day"
.Range("J1") = "Fall GDDs"
.Range("K1") = "Spring GDDs"
.Range("H2:H" & LRow & "").FormulaR1C1 = (...calculations elided...)
End With
'Summarize
RowL = CC.Cells(Rows.Count, "A").End(xlUp).Row
With Calcs.UsedRange
.Range("A1") = "Year"
.Range("B1") = "Fall GDD"
.Range("C1") = "Spring GDD"
.Range("D1") = "Total GDD"
.Range("A2:A" & RowL & "").FormulaR1C1 = (...calculations elided...)
End With
End Sub
Any ideas why Range("H1") is printing to H2? Thanks much.
The .UsedRange is likely offsetting your addressing. Remove it in both With statements. If the UsedRange starts in row 2, then "A1" in that range refers to the top-left cell in that range. If you want to write to absolute cell address A1 (i.e. not relative to where UsedRange starts), work off the actual worksheet cells instead.
'Calculations in Climate Sheet
With Clim.UsedRange
LRow = Cells(Rows.Count, "A").End(xlUp).Row
That Cells call is implicitly going against whatever the ActiveSheet happens to be. Qualify it with the Worksheet object you mean to work with - in this case, most likely the With block variable:
'Calculations in Climate Sheet
With Clim
LRow = .Cells(Rows.Count, "A").End(xlUp).Row
See if Rubberduck (free, open-source VBIDE add-in project I manage) can spot more implicit ActiveSheet references (and other potential issues in your code, like these implicit Variant variables:
Dim Clim, Vars, CC, Calc As Worksheet
Dim LRow, RowL As Long
Also:
And this happens whether or not I append ".value" after the range calls
That's because the .Value member call is implicit if it's not specified.
I'm trying to look for the last row of data between column A and I and then duplicate the value to the row below which is empty.
Every time I run it, Excel crashes
Sub insert_row()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
LastRow = LastRow
Dim lastrow_start As String
Dim lastrow_end As String
lastrow_start = "A" & LastRow
lastrow_end = "I" & LastRow
Dim lastrowregion As String
lastrowregion = lastrow_start & ":" & lastrow_end
Dim lastrowrange As Range
Set lastrowrange = Range(lastrowregion)
Dim rng As Range
Set rng = Range(lastrow_start)
Do While (rng.Value <> "")
rng.Offset(1).insert
lastrowrange.Copy rng.Offset(1)
Set lastrowrange = rng.Offset(2)
Loop
End Sub
Is it just copying too much and causing a crash? It's only nine columns and they're all text apart from one cell which is a shape (button).
You are trying to set a String to a range object. To get the range use:
Set rng = Range(lastrowregion)
The Range you are getting is A2:I2. So your Do While will error because rng.Value is actually returning an Array. You could either loop through either the Range or the Array at that point if you intended on it being multiple cells.
If the goal is simply to copy the last row of data down one row then this method can be much simpler. You can simply set the Offset to equal the value of the last row. Since they are the same size it will just work.
To show this I used CurrentRegion but you could also do it with your A2:I2 Range.
Public Sub copyLastRowDown()
Dim region As Range
Set region = ThisWorkbook.Worksheets("Sheet1").Range("A1").CurrentRegion
With region.Rows(region.Rows.Count)
.Offset(1).Value = .Value
End With
End Sub
Additional Notes
Use Option Explicit to ensure all variables are explicitly declared.
Declare and assign variables next to where they are going to be used, but place them in a reasonable place.
Do not use underscore case as this has special meaning with events and interfaces.
I am building a module to import text into an Excel workbook. After it imports, I want to format the data as a table. The problem I have is that the import will never have the same range.
I'm using the following code, but it throws an error, Run-time error '424': Object required.
Sub ImportRange()
Dim ws As Worksheet
Dim lRow As Long
Dim lCol As Long
Dim rng As Range
Set ws = ThisWorkbook.Worksheets("Import")
lRow = ws.UsedRange.Row - 1 + ws.UsedRange.Rows.Count
lCol = ws.UsedRange.Column - 1 + ws.UsedRange.Columns.Count
Set rng = ws.Cells(lRow, lCol).Address(True, True)
'MsgBox Cells(lRow, lCol).Address(True, True)
End Sub
I've done quite a bit of searching, but I have been unable to find an answer or figure out how I should be doing this.
The end result would look something like this in the code with the start of the range always being set to $A$1:
ws.ListObjects.Add(xlSrcRange, Range("$A$1:$AM$90"), , xlYes).Name = _
"Import"
If your goal is to set a range to the used range on a sheet, it can be done simpler:
Set rng = ws.UsedRange
Obviously, you need to make sure that the usedrange on that sheet properly represents your imported data.
To convert the range to a table:
Dim Import_Table As ListObject
Set Import_Table = ws.ListObjects.Add(SourceType:=xlSrcRange, Source:=rng, XlListObjectHasHeaders:=xlYes)
Import_Table.Name = "Import"
Note: the code is for Excel 2010. For later versions, replace XlListObjectHasHeaders with HasHeaders