How can you shift selected cell using Excel VBA? - excel

I am trying to automate a rolling calendar spreadsheet that tracks various metrics and charts them into a spark line. The script I would like to write would shift the selected range in the spark-lines every time it is ran.
I have done some googlefu and have tried using the offset function to no avail. This is because the data is in a predefined range defaulting to num 0 based on the formulas used to populate the spreadsheet int the first place.
excel vba : selected cells loop
https://www.excel-easy.com/vba/examples/loop-through-entire-column.html
https://support.microsoft.com/en-us/help/291308/how-to-select-cells-ranges-by-using-visual-basic-procedures-in-excel
I am stuck at incrementing the ActiveCell.SparklineGroups.Item(1).Item(1).SourceData from its current selected range to PPTracking!G8:R8 ... H8:S8 ... and so on each time the macro is ran.
This is my first time working in VBA and any help is greatly appreciated!
Sub Macro4()
Dim selectedRange As Range
Set selectedRange = PPTracking!F8:Q8
Range("E5:E6").Select
Application.CutCopyMode = False
ActiveCell.SparklineGroups.Item(1).Item(1).SourceData = "PPTracking!F8:Q8"
Range("E5:E6").Select
End Sub

You can either use Sparkline.ModifySourceData or directly change the Sparkline.SourceData property, as it looks like you are currently aiming to do.
This code will shift the SourceData 1 column to the right - from F8:Q8 to G8:R8, then to H8:S8, etc. - by using the original SourceData value as a reference within Range, which is then Offset by 1 column.
It concatenates the Parent.Name to the Address to get the full Worksheet Name and cell reference.
Sub ShiftSparklineData()
If ActiveCell.SparklineGroups.Count > 0 Then
With ActiveCell.SparklineGroups.Item(1)
.SourceData = "'" & Range(.SourceData).Parent.Name & "'!" & Range(.SourceData).Offset(, 1).Address
End With
End If
End Sub
Avoid using ActiveCell where possible though; reference the cell(s) with a sparkline using Sheets("Yoursheetname").Range("Cellreference")

Related

Excel - Using a cell reference as a TableName in a fully qualified structured reference

I am putting together a dashboard to quickly see counts of orders from vendors
image of dashboard
The simplified version of the formula I am using is as follows:
=IFERROR(
SUMPRODUCT(
(TEXT(Table1[[openDate]:[openDate]],"YYYY")=$B$2)
*
(TEXT(Table1[[openDate]:[openDate]],"MMMM")=C$3)
/
COUNTIF(Table1[[orderNum]:[orderNum]],Table1[[orderNum]:[orderNum]]&"")
)
,"")
Each table is a separate worksheet for each vendor. What I am trying to accomplish is have the table reference (Table1 in this example) be able to be a cell reference. This would allow the formula to be pre-populated in the dashboard and not require manually updating the table name each time a new table is created.
I have created a VBA module that pulls all table names and places them into a table on a reference worksheet image of reference table:
Sub GetTableNameList()
Dim x As ListObject
Dim y As Worksheet
Dim z As Long
z = -1
For Each y In Worksheets
For Each x In y.ListObjects
z = z + 1
Sheets("Reference").Range("B3").Offset(z).Value = x.Name
Next x
Next
End Sub
My thought is that as new tables are created the cell in the formula would reference the next line on the reference table. I am open to all other suggestions for how to complete this task. My only stipulation is there needs to be little to no interaction required by the end user for this to work properly.
NOTE: Dashboard begins blank, and there are no tables to reference. Tables/worksheets are added by the user as they select vendors they wish to track.
Update:
I am attempting to use my reference table by adding
TEXT(EvaluateString("Reference!$B6"&"[[openDate]:[openDate]])"),"YYYY")=$B$2)
Where "EvaluateString" is:
Function EvaluateString(strTextString As String)
Application.Volatile
EvaluateString = Evaluate(strTextString)
End Function
I feel like I am close, and it may just be a syntax error at this point. I need to find a way for the string to be recognized as a structured reference.
You could use INDIRECT. E.g. With "Table1" entered into A1, TEXT(INDIRECT(A1&"[[openDate]:[openDate]]"),"YYYY") will be understood as TEXT(Table1[[openDate]:[openDate]],"YYYY")
Keep in mind, however, that too many INDIRECT formulas slows down Excel considerably. Since you're also using VBA, you could also consider something like this:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A1")) Is Nothing Then
TableString = Target.Value
Built_Formula = "=IFERROR(SUMPRODUCT((TEXT(" & TableString & "[[openDate]:[openDate]],""YYYY"")=$F$2)*(TEXT(" & TableString & "[[openDate]:[openDate]],""MMMM"")=F$3)/" _
+ "COUNTIF(" & TableString & "[[orderNum]:[orderNum]]," & TableString & "[[orderNum]:[orderNum]]&"""")),"""")"
Range("A2").Formula = Built_Formula
Else
End If
End Sub
This would rebuild the formula each time you change the value in A1 (e.g. make it a dropdown of those table names).
Or even do the calc in VBA! :)

Excel Macro copy paste in right till data in left cell

I have two excel columns in a worksheet, consider as A(left) and B(right) and I have recorded a macro where it will calculate a formula and copy/paste it to all the right side columns till where the left side column has data. but when next time some extra data is added to the left column and when I run a macro to copy/paste then it is only considering the previous range but not extending to the newly added cells.
example : A1:A5 is left side and B1:B5 is the right side and my formula in B range which is right range calculate based on A1:A5 and my macro works fine and restricted only to B1:B5 even when I added new data like A1:A10 only copying B1:B5. what is the method I can use my macro automatically till the data range of A side column?
Better next time, you provide a screenshot of your data & also VBA code in question.
You are using a static range, while you require is dynamic range.
try this
Sub test()
Dim i As Integer
i = WorksheetFunction.CountA(Range("A:A"))
Range("B1:b" & i).Select 'instead of selecting you can provide your formula to whole range.
End Sub
For any other issue, feel free to comment
As per your description just try below.
Sub FillFormula()
Dim i As Long
i = Range("A1").End(xlDown).Row
Range("B1").FormulaR1C1 = "=RC[-1]+5"
Range("B1").AutoFill Destination:=Range("B1:B" & i)
End Sub

How can I convert between 2 currencies USD and AED #rate of 3.68 throughout the rest of my workbook but only in selected cell ranges on each sheet?

For example. I want my intro sheet "Main Sheet" to have an option to switch the workbook between currencies. USD and AED at the rate of 3.68. Some cells are referencing other cells in different sheets, so I don't want to change the cell references, I only need to calculate the rate in specific cells within each sheet.
How can I accomplish this preferably using a check box or button for easy converting from the start. I'm using excel for Mac. Thank you
Create a cell with a validation drop-down allowing to choose between AED and USD. Convert that cell to a named range for easy referencing throughout the workbook. You might call it "Curr", short for "Currency" (short because it will be used often).
I recommend that you create a similar cell somewhere where you enter the rate, currently 3.68 but plan on changing the rate in that cell only and have it applied to all the workbook. Name that cell as "Rate".
Now all cells containing values which you may want switched would be subject to the following formula. =[CellValue] * IF(Curr = "AED", Rate, 1). This formula presumes that the values are all entered in USD. If they are entered in AED the formula should look as follows. = ROUND([CellValue] / IF(Curr = "AED", 1, Rate), 2)
As you see, this solution would require the original cell values to be recorded somewhere, meaning, the cells used for data capture can't be the same as the ones used for data display. If you wish to insist on capture and display being in the same cell you would need code to do the conversion.
On the face of it this seems simple: When the Curr selection is changed, all cells with affected values are re-calculated. In practise this would end in disaster because there are 1001 ways in which something might go wrong and then you would lose all your data, not knowing whether the values are USD or AED at that moment.
Therefore the starting point needs to be to separate data capture and data display. Once that is done workheet functions might well be not only the easiest but also the most efficient way of achieving what you want.
I'm going to assume that you want to have the conversion on the input cell and not all of your cells are formulas and that a lot of the cells you want to convert are values. You should seriously consider the answer to split out input vs display, it will be much more foolproof and protected from any logic that may break your workbook.
If you're keen on this pathe then do the following, but, before you do ... BACKUP YOUR WORKBOOK. Any tests I've done with the below code are not breaking but I don't have your workbook, therefore, I make no guarantees.
Firstly, you need a cell that gives you the current exchange rate. You need to give that cell a named range of ExchangeRate.
In my workbook, that cell contains a formula ...
=IF(B1="USD",1,3.68)
It looks like this ...
... and cell B1 has a validation attached to it that allows you to select from 2 currencies, AED or USD.
You said you want to be able to ensure that only a selection of cells will be converted. To make sure we ring fence just those cells, you need to create a named range ON EACH SHEET that includes all of those cells.
The name of that range needs to be called CellsToConvert and you can do that through the Name Manager. When creating the named range, make sure you specify the worksheet you're creating it for, do not selected the "Workbook" option.
... the below shows the sporadic range I used on the first sheet. All coloured cells a part of that range. The green cells contain values and the yellow cells contain formulas.
At the end of the day, that range can be huge and across different sheets but it should work.
Now, add the following code into the ThisWorkbook object within the VBA editor ...
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim objCell As Range, dblExRate As Double, strFormula As String, objSheet As Worksheet
Dim strNewFormula As String, strOpeningChar As String, bIsFormula As Boolean
Dim objCells As Range, strError As String, strExRateRangeName As String
strExRateRangeName = "ExchangeRate"
dblExRate = Range(strExRateRangeName)
Application.EnableEvents = False
For Each objSheet In Worksheets
On Error Resume Next
strError = ""
Err.Clear
Set objCells = objSheet.Range("CellsToConvert")
strError = Err.Description
On Error GoTo 0
If strError = "" Then
For Each objCell In objCells
strFormula = objCell.FormulaR1C1
bIsFormula = False
' Check to make sure this field contains a formula.
If Left(strFormula, 1) = "=" And objCell.NumberFormat <> "#" Then
bIsFormula = True
End If
If dblExRate = 1 Then
' Base currency selected.
' Check to see if the cell contains a formula, if it does,
' convert it back to a value
If bIsFormula Then
' It's a formula and the cell is not set to text, proces it back
' to its original value, that could still be a formula.
' Remove all of the exchange rate components we would've added as
' a part of this routine.
strNewFormula = Replace(strFormula, ") * " & strExRateRangeName, "")
' Check to see if the formula has changed against the previous statement,
' if it has, then it contained the custom additions, otherwise, it didn't.
If strFormula <> strNewFormula Then
strNewFormula = Mid(strNewFormula, 3)
' Check to see if the new value is numeric, if it is, remove the leading
' equals sign as it wasn't originally a formula, or, at least it doesn't
' need to be a formula.
If IsNumeric(strNewFormula) Then
objCell.Value = strNewFormula
Else
objCell.FormulaR1C1 = "=" & strNewFormula
End If
End If
End If
Else
' Something other than the base currency has been selected.
strNewFormula = objCell.FormulaR1C1
If InStr(1, strNewFormula, strExRateRangeName, vbTextCompare) = 0 Then
If bIsFormula Then strNewFormula = Mid(objCell.FormulaR1C1, 2)
objCell.FormulaR1C1 = "=(" & strNewFormula & ") * " & strExRateRangeName
End If
End If
Next
End If
Next
Application.EnableEvents = True
End Sub
... once you've done all of the above, it should work for you. Performance could be tested if the workbook is large but that's something you'll need to check for yourself.
If you change a cell and it's within one of those ranges AND the currency of USD is not selected, you'll see the input value changed to a formula after you hit enter. That's pretty neat when you think about it but may not be for you.
One last thing to note, if your range contains broken links, the calculation for that sheet will fail and my code will not notify you of that.
This adds another option for you but is riskier than the first answer. There's nothing like options. :-)

Convert VBA Simple Macro to LibreOffice Macro

I have the following macro, which takes a sparse set of data and copies the only entry from each row into the left-most column. Example of data
I am hoping someone could rewrite this macro into one that will work with the same data in LibreOffice.
Sub Macro1()
Dim rng As Range
Set rng = Selection
For Each row In rng.Rows
For Each cell In row.Cells
If cell <> "" Then
Debug.Print cell
row.Cells(1) = cell
End If
Next
Next
End Sub
Example of data
    
Have a look at Andrew Pitonyak's macro document. Section 6 is the area that deals with Calc macros.
Get the selected rows with code similar to section 6.9 Fill selected range with text.
Also use code similar to section 6.14 Display all data in a column.

adding new rows in excel without breaking a vba macro that uses Range.Value

I've written a macro in VBA that simply fills in a given cell's value from another cell in that sheet. I do this for lots of cells in the sheet, and I'm doing it like so:
Range("B3").Value = Range("B200")
Range("B4").Value = Range("B201")
'etc.
Now, I am often adding values by inserting new rows, so I might insert a new row
between B200 and B201, which will break the macro because it doesn't autoupdate when
I insert the new row.
How can I code the macro so it autoupdates the cell references when I insert new rows or columns?
My suggestion would be to make sure the ROW you want to retrieve values from has a unique value in it that you can .FIND anytime you want, then grab your values from column B of that found cell's row. So right now you want to get a value in B200 and A200 always has the text in it: "Final Total" and that is unique.
Dim MyRNG As Range
Set MyRNG = Range("A:A").Find("Final Total", LookIn:=xlValues, LookAt:=xlWhole)
Range("B3").Value = Range("B" & MyRNG.Row)
Range("B4").Value = Range("B" & MyRNG.Row + 1)
This is not an answer but an alternative.
Naming your range is the way to go as Shiin suggested but then if you have 500 cells then like I mentioned earlier, naming 500 cells and using them in your code can be very painful. The alternative is to use smart code. Let's take an example
Let's say you have a code like this
Sub Sample()
Range("B3").Value = Range("B200")
Range("B4").Value = Range("B201")
Range("B5").Value = Range("B201")
' And
' So On
' till
Range("B500").Value = Range("B697")
End Sub
The best way to write this code is like this
Sub Sample()
Dim i As Long
For i = 200 To 697
Range("B" & i - 197).Value = Range("B" & i)
Next i
End Sub
and say if you insert a line at say row 300 then simply break the above code in two parts
Sub Sample()
Dim i As Long
For i = 200 To 299
Range("B" & i - 197).Value = Range("B" & i)
Next i
For i = 301 To 698
Range("B" & i - 197).Value = Range("B" & i)
Next i
End Sub
So every time you insert a row, simply break the for loop into an extra part. This looks tedious but is much better than naming 500 cells and using them in your code.
If you are planning to use the macro only once (i.e for 1 time use) then read ahead.
If you are worried that when the user inserts the row then the cells are not updated then you can instead of assigning a value, assign a formula.
For example
Range("B3").Formula = "=B200"
This will put a formula =B200 in cell B3. So next time when you insert a row so that the 200th row moves it's position, you will notice that the formula automatically gets updated in cell B3
HTH
Try giving a name to the range. If you refer to the range by name Excel searches for it and retrieves the rows that defines it. Range names update their definition when new rows are added.
Adding to the above, i think this tutorial illustrates my point:
http://www.homeandlearn.co.uk/excel2007/excel2007s7p6.html this is how to define the name of the range.
This tutorial explains how to use it on macros and vba:
http://excel.tips.net/T003106_Using_Named_Ranges_in_a_Macro.html
I hope this helps :D

Resources