Forumula to create Table of Content in Excel - excel

I am looking for a formula which can directly be used in cells to read all the active tabs' name. Please refer the screen shot for the same.
There is also a =MID(CELL("filename"),FIND("]",CELL("filename"))+1,255) formula, but it is only giving the current tab name.
Though this is easily possible using macro, but would be great if can get formula for that.

There is a way to do this through formula's only,
Have a look here
It feels a bit double to post exactly how it's done, but the approach makes use of a named range and a lookup formula
It's fairly easy to do

I note you say formula but you could use a very simple User Defined Function (UDF) which goes in a standard module in the VBE (which you open with Alt+F11)
Option Explicit
Public Function GetTabName(ByVal tabIndex As Long) As String
GetTabName = ThisWorkbook.Worksheets(tabIndex).Name
End Function
The sheet index gets passed into the UDF as a parameter and the associated sheetname is returned.
If testing for visible sheet you could use the following, which has additional handling for sheet not found:
Option Explicit
Public Function GetTabName(ByVal tabIndex As Long) As String
Dim ws As Worksheet
On Error GoTo Errhand
Set ws = ThisWorkbook.Worksheets(tabIndex)
If ws.Visible Then
GetTabName = ThisWorkbook.Worksheets(tabIndex).Name
Else
GetTabName = "N/A"
End If
Errhand:
If Err.Number <> 0 Then
Select Case Err.Number
Case 9
GetTabName = "Sheet not found"
End Select
End If
End Function
UDF Limitations

Related

Why doesn't this simple VBA code work in Excel?

I'm quite new to programming with VBA (or any language, let's be honest). I'm trying to do a project for work, adding short sections at a time to see if the code still works, and trying out new code in a separate Sub. I've come across an error that I can't get around. The results don't change when they're the only line in a separate Sub.
The following code works:
ActiveWorkbook.Sheets("Template").Copy After:=ActiveWorkbook.Sheets("Student info")
Whereas the following code, when run, breaks with a 424 run-time error (object required). I've tried selecting instead of naming, still no luck. It does successfully copy the worksheet to the correct place, despite the error, but is called 'Template (2)'.
ActiveWorkbook.Sheets("Template").Copy(After:=ActiveWorkbook.Sheets("Student info")).name = "newname"
This is very confusing because the code below does work. Is it just that trying to name something after using 'add' does work, but after 'copy', it doesn't?
ActiveWorkbook.Sheets.Add(After:=ActiveWorkbook.Sheets("Student info")).name = Student_name
Thanks in advance for any help.
The reference (to the created copy) as return value (of a function) would be useful, but as Worksheet.Copy is a method of one worksheet (in opposite to Worksheets.Add what is a method of the worksheets-collection), they didn't created it. But as you know where you created it (before or after the worksheet you specified in arguments, if you did), you can get its reference by that position (before or after).
In a function returning the reference:
Public Enum WorkdheetInsertPosition
InsertAfter
InsertBefore
End Enum
Public Function CopyAndRenameWorksheet(ByRef sourceWs As Worksheet, ByRef targetPosWs As Worksheet, ByVal insertPos As WorkdheetInsertPosition, ByVal NewName As String) As Worksheet
'If isWsNameInUse(NewName) then 'Function isWsNameInUse needs to be created to check name!
'Debug.Print NewName & " alredy in use"
'Exit Function
'End If
With sourceWs
Dim n As Long
Select Case insertPos
Case InsertAfter
.Copy After:=targetPosWs
n = 1
Case InsertBefore
.Copy Before:=targetPosWs
n = -1
Case Else
'should not happen unless enum is extended
End Select
End With
Dim NewWorksheet As Worksheet
Set NewWorksheet = targetPosWs.Parent.Worksheets(targetPosWs.Index + n) 'Worksheet.Parent returns the Workbook reference to targetPosWs
NewWorksheet.Name = NewName ' if name already in use an error occurs, should be tested before
Set CopyWorksheetAndRename = NewWorksheet
End Function
usage (insert after):
Private Sub testCopyWorkSheet()
Debug.Print CopyAndRenameWorksheet(ActiveWorkbook.Sheets("Template"), ActiveWorkbook.Sheets("Student info"), InsertAfter, Student_name).Name
End Sub
to insert the copy before the target worksheet, change third argument to InsertBefore (enumeration of options).
New Worksheet.Name needs to be unique or you'll get an error (as long you not implemented the isWsNameInUse function to check that).
Also note that there is a difference between .Sheets and .Worksheets
You can get the links to the documentation by moving the cursor (with mouse left-click) in the code over the object/method you want more infos on and then press F1

Excel global variable reseted to empty when navigating through workbooks

I encounter a weird problem that I believe is related to Excel behavior, rather than to my code.
I have a global variable named "numEtape", which is an integer. My code consists in several steps where the user has to type data on a sheet, then press a button which saves the data in an array and increments the "numEtape", before going to the next step.
The code (simplified) looks like this :
Dim numEtape As Integer
Sub AjoutEssai()
numEtape = 2
UFPreAjoutInfos.Show 'Unrelated Userform that asks user for more informations, but doesn't modify "numEtape" or call any other macro
Call InterfaceFiller
End Sub
Sub InterfaceFiller()
Dim rangedubtn As Range
Dim btnPrecedent As Button
Select Case numEtape
Case 2
'Change value of some cells
Case 3
'Change value of some cells
Case 4
'Change value of some cells
Case Is >= 5
'Change value of some cells
Case Else
Debug.Print "Error"
End Select
Set rangedubtn = Sheets("Interface").Range("M3")
Set btnPrecedent = Sheets("Interface").Buttons.Add(rangedubtn.Left, rangedubtn.Top,rangedubtn.Width, rangedubtn.Height)
With btnPrecedent
.OnAction = "mSuivant"
.Caption = "Suivant"
.Name = "btnSuivant"
End With
End Sub
Sub mSuivant()
numEtape = numEtape + 1
Call InterfaceFiller
End Sub
I don't think the code itself is important, what I can expect from it, since I first call AjoutEssai(), is for numEtape to always be greater than 2.
However, when during the steps the user opens and close other excel/office files (that don't have any vba code/macros in it), excel seems to empty numEtape, which makes the Select Case go to the Case Else.
When does excel remove global variables from memory, and is there a way to prevent this behavior from happening?
Public numEtape As Long
A viable option is to use the word public like public numEtape As Long.
Then the variable will be saving its value for as long as the Excel workbook is opened. In older versions of VBA the word was Global (What is the difference between Dim, Global, Public, and Private as Modular Field Access Modifiers?)
Dim numEtape As Long
For using Dim outside of Sub or Function, the variable will be emptied, after the code is over. Take this snippet only:
Dim numEtape As Long
Sub MainTest()
numEtape = 23
End Sub
Once you run it and you hit End Sub the variable would be emptied as well. Check after running the MainTest():
Sub PrintingVariable()
Debug.Print numEtape
End Sub
If you want to save the value, there are 2 basic ways that work:
Write the value in an excel cell
Write the value in a database

Count Elements Selected Within a Slicer

I have started to work on an excel sheet and to run some calculated field I need a public function that returns the number of items selected within a single slicer.
I surfed the internet, but unfortunately I was only able to find VBA codes that actually do not work at all.
I have started to work with VBA only recently for the first time, so I have no experience with it.
I managed to use this code, which however is not a public function, so it cannot be used within calculated fields in pivot tables:
Private Sub Worksheet_PivotTableChangeSync(ByVal Target As PivotTable)
Const sSlicerName As String = "Producer"
On Error Resume Next
If Not IsError(Target.Slicers(sSlicerName).Name) Then _
Range("A1") = ActiveWorkbook.SlicerCaches("Slicer_" & sSlicerName) _
.VisibleSlicerItems.Count
End Sub
Did any of you face this issue and find a way to solve the problem?
Thank you in advance!
This should do the trick:
Public Function CountProducers()
Dim lngCount As Long
Const sSlicerName As String = "Producer"
Application.Volatile
On Error Resume Next
If Not IsError(Sheet1.PivotTables("PivotTable1").Slicers(sSlicerName).Name) Then
lngCount = ThisWorkbook.SlicerCaches("Slicer_" & sSlicerName) _
.VisibleSlicerItems.Count
End If
On Error GoTo 0
CountProducers = lngCount
End Function
As you can see your initial attempt was almost correct. A few things to note:
I added Application.Volatile part to make the formula dynamic, i.e. it will always re-calculate whenever you change your slicer selections.
On Error ... and If Not IsError... parts are not really required, you can simply leave lngCount part all by itself. If the formula won't find a slicer called "Producer" it will simply return #VALUE! error.

Excel VBA - Create rows for each updated value in DDE link cell

Lets say cell A1 has a DDE link, "=dde(realtime_stock_price". How can I send the value to a new row of a column everytime it updates? The Change() function doesnt work because its not the user doing the change. I tried the Calculate() function but not too sure how to implement it.
The formula "=dde()" isn't something I am familiar with. However you could just make your own dde function which calls your event code...
Public Function dde2(ByVal app as string,ByVal topic as string,ByVal item as string) as variant
with Application
channelNumber = .DDEInitiate(app,topic)
dde2 = .DDERequest(channelNumber, "Topics")
.DDETerminate(channelNumber)
end with
on error resume next 'incase macro doesn't exist
Application.run "dde2_postExec", dde2
on error goto 0
End Function
Sub dde2_postExec(ByVal message as string)
'do stuff...
end sub
Edit
According to this question the string passed to =DDE() is "Service|Topic!Item". Or in our case: "App|Topic!Item" which can be extracted from the string easily. However do note that DDE() is NOT a native function of excel. So it is unlikely we will be able to know how DDE() is working to make a clean solution.
Edit2
Given your comment, say DDE is streaming "12...13...14..." and you want to output B1 = 12, B2=13 and B3=14. Your code would be something like this [untested]:
Sub dde2_postExec(ByVal message as string)
dim v as variant: v=split(message,"...")
for i = 1 to ubound(v)-1
range("B" & i).value = v(i)
next
end sub
However do note that this may not work if called as a formula. The VBA runtime is designed such that when forumla's are evaluated they cannot write values to cells in the sheet, other than the cell that called them. For this reason you may be forced into hooking into worksheet events instead of using the formula's calculate event.

Modifying a spreadsheet using a VB macro

I have two spreadsheets... when one gets modified in a certain way I want to have a macro run that modifies the second in an appropriate manner. I've already isolated the event I need to act on (the modification of any cell in a particular column), I just can't seem to find any concrete information on accessing and modifying another spreadsheet (this spreadsheet is located on a different LAN share also... the user has access to both, though).
Any help would be great. References on how to do this or something similar are just as good as concrete code samples.
In Excel, you would likely just write code to open the other worksheet, modify it and then save the data.
See this tutorial for more info.
I'll have to edit my VBA later, so pretend this is pseudocode, but it should look something like:
Dim xl: Set xl = CreateObject("Excel.Application")
xl.Open "\\the\share\file.xls"
Dim ws: Set ws = xl.Worksheets(1)
ws.Cells(0,1).Value = "New Value"
ws.Save
xl.Quit constSilent
You can open a spreadsheet in a single line:
Workbooks.Open FileName:="\\the\share\file.xls"
and refer to it as the active workbook:
Range("A1").value = "New value"
After playing with this for a while, I found the Michael's pseudo-code was the closest, but here's how I did it:
Dim xl As Excel.Application
Set xl = CreateObject("Excel.Application")
xl.Workbooks.Open "\\owghome1\bennejm$\testing.xls"
xl.Sheets("Sheet1").Select
Then, manipulate the sheet... maybe like this:
xl.Cells(x, y).Value = "Some text"
When you're done, use these lines to finish up:
xl.Workbooks.Close
xl.Quit
If changes were made, the user will be prompted to save the file before it's closed. There might be a way to save automatically, but this way is actually better so I'm leaving it like it is.
Thanks for all the help!
Copy the following in your ThisWorkbook object to watch for specific changes. In this case when you increase a numeric value to another numeric value.
NB: you will have to replace Workbook-SheetChange and Workbook-SheetSelectionChange with an underscore. Ex: Workbook_SheetChange and Workbook_SheetSelectionChange the underscore gets escaped in Markdown code.
Option Explicit
Dim varPreviousValue As Variant ' required for IsThisMyChange() . This should be made more unique since it's in the global space.
Private Sub Workbook-SheetChange(ByVal Sh As Object, ByVal Target As Range)
' required for IsThisMyChange()
IsThisMyChange Sh, Target
End Sub
Private Sub Workbook-SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
' This implements and awful way of accessing the previous value via a global.
' not pretty but required for IsThisMyChange()
varPreviousValue = Target.Cells(1, 1).Value ' NB: This is used so that if a Merged set of cells if referenced only the first cell is used
End Sub
Private Sub IsThisMyChange(Sh As Object, Target As Range)
Dim isMyChange As Boolean
Dim dblValue As Double
Dim dblPreviousValue As Double
isMyChange = False
' Simple catch all. If either number cant be expressed as doubles, then exit.
On Error GoTo ErrorHandler
dblValue = CDbl(Target.Value)
dblPreviousValue = CDbl(varPreviousValue)
On Error GoTo 0 ' This turns off "On Error" statements in VBA.
If dblValue > dblPreviousValue Then
isMyChange = True
End If
If isMyChange Then
MsgBox ("You've increased the value of " & Target.Address)
End If
' end of normal execution
Exit Sub
ErrorHandler:
' Do nothing much.
Exit Sub
End Sub
If you are wishing to change another workbook based on this, i'd think about checking to see if the workbook is already open first... or even better design a solution that can batch up all your changes and do them at once. Continuously changing another spreadsheet based on you listening to this one could be painful.

Resources