Excel Selecting a Row - vba

I am Trying to create a macro that will take the 1st Cell of a selection and merge it with the 6th Cell. I am getting error 400.
Sub SignPage()
Dim ws As Worksheet, BasePoint As Range, row As Long, singlecell As Long,
singleCol As Long, sLong As Long, sShort As String
Set BasePoint = Selection
row = BasePoint.row
Range(Cells(row, BasePoint.Columns(1)), Cells(row, BasePoint.Columns(6))).Merge
End Sub

This should be enough:
Selection(1).Resize(, 6).Merge
Explanation: Selection can span several cells, so Selection(1) takes the top left cell - thus, we always refer to one cell.

The point is that you should refer to the first column of Basepoint and take its column. This could be achieved, if you amend your line like to this:
Range(Cells(row,BasePoint.Columns(1).Column),Cells(row,BasePoint.Columns(6).Column)).Merge
As an advice:
do not use names like row, column, Cells, Range etc. as a variable, the Excel object module library inside VBA uses them as well. In your case, you have BasePoint.row. If you did not have the variable row, then the VBEditor would have written BasePoint.Row automatically.
try to avoid Selection, although it depends on the context - How to avoid using Select in Excel VBA
as #Mat's Mug mentioned in the comments, it is a good habit to show the "parent" of Cells and Ranges, when you are using them.
Thus in your case, something like this:
With Worksheets(1)
.Range(.Cells(row, BP.Columns(1).Column), .Cells(row, BP.Columns(6).Column)).Merge
End With
Then it would always refer to the first worksheet (or whichever you need to) and not to the ActiveSheet, which would be referred, if the Parent in not mentioned. (I have written BP instead of BasePoint to make sure it goes on one line.)

Related

Excel VBA Rows.Count reference for a loop

I need to pull a range of data from a sheet where the top 16 rows will always be the same but the data below will vary. I can find the starting cell with this
Sheets("AA").Next.Select
Range("A17").Select
Selection.End(xlDown).Offset(2, 2).Select
and then I want to count the cells using (starting cell selected above) to end of cells containing data. I have tried this
Range(Selection, Range(Selection.End(xlDown)).Rows.Count
and all kinds of variations on it but cannot seem to make it work. I need to be able refer back to both the start cell and the number of rows to pull data from that cell down to the last cell using a loop (another topic I will probably ask questions on when I get that far)
Can someone help?
Your explanation as to why you are trying to do this makes it appear you are placing more work on yourself than you need to.
Let's create a couple functions.
The first function we will call nextBlankCell, which will automatically grab the last cell before an empty range. In your case, we can even use your selection to determine this - which we will do in our next function.
nextBlankCell Function
Function nextBlankCell(ByVal startRng As Range) As Range
Set nextBlankCell = startRng.End(xlDown)
End Function
Next, let's create a function that will automatically set your entire range for you. In this case, it will be the range from your current selection to the last row containing data that we get from using the above function.
getRngDownwards Function
Function getRngDownwards() As Range
Dim celStart As Range, celEnd As Range, ws As Worksheet
Set celStart = Selection
Set ws = celStart.Parent
Set celEnd = nextBlankCell(celStart)
Set getRngDownwards = ws.Range(celStart, celEnd)
End Function
In the above function, we have two ranges celStart and celEnd. celStart is simply your current selection. I always prefer to immediately set your selection to a static range if you must use Selection (most cases it’s not necessary).
celEnd is the range that will contain the last used cell in your column.
We also determine the worksheet ws by using the selection's parent object. Protip: We avoided ActiveSheet!
Now you can put it to the test:
Sub test()
' This test shows you the address of the range
MsgBox getRngDownwards.Address
' This test visually shows you the range
getRngDownwards.Select
End Sub

Excel VBA Dynamic Range / Loop issue

I am developing a financial model for a bank and come across the below issue which I am not able to resolve in Excel VBA, and would appreciate your help.
I have built a simple macro which essentially does two things: (i) it clears contents in a given range, (ii) it populates the same range with a formula. In a very abbreviated way it looks like the following:
Sub AutoCalculateCashFlows()
Range(D208:L208).ClearContents
Range("L208").FormulaR1C1 = "=+R[-34]C-R[-34]C[-1]"
Range("L208").AutoFill Destination:=Range("E208:L208"), Type:=xlFillDefault
End Sub
My problem is that the range that should be auto populated is dependent on how many cells did the user fill in within the range of E10:L10. Users will start populating this range from right to left, but I don't know how far they will go from column L to the left. The formula that my macro auto populates needs at least two data, ie. at least L10 and K10 should be populated and if the latter is the case then the macro only needs to auto populate L208 with formula, in case J10:L10 is filled out then the macro needs to auto populate the range L208:K208 and so on to the point that in case the full D10:L10 range is filled out then E208:L208 should be populated with formula.
I have thought to resolve this issue via two routes: (i) approaching it as a dynamic range problem in which case I need a vba code to determine the previous to the last cell populated by the user in the range D10:L10 and use the column code of that cell in "Destination:=Range("E208:L208")", or (ii) run a loop which will populate range E208:L208 with formula until the cell in the previous column within range D10:L10 is filled in by the user and stop when it is not.
Hope this makes sense and thanks in advance for the help.
When you need a dynamic range in VBA, you should simply build one. This is probably the easiest method:
Sub TestMe()
Dim colRange As Long
Dim rowRange As Long
Dim rngMain As Range
rowRange = 10
With Worksheets(1)
colRange = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set rngMain = .Range(.Cells(rowRange, colRange), .Cells(100, 200))
MsgBox rngMain.Address
End With
End Sub
It is dynamic, based on the last used column in row 1 of the first Worksheet.
Concerning the second used column in Row 1, one of these 3 would probably do it for you, depending on what exactly do you want:
.Cells(1, 1).End(xlToRight).End(xlToRight).Column
.Cells(1, 1).End(xlToRight).Column
.Cells(1, 1).End(xlToRight).Column + 1

Copy a static range on one sheet, and paste in a dynamic range in another sheet based on a single value in a cell

I have three parts to this problem. I have a single cell with a Week number in Sheet1!A1. I have a static range in Sheet1!B1:F1 that needs to be copied. Then I need to paste the value in a dynamic range in Sheet2 offset by the week number for rows. This is part of a larger macro I am writing for a sheet I use regularly, but I seem to have those parts down. I may be either oversimplifying or oversimplifying but this is what I have currently.
Sub CopyPaste()
Sheets(1).Range("B1:F1").Copy
OffsetRange = Sheets(1).Cells(1,1).Value
Sheets(2).Cells(1+OffsetRange,1).Paste
End Sub
When I run this, it either gives me a Runtime Error 9 or Runtime Error 438.
Anyone know whats causing these errors? When I paste the range, does the cells object point towards the first cell of the copied range when I paste in at the location?
Try it as,
Option Explicit
Sub CopyPasteOffset()
Dim OffsetRange As Long
OffsetRange = Worksheets(1).Cells(1, 1).Value
Worksheets(1).Range("B1:F1").Copy _
Destination:=Worksheets(2).Cells(1 + OffsetRange, 1)
End Sub
The .Paste method is a member of Worksheet, not Range or Cells. You may have it confused with .PasteSpecial which is a member of the Range object. In any event, it is unnecessary as a destination can be applied directly to the copy command.

Using VBA to name ranges and create charts with those ranges

I'm new to VBA. I'm attempting to create over 500 xlClusteredColumn charts using two columns of information and I'd like to expedite the work. The first column contains names I'd like to use for named ranges (i.e.: Line1, Line2, etc.) and the second column contains the indirect references of the data ranges (i.e., Sheet1!C4:D28, Sheet1!C28:D90). I noticed that if I use a named range for the "Chart Data Series" field, the data shows up nicely (but I have to first create that named range being sure to include the INDIRECT formula in the reference, (e.g.: Named Range Line1 is equal to =INDIRECT(Sheet1!C4:C28)). The ranges will be static.
In reviewing prior questions I couldn't seem to find a solution that would select the first cell in this set and name it, then uses the second cell to define that range. I think I might need the ActiveWorkbook.Names.Add Name:= formula and combine it with a loop (but I couldn't get it to use a selection or cell to define the Add Name aspect, only a hard coded name).
If the solution requires it, I can go back and extract the individual ranges (i.e.: C4:D28) from the cell and have the chart reference only that if it makes the code simpler. I know my first outlined attempt isn't the only solution and there's probably one much more elegant. I figured using named ranges would speed up the chart work, but perhaps there's a way to cut that step out?
Populating a new sheet with all the charts for each of these ranges would be icing on the cake, but I'll be happy enough receiving help to get the data set up to chart.
Example information:
NameRange1 (let's say in cells A1:A4)
WKD_1_NB
WKD_2_EB
WKD_3_EB
SerRange1 (in cells B1:B4)
WKDpivot!C4:D43
WKDpivot!C84:D140
WKDpivot!C197:D233
(Chart data range requires the reference of named range "WKD_1_NB" to be '=INDIRECT(WKDpivot!C4:D43)' in order for the chart to work.
OK so why don't you try the two-step process. I am going to do this without Indirect because I don't see that it is necessary.
Sub CreateNames()
Dim rng As Range
Dim r As Range
Dim myName As String
Dim addr As String
Set rng = Range("A1:A2") '## Modify as needed
For Each r In rng.Cells
myName = r.Value
addr = "=" & r.Offset(0, 1).Value
ThisWorkbook.Names.Add myName, addr
Next
End Sub
This creates your names (screenshot). Note there are some rules about naming conventions and allowable names, etc., the code above does not take any of these in to account.
From there it should be fairly simple to create a loop that adds your charts one by one, and assigns each named range to each chart.

Get the cell reference of the value found by Excel INDEX function

The Problem
Assume that the active cell contains a formula based on the INDEX function:
=INDEX(myrange, x,y)
I would like to build a macro that locates the value found value by INDEX and moves the focus there, that is a macro changing the active cell to:
Range("myrange").Cells(x,y)
Doing the job without macros (slow but it works)
Apart from trivially moving the selection to myrange and manually counting x rows y and columns, one can:
Copy and paste the formula in another cell as follows:
=CELL("address", INDEX(myrange, x,y))
(that shows the address of the cell matched by INDEX).
Copy the result of the formula above.
Hit F5, Ctrl-V, Enter (paste the copied address in the GoTo dialog).
You are now located on the very cell found by the INDEX function.
Now the challenge is to automate these steps (or similar ones) with a macro.
Tentative macros (not working)
Tentative 1
WorksheetFunction.CELL("address", ActiveCell.Formula)
It doesn't work since CELL for some reason is not part of the members of WorksheetFunction.
Tentative 2
This method involves parsing the INDEX-formula.
Sub GoToIndex()
Dim form As String, rng As String, row As String, col As String
form = ActiveCell.Formula
form = Split(form, "(")(1)
rng = Split(form, ",")(0)
row = Split(form, ",")(1)
col = Split(Split(form, ",")(2), ")")(0)
Range(rng).Cells(row, CInt(col)).Select
End Sub
This method actually works, but only for a simple case, where the main INDEX-formula has no nested subformulas.
Note
Obviously in a real case myrange, x and ycan be both simple values, such as =INDEX(A1:D10, 1,1), or values returned from complex expressions. Typically x, y are the results of a MATCH function.
EDIT
It was discovered that some solutions do not work when myrange is located on a sheet different from that hosting =INDEX(myrange ...).
They are common practice in financial reporting, where some sheets have the main statements whose entries are recalled from others via an INDEX+MATCH formula.
Unfortunately it is just when the found value is located on a "far" report out of sight that you need more the jump-to-the-cell function.
The task could be done in one line much simpler than any other method:
Sub GoToIndex()
Application.Evaluate(ActiveCell.Formula).Select
End Sub
Application.Evaluate(ActiveCell.Formula) returns a range object from which the CELL function gets properties when called from sheets.
EDIT
For navigating from another sheet you should first activate the target sheet:
Option Explicit
Sub GoToIndex()
Dim r As Range
Set r = Application.Evaluate(ActiveCell.Formula)
r.Worksheet.Activate
r.Select
End Sub
Add error handling for a general case:
Option Explicit
Sub GoToIndex()
Dim r As Range
On Error Resume Next ' errors off
Set r = Application.Evaluate(ActiveCell.Formula) ' will work only if the result is a range
On Error GoTo 0 ' errors on
If Not (r Is Nothing) Then
r.Worksheet.Activate
r.Select
End If
End Sub
There are several approaches to select the cell that a formula refers to...
Assume the active cell contains: =INDEX(myrange,x,y).
From the Worksheet, you could try any of these:
Copy the formula from the formula bar and paste into the name box (to the left of the formula bar)
Define the formula as a name, say A. Then type A into the Goto box or (name box)
Insert hyperlink > Existing File or Web page > Address: #INDEX(myrange,x,y)
Adapt the formula to make it a hyperlink: =HYPERLINK("#INDEX(myrange,x,y)")
Or from the VBA editor, either of these should do the trick:
Application.Goto Activecell.FormulaR1C1
Range(Activecell.Formula).Select
Additional Note:
If the cell contains a formula that refers to relative references such as =INDEX(A:A,ROW(),1) the last of these would need some tweaking. (Also see: Excel Evaluate formula error). To allow for this you could try:
Range(Evaluate("cell(""address""," & Mid(ActiveCell.Formula, 2) & ")")).Select
This problem doesn't seem to occur with R1C1 references used in Application.Goto or:
ThisWorkbook.FollowHyperlink "#" & mid(ActiveCell.FormulaR1C1,2)
You could use the MATCH() worksheet function or the VBA FIND() method.
EDIT#1
As you correctly pointed out, INDEX will return a value that may appear many times within the range, but INDEX will always return a value from some fixed spot, say
=INDEX(A1:K100,3,7)
will always give the value in cell G3 so the address is "builtin" to the formula
If, however, we have something like:
=INDEX(A1:K100,Z100,Z101)
Then we would require a macro to parse the formula and evaluate the arguments.
Both #lori_m and #V.B. gave brilliant solutions in their own way almost in parallel.
Very difficult for me to choose the closing answer, but V.B. even created Dropbox test file, so...
Here I just steal the best from parts from them.
'Move to cell found by Index()
Sub GoToIndex()
On Error GoTo ErrorHandler
Application.Goto ActiveCell.FormulaR1C1 ' will work only if the result is a range
Exit Sub
ErrorHandler:
MsgBox ("Active cell does not evaluate to a range")
End Sub
I associated this "jump" macro with CTRL-j and it works like a charm.
If you use balance sheet like worksheets (where INDEX-formulas, selecting entries from other sheets, are very common), I really suggest you to try it.