I would like to have an Excel sheet that displays a custom format. The custom format uses the contents of another cell. Here is an example:
Column A : Show a column of numbers in accounting format in the currency of cell(b1).
Cell(B1) : "XYZ"
The intention is that users can enter their own currency. I know there are formatting tools to do this in Excel but this is a question that implements a custom format based upon another cell contents. That's the real question...
A worksheet_change in the worksheet's private code sheet can alter the number formatting in column A.
private sub worksheet_change (byval target as range)
if not intersect(target, range("b1")) is nothing then
if len(cells(1, "b").value2) = 3 then
dim f as string
f = ucase(cells(1, "b").value2)
f = "0 \" & left(f, 1) & "\" & mid(f, 2, 1) & "\" & right(f, 1)
range(cells(2, "a"), cells(rows.count, "a").end(xlup)).numberformat = f
end if
end if
end sub
There are a maximum number of custom number formats that can be added to the existing number formats without deleting previously created cnfs; I think it's around 30 or so.
Assuming you want something like this:
..then just put this in the Sheet Module:
Sub worksheet_Change(ByVal Target As Range)
Dim sFormat As String
If Not Intersect(Target, Range("NumberFormat")) Is Nothing Then
sFormat = Chr(34) & Target.Value & Chr(34) & " "
Range("FormatWhat").NumberFormat = sFormat & "$#,##0;" & sFormat & "[Red]-$#,##0;-"
End If
End Sub
...and give B1 the Name NumberFormat in the Name Box:
...and likewise name some or all of column A "FormatWhat".
(Using Named Ranges avoids hard-coding references in your code. If you hard code cell address into your code, those references will be pointing at the wrong place if you (or a user) later adds new rows/columns above/to the left of those hard-coded references. Using Names avoids this, and makes for more robust code.
I almost never hard-code cell addresses in my code. I almost always use Excel Tables aka ListObjects to hold any data that VBA interacts with for the same reason...ListObjects are dynamic named ranges that Excel automatically expands/contracts to suit the data.)
Related
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! :)
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.
I've programmed an Excel document for my cryptocurrency portfolio. I add new currencies in a table, to which I pull data via an API to get the prices. I format the result like this: #.##0,00000000 [$ETH] (for Ethereum).
What I would like to achieve is, that when I input currency in column A, the value in column B is formatted to this currency.
Example: http://prntscr.com/p3sof8
So if I input XRP in A5, the value in B5 would read 0,00000000 XRP.
There's no fixed number of currencies I'd use, since I may add new every day.
Basic version of the above logic would be:
Input currency from column A into formatting argument like: #.##0,00000000 [$], except for BTC where the format would be: ฿0,00000000
.
Place this in your Sheet e.g "Sheet1"
Private Sub Worksheet_Change(ByVal Target As Range)
With Target
If .Count = 1 Then
If .Column = 1 Then
If .Value <> "BTC" Then
.Offset(, 1).NumberFormat = "0.00000000" & """ " & .Value & """"
Else
.Offset(, 1).NumberFormat = """" & ChrW(&HE3F) & """" & "0.00000000"
End If
End If
End If
End With
End Sub
No need to use VBA. You can do this with conditional formatting.
To do the specific example:
Go to cell B5
Click Conditional Formatting > New Rule...
Select Use a formula to determine which cells to format
As the formula, put =$A$5="XRP"
Click Format and on the Number tab, set it to custom such as #.##0,00000000 [$XRP]
I will leave it up to you to generalize this
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")
I'm not sure if this is even possible in Excel but this is what I need to do:
I have a column with a list of hotels and then another column which needs to pull data from each individual hotel's excel file. For example, cell A2 will have the name "Paris" for the hotel and then cell B2 will have the link:
='G:\Hotels\Paris\Paris - Monthly\[Paris_summary_2018.xlsm]Feb'!$CD$89
I have lots of hotels I need to do this for in different sheets. So I need the link to dynamically refer to whatever hotel is in column A and the title of the sheet, for example could I do something like this?
=''G:\Hotels\A2\A2 - Monthly\[A2_summary_2018.xlsm]Feb'!$CD$89
where A2 has the string "Paris". Also is there a way to dynamically refer to "Feb" depending on what sheet I am in the month will be the title
I am also open to using VBA for this.
As long as you don't mind using VBA, you can easily generate the links with something like this:
Sub generate_hotel_links()
Dim r As Range, c As Range
Dim s As String
' This is the range which all the hotel-locations are in
Set r = ThisWorkbook.Worksheets("Sheet1").Range("A1:A10")
On Error Resume Next
For Each c In r
' Generate the formula based on the current cell we are in
s = "=" & Chr(39) & "G:\Hotels\" & CStr(c) & "\" & CStr(c) & " - Monthly\[" & CStr(c) & "_summary_2018.xlsm]Feb" & Chr(39) & "!$CD$89"
' ...and put it in the neighbouring cell
c.Offset(0, 1).Formula = s
Next c
On Error Goto 0
End Sub
On Error Resume Next will make the macro continue no matter what error pops up - the ideal case would be some more robust error handling, or a check for if the workbook / sheet actually exists before attempting to write the formula, but I'll leave it to you to attempt to write this if you feel the need improve the macro.
If you want to just use generic Excel formulas, I'd advice having a look at the question and answers I posted in the comments to your question.
I am not sure if you can use HYPERLINK.
Like this:
=HYPERLINK("G:\Hotels\"&A2&"\"&A2&" - Monthly\["&A2&"_summary_2018.xlsm]Feb!"&$CD$89)