I have a variable called 'attached' that increments each time a certain condition is verified as true. When that condition is true, I'm trying to select a worksheet that was created earlier that has the name "Attachment(x)". 'x' is a number 2,3,4, etc. For instance, a worksheet might have the name "Attachment (3)". In my code, x will be the value that the incrementing variable 'attached' has achieved at that point.
What code will select the worksheet based upon the current value of the variable 'attached'? I declared the variable attached as a variant......Dim attached As Variant.
I just keep getting errors.
Thanks
To select the sheet, do this:
Worksheets("Attachment(" & x & ")").Select
If I understand correctly, your variable (named Attachment(#)) will decide which sheet to use (sheet named Attachment (#))?
I assume you're using some kind of loop that iterates through the #, so you could do something like this
For i = 1 to 100
myAttachment = "Attachment(" & i & ")"
myWorksheet = Worksheets("Attachment (" & i & ")")
next i
Related
My file has two identical Worksheets for users to input two different sets of assumption variables, called "InputA" and "InputB". I want to quickly switch which Input sheet is feeding into the other sheets of the model.
Using Find and Replace took over 5 minutes, and there were over 350,000 references to "InputA".
I tried the following macro, which takes an instant to run, but unfortunately also changes all references in the workbook, effectively keeping everything referenced to the original input sheet.
Sheets("InputA").Name = "temp"
Sheets("InputB").Name = "InputA"
Sheets("temp").Name = "InputB"
Is there a way to execute the macro but prevent any references to worksheets from changing to the new sheet name, basically freezing everything except the sheet name change? Or perhaps any other solution that will work quickly? I don't want to go through 350,000 instances and rewrite using INDIRECT(), as that is the only other solution I've seen, because my references are complex and nested and that will take an age.
Thanks.
Assuming that your 2 Input-Sheets have the same structure, I would suggest the following:
Create a named range on Workbook-level, name it for example InputData. This range should contain all data from InputA.
Create a helper-sheet and name it Input - you can later set it to hidden.
Mark the range in the new sheet that is exactly the size of the Input-Data-Range and enter the formula =InputData as Array-formula. You can do so by entering Ctrl+Shift+Enter. The formula should have curly brackets and the sheet should now display the data of InputA.
Change all you formulas to refer to the helper Sheet Input instead of InputA.
Create a macro:
Sub switchInput()
Const sheetAName = "InputA"
Const sheetBName = "InputB"
With ThisWorkbook.Names("inputData")
If InStr(.RefersTo, sheetAName) > 0 Then
.RefersTo = Replace(.RefersTo, sheetAName, sheetBName)
Else
.RefersTo = Replace(.RefersTo, sheetBName, sheetAName)
End If
End With
End Sub
This routine will just change the definition of the named range to point either to the first or second input sheet. As a consequence, the helper sheet will show the data of the selected Input-Sheet. All your formulas itself stays unchanged.
As stated in the comments, you could take the approach recommended by Damian and use a conditional in all relevant cells. The generic conditional would be
=IF(A1="InputA",FORMULA INPUTA,FORMULA INPUTB)
This formula makes A1 the cell that decides which input to pull. This will make changing the around 350.000 output formulas in your workbook the bottleneck, the following macro takes care of changing all the formulas to conatin the conditional:
Sub changeFormulas()
Dim rng As Range, cll As Range
Set rng = shOutput.Range("A2:C10") 'Your relevant worksheet and range here
Dim aStr As String, bStr As String, formulaStr As String
aStr = "InputA"
bStr = "InputB"
For Each cll In rng
If cll.HasFormula And InStr(1, cll.Formula, aStr, 1) Then
formulaStr = Right(cll.Formula, Len(cll.Formula) - 1)
cll.Formula = "=IF(A1=" & Chr(34) & aStr & Chr(34) & "," & formulaStr & "," & Replace(formulaStr, aStr, bStr) & ")" 'Change A1 to the reference most suited for your case
End If
Next cll
End Sub
This might take a bit of time, since it has to access all the relevant cells one by one, but it will only have to run once.
To explain: This macro will go through all the cells in your range rng specified at the top. If a cell has a formula in it and the formula contains "InputA" it will change that formula to fit the generic conditional (with the cells own formula of course). Chr(34) is the quotation mark ", I find using Chr(34) more readable than multiple quotation marks """, but that is just preference.
Whenever trying to select a variable range using a subroutine with parameters, I will also receive the error 9 Subscript out of range. If I remove the "ThisWorkBook.Worksheets("Sheet1")" I then receive a Gobal error.
I have tried using different routines such as Cells(x,y)
Sub Trail()
Call SelectRow(2)
End Sub
Sub SelectRow(i As String)
Dim theAddressA As String
Dim theAddressL As String
theAddressA = "A" & i
theAddressL = "L" & i
MsgBox (theAddressA) 'Prints "A2".
MsgBox (theAddressL) 'Prints "L2".
ThisWorkbook.Worksheets("Sheet1").Range("theAddressA:theAddressL").Select
End Sub
I want to be able to select a row based on a variable input, as shown by the subroutine with the argument parameters. Ultimately once I can do this I'd like to compare individual rows with another table to then identify differences and similarities.
Debug display image
Dont put your variables in quotes... they are not literal strings. Instead, concatenate them like this.
ThisWorkbook.Worksheets("Sheet1").Range(theAddressA & ":" & theAddressL).Select
I was referencing the name of the worksheet incorrectly, I thought that the name Sheet1 shown in the image would be what VBA references. However, it is the actual name which has been assigned that should be referenced. Thanks everyone
Name should be referenced not (Name)
I have a code snippet that I can't understand why it's failing. I get "Application-Defined or Object-Defined Error". I tried hard coding the range and it does work, but I even hardcoded the "Named Range" and it still failed.
Eg: This Doesn't Work
If regexp.test(strInput) Then
Set clloffset = rcell.Offset(0, 1)
Call GetColLet(rcell)
'Set PalletCol = Range(rcell.Address(False, False) & ":K39")
Set PalletCol = ActiveWorkbook.ActiveSheet.Range("K24:K39")
Set FormulaCol = ActiveWorkbook.ActiveSheet.Range("L24:L39")
ActiveWorkbook.ActiveSheet.Range(FormulaCol).Formula = "=" & Chr(34) & "BINWH" & Chr(34) & "&" & rcell.Address(0, 1)
But this does and I can't get it!
If regexp.test(strInput) Then
Set clloffset = rcell.Offset(0, 1)
Call GetColLet(rcell)
'Set PalletCol = Range(rcell.Address(False, False) & ":K39")
Set PalletCol = ActiveWorkbook.ActiveSheet.Range("K24:K39")
Set FormulaCol = ActiveWorkbook.ActiveSheet.Range("L24:L39")
ActiveWorkbook.ActiveSheet.Range("L24:L39").Formula = "=" & Chr(34) & "BINWH" & Chr(34) & "&" & rcell.Address(0, 1)
Note: I tried using both (FormulaCol) and ("FormulaCol"), I'm not sure when to use "" inside a named range to be honest, but neither work. Then I tried hard coding ("L24:L39"). and it worked. I appreciate any help troubleshooting this.
There are three main methods of accessing ranges in Excel:
Hard-coded values
Variables
Named Ranges
Hard-coded values
I won't really spend time explaining these, but here are some examples:
ThisWorkbook.Sheets("Name").Range("A1:B2")
ActiveWorkbook.ActiveSheet.Range("C3:E50")
Sheet1.Cells(1,3) 'Range("C1")
Notes
ThisWorkbook is a reference to the workbook which holds the code. An important distinction from ActiveWorkbook, as they are not always the same workbook (primarily when 2+ workbooks are open).
Sheet1 is the code name of a sheet. Sheet code names can only be used for the sheets contained in ThisWorkbook. If referencing a sheet from a different workbook, you'd have to use either the name or index to access it.
Variables
This would be like the FormulaCol in your original code. Variables are either declared in the code (Set FormulaCol = ActiveWorkbook.ActiveSheet.Range("L24:L39")) at local or global scope level, or are passed into routines as parameters (Function Sample(rangeVariable as Range) : End Function).
Interacting with a range variable could be thought of as using substitution, such as:
Set FormulaCol = Sheet1.Range("L24:L39")
FormulaCol.Formula = ""
instead of
Sheet1.Range("L24:L39").Formula = ""
Named Ranges
Interacting with a named range is similar to interacting with a hard-coded range. Assuming that the named range "Test" references Range("B2:C3") on Sheet1, it would be interacted with via:
Sheet1.Range("Test")
However, before named ranges can be referenced through VBA, they first have to be set up in the workbook. This can be done a couple of different ways:
Select all the cells for the named range, then in the "Name Box" (to the left of the formula bar) type in the name for that named range, and press enter (if you forget "enter", the name doesn't get saved).
Open the Name Manager in the Formulas tab and press "New" (or "Edit", if editing an existing named range). Note: when adding a named range via the manager, you can select the scope to be either the workbook or any of the sheets within the workbook. This affects which sheets can reference the named range.
and a window will open where you choose the name for the named range, and select what range it's referencing.
I'm working on a cell formula that returns (via VLOOKUP?) a cell in a closed workbook (thus I believe INDIRECT will not work). The issue is that I want to use a value in the active sheet to determine the name of the sheet in the reference workbook and can't figure it out. Here's the best I've got.
=VLOOKUP($A3,'[Other Workbook.xlsm]ObsDFW'!$1:$800,COLUMNS($D4:D4)+3)
ObsDAL is the name of one of the sheets in the "Other Workbook". What I can't figure out is how to keep the "Obs" part of that name constant, but take the "DFW" from a cell value.
Using bad code, I want it to be:
=...[Master Statistics.xlsm]("Obs" & A1)'!$1:$800...
If context is helpful, the "Other Workbook" is full of hourly weather observations, separated into one sheet for each of a series of airports. I'm trying to pull this info into another file/workbook so I don't have to specify each airport specifically in the code many times over.
Thanks in advance!
You could try this VBA approach. This way you are adjusting Vlookup formula based on your dynamic_part (sheetname)
Sub VlookupClosedWorkbook()
Dim dynamic_part As Variant
dynamic_part = Range("B1").Value 'You should enter in cell B1 dynamic part of sheet name
For x = 3 To Range("A" & Rows.Count).End(xlUp).Row
Range("B" & x).Value = "=VLOOKUP(A" & x & ",'[Other Workbook.xlsm]Obs" & dynamic_part & "'!$1:$800,COLUMNS($D4:D4)+3,FALSE)"
Next x
End Sub
Assign this macro to shape and fire it after you change your dynamic part. When you trigger it for the first time, make sure that you have both Workbooks open.
Latest edit: I've tried a ton of stuff, still can't figure this out. A more concise question is how I assign the variable. Per the first comment I got, I'm using VSTO.
Dim wksSheetVSTO As Worksheet = Globals.Factory.GetVstoObject(wsThisSheet)
Dim nrThisNamedRange As NamedRange = ???
Original Question:
I am brand new to Visual Studio/VB.Net but am an experienced VBA programmer. I have a very complex spreadsheet that uses Excel named ranges, and I can’t seem to figure out how I can manipulate them.
The existing spreadsheet uses Excel named ranges that essentially act like database tables. When a user adds a row, it adds a row to the named range. Since Excel doesn’t automatically extend the range itself, the VBA code re-defines the named range to include the new row by re-defining the RefersToR1C1 property of the named range. It works perfectly in VBA. (I can post the working VBA code if it helps.)
In Visual Studio/VB, I can’t figure out a way to assign a variable to the named range. The only documentation I can find talks about assigning a variable by creating a NEW named range, but I already have the named range. My work-around (which works, but looks bad to me) is below:
Dim rngDataStore As Excel.Range
Dim nrThisNamedRange As NamedRange
...<<stuff happening here>>...
rngDataStore.Resize(1).Insert(Excel.XlInsertShiftDirection.xlShiftDown)
' The next line is what I don't want.
nrThisNamedRange = wksSheetVSTO.Controls.AddNamedRange(wksSheetVSTO.Range("A1"), <<name>>)
nrThisNamedRange.RefersToR1C1 = <<new named range R1C1 here>>
I don’t want to just extend the working range (rngDataStore), I need to actually update the definition of the named range so that I can continue to reference and write to it. (This is doing what ctrl+F3 allows you to do within Excel.) I’ve tried looping through the Names collection, but other than using the ‘Controls.AddNamedRange’ I can’t figure out how to simply assign the nrThisNamedRange variable and update its RefersToR1C1 property.
Added: The above code doesn't always work, it's giving me an error that the named range already exists. (Which of course I know!)
For global named range:
Dim name = Globals.ThisWorkbook.Names.Item("name")
Dim range = name.RefersToRange
name.RefersTo = range.Resize(range.Rows.Count + 1)
#Slai helped me uncover the answer (the worksheet had global named ranges in it) so I thought I'd at least share the full function for anyone who might need it.
Function ExpandNamedRange(sRangeName As String, lRowsToAdd As Long, sSheetName As String) As Boolean
' Adds the specified number of rows and resets the surrounding named range.
Dim wsThisSheet As Excel.Worksheet = Globals.ThisWorkbook.Application.Worksheets(sSheetName)
Dim rngDataStore As Excel.Range = wsThisSheet.Range(sRangeName)
If lRowsToAdd > 0 Then
rngDataStore.Resize(lRowsToAdd).Insert(Excel.XlInsertShiftDirection.xlShiftDown)
' For a global/workbook named range
Globals.ThisWorkbook.Names.Item(sRangeName).RefersToR1C1 = "=" & wsThisSheet.Name &
"!R" & rngDataStore.Row - lRowsToAdd & "C" & rngDataStore.Column &
":R" & rngDataStore.Row + rngDataStore.Rows.Count - 1 & "C" & rngDataStore.Column + rngDataStore.Columns.Count - 1
' For a local/worksheet named range
'wsThisSheet.Names.Item(sRangeName).RefersToR1C1 = "=" & wsThisSheet.Name &
' "!R" & rngDataStore.Row - lRowsToAdd & "C" & rngDataStore.Column &
' ":R" & rngDataStore.Row + rngDataStore.Rows.Count - 1 & "C" & rngDataStore.Column + rngDataStore.Columns.Count - 1
End If
Return True
End Function
Two things I learned:
(1) I didn't actually have to refer to a NamedRange object at all, I could just use the Names collection of the workbook; I was originally trying that because my first attempts failed. But this is nice, it saved me steps.
(2) In VBA, you can refer to 'global' Named Ranges from anywhere. That's where I got stuck.