make excel files use a global workbook function with vba? - vba

I am new to vba and I have problem understanding the workbooks in vba for excel files.
I have a macro called Addin.xla, this macro is used by 2000 files.
The problem that I have, is that the 2000 files have its own thisworkbook thats declares the function Workbook_BeforeClose. All the 2000 files have the exact same Workbook_BeforeClose.
What I want to do is to change the Workbook_BeforeClose function in all the 2000 files. Can I make the files use the Workbook_BeforeClose function in the Addin.xla file or must I change the same Workbook_BeforeClose function for all 2000 files?
If the later is the case, can someone show me how to write a macro that loops through all the files and updates this function automatic?

In the addin, handle the Application.WorkbookBeforeClose event.
But if you already have same code in 2000 files, you will have to remove it anyway. Otherwise it will run in addition to the handler you will define in the addin.
To auto remove:
Add a reference to Microsoft Visual Basic for Applications Extensibility X.X.
Then open each file in a loop and do
With currentWorkBook.VBProject.VBComponents("ThisWorkbook").CodeModule
.DeleteLines .ProcStartLine("Workbook_BeforeClose", vbext_pk_Proc), .ProcCountLines("Workbook_BeforeClose", vbext_pk_Proc)
End With

One solution would be to delegate the function to the addin:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
runCleanupCode
End Sub
where runCleanupCode is located in your addin. That means that if you want to make changes in the future, you only need to make them in the addin.
To change the code in all your sheets, you can follow this example.

Related

Edit Excel JumpList via VBA

I'm desperately trying to find how I can access the JumpList via VBA.
I currently have a bit of code that rips apart an Excel file into separate sheets and saves them all separately (about 50+ sheets) This unfortunately triggers the JumpList to be updated with each individual file which gets a bit annoying especially as Excel's own 'recently used files' doesn't get updated.
What I want to do is either a) stop them being added to the Jump List or b) recreate the jump list.
Having looked around the easiest way to do this is to reference the WindowsAPICodePack. Either from it itself or the PresentationFramework Library. However I can't for the life of me find how to access these from Excel. I've seen a lot of references to 'Tools/AddIns/Automation' However I can't see that in my Office 2013. Can anyone help at all as to how I could include these references or access the JumpList
The VBA work-around is to set the Excel's jump list file to read-only, after that whatever you do, the list is not updated. Once you are done and want to resume the list update feature just the set the file attribute as normal.
Sub manageJumpList(bAllowAdd As Boolean)
Dim strJumpFile As String
'/ Excel's Jump Location
strJumpFile = Environ("userprofile") & _
"\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\b8ab77100df80ab2.automaticDestinations-ms"
If Not bAllowAdd Then
'/Stop from updating
SetAttr strJumpFile, vbReadOnly
'<<After doing this, whatever files you open, they will not be added to recent list>>
Else
'/ Allow to update
SetAttr strJumpFile, vbNormal
End If
End Sub
Sub test()
'/ Stop Updating JumpList
Call manageJumpList(False)
'/ Start Updating JumpList
'Call manageJumpList(True)
End Sub

Where to save excel vba function to be able to access the function in other workbooks

I've created a function and I would like to be able to open any excel file and use this function just by typing into a cell '=function'. Is this possible and how do I do this? Where do I save the function?
Save the workbook containing the function as an add-in (either .xlam or .xla depending on your Excel version). You can then install it via the Add-in manager and call it from any workbook.
Note: you don't technically have to save it as an add-in - you can use a regular workbook - but then you will have to prefix the function name with the name of the workbook whenever you call it (e.g. =Personal.xlsb!some_function), and you will have to remember to open the workbook each time (or put it in your XLSTART or other startup folder).
I'm going to provide an answer with the example of Workbook A as wbCompany and Workbook B as wbEmployee.
From what I understood is that you have a function in wbCompany.getEmployeeCount() and you want to use this function in wbEmployee.
Firstly, rename the VBA Projects of both files to prevent duplicate project name. So we will rename the VBA Projects as vbaPrjEmp and vbaPrjCmp for wbEmployee and wbCompany workbooks respectively.
Secondly, you need to add wbCompany as reference to wbEmployee.
In wbEmployee, open the Microsoft Visual Basic for Applications window.
Select Tools > References.
In the References dialog that appears, click on Browse.
In the Add Reference dialog that appears, select Microsoft Excel Files from the Files of type box, select the file that you want to call (in this case wbCompany), and click on Open.
Choose OK to close the References dialog.
Finally in wbEmployee you can now refer/call the function from wbCompany in the following manner:
Sub compareEmpCount()
msgbox vbaPrjCmp.ThisWorkbook.getEmployeeCount
End Sub
You can store it either in your Personal.xlsb workbook or in an Excel add-in (.xlam). Search for either of these two to get you on the right track.
Well, first of all, you have to enable macros in Excel.
After that, you can open Excel VBA editor with Alt+F11, and create your function there.
Finally, you can use your function in a cell with '=function'.
Anyway, you can try this link to help you with your first vba function: http://www.fontstuff.com/vba/vbatut01.htm
Note: I've already created some functions on Excel, and it always worked for me. But always remember: you must enable macros in Excel.
You can read about macros here: https://support.office.com/en-nz/article/Enable-or-disable-macros-in-Office-documents-7b4fdd2e-174f-47e2-9611-9efe4f860b12

Timeout in Excel VBA

I was wondering if it's possible to create a VBA macro in Excel, which will save the file each X minutes. I've already figured how to initialize the macro after excel startup and I found on the google something like this should pause the macro
Application.Wait(Now + TimeValue("0:10:00"))
but this will also block the user input and he cannot make any changes during that time.
This is not a anti-crash protection , the reason why I need this, is that some users are forgetting to save the document regularly...
Thank you
Francis
The 2 examples of Simoco will work great, but if you want to prevent having to deal with unnecessary saves (especially if you're working on network files, or large files), you can do a check everytime there is a change in the worksheet.
Just use the Worksheet_Change function to do that, here is a possible pattern:
Private Sub Worksheet_Change(ByVal Target As Range)
If (Now > TimeStamp)
ThisWorkbook.SaveAs BlaBlaBlaBlaBla
TimeStamp = Now + TimeValue("0:10:00")
End If
End Sub
TimeStamp needs to be a global variable defined in each workbook.
Btw, make sure that saving your file every X minutes doesn't screw with the undo / redo function of excel. I remember I had unwanted behaviors in the past when using an auto-save function.
Other thought: Google document won't require this type of macro as there is no need to save.

Macro Fails, 'The macro may not be available in this...'

Hello and thank you in advance for your assistance.
I have some code that I admittedly borrowed from a site. It changes the sheet that is being displayed every X seconds. In my case 3 seconds. When I run it it will change to the next sheet one time and then error out after the 3 seconds.
The error I receive is "Cannot run the macro "C:\users\BenjaminSmith\Desktop\Book1.xlsm'!displaysheets'. The Macro may not be available in this workbook or all macros may be disabled."
Here is the code for my Macro
Sub displaysheets()
ShtNum = ActiveSheet.Index
ShtNum = ShtNum + 1
If ShtNum > Sheets.Count Then
ShtNum = 1
End If
Sheets(ShtNum).Activate
Application.OnTime Now + TimeValue("00:00:03"), "displaysheets"
End Sub
If I remove the line
Application.OnTime Now + TimeValue("00:00:03"), "displaysheets"
I can run the macro over and over and there are no issues. Other than the fact it doesn't continue on its own...
The spreadsheet is an XLSM.
MS VBA is 7.0.
Excel is 2010.
I am thinking maybe the issue is because the code is recursive?
Thanks for your suggestions.
Further from the comments...
The code didn't work because you didn't paste the code in a module. This is a very common mistake among new programmers. In such a case, Excel is unable to find the code as it searches the module.
#Siddharth Rout I had the code in 'ThisWorkbook' I inserted a module 'Module1' and moved the code there and everything works as expected. What is the difference with these two places?
I would recommend going through Chip Pearson's link HERE
Extract from the link if the link ever rots.
Standard Code Modules, also called simply Code Modules or just
Modules, are where you put most of your VBA code. Your basic macros
and your custom function (User Defined Functions) should be in these
modules. For the novice programmer, all your code will be in standard
modules. In addition to your basic procedures, the code modules
should contain any Declare statements to external functions (Windows
APIs or other DLLs), and custom Data Structures defined with the Type
statement.
Your workbook's VBA Project can contain as many standard code modules
as you want. This makes it easy to split your procedure into
different modules for organization and ease of maintenance. For
example, you could put all your database procedures in a module named
DataBase, and all your mathematical procedures in another module
called Math. As long as a procedure isn't declared with the Private
keyword, or the module isn't marked as private, you can call any
procedure in any module from any other module without doing anything
special.
Workbook And Sheet Modules are special modules tied directly to the
Workbook object and to each Sheet object. The module for the
workbook is called ThisWorkbook, and each Sheet module has the same
name as the sheet that it is part of. These modules should contain
the event procedures for the object, and that's all. If you put the
event procedures in a standard code module, Excel won't find them, so
they won't be executed. And if you put ordinary procedures in a
workbook or sheet module, you won't be able to call them without fully
qualifying the reference.
User Form Modules are part of the UserForm object, and contain the
event procedures for the controls on that form. For example, the
Click event for a command button on a UserForm is stored in that
UserForm's code module. Like workbook and sheet modules, you should
put only event procedures for the UserForm controls in this module.
Class Modules are used to create new objects. Class modules aren't
discussed here, except to say that a class module is used to handle
Application Event Procedures.
Try : (i use this code)
With Application
.EnableEvents = True 'needed
.OnTime EarliestTime:=Now + TimeSerial(0, 0, 3), Procedure:="displaysheets", Schedule:=True
End With
Try to put your timer in a global variable and add it each time you run the function, also configure OnTime to be schedulable
Global tmrTimer1
Sub displaysheets()
tmrTimer1 = Now + TimeValue("00:00:03")
'Enable the schedule
Application.OnTime tmrTimer1 , "displaysheets", , True
End Sub

Where Exactly To Store VBA

We receive Excel files daily from our field offices which I have to clean and re-format (about 110 columns and 500 rows-worth) using VBA.
I need to save my VBA as a macro so we can use it to clean up all the workbook we receive by running the macro and saving the edited sheet as a new worksheet by getting the name from UserForm Combobox items.
Where exactly should I store the VBA snippets? I mean when I open the Visual Basic panel, I have these three options:
Running The Code From Microsoft Excel Object :Sheets1(Sheet1)
Running the Code From An Inserted Module
Running the Code From User Form
If I am supposed to use options 1 or 2, how can I call the UserForm for saving the sheet?
I Recomend you to use modules (Option B)
Option C goes with option B, ill explain, you can create a sub in a module in option B, then you can do:
UserForm1.show
In Option B I would writte this code, but before trying this i recomend you to understand a bit more of vba
sub ClearWBs()
'opening workbook
Workbooks.Open Filename:="c:\book1.xls"
'your code
'your code
'below code for saving and closing the workbook
Workbooks("book1.xls").Activate
ActiveWorkbook.Save
ActiveWorkbook.Close
end sub
Use Module:
If your VBA code focusses on data summarization and manipulation I suggest you use a Module.(example is what you have described in your question).
Use Form:
If what you wan't to do requires a GUI(Graphical User Interface) then you'll have to resort to Form where you can design your GUI. (example is if you have many fields that the user needs to fill-up with distinct values in which you provide the choices)
Use Excel Object:
If what you wan't to do has something to do with events happening on Worksheet and/or Workbook, like for example whenever you add sheet the macro runs, or when you open or close the workbook the macro runs then you will have to write the macro in the Excel Object.
This is basically what i have in mind, hope this helps.
If you receive files that do not contain VBA and you need to apply the same code on those files all the time then I propose that you either save that code in your personal workbook.
You can see how to do that here: http://office.microsoft.com/en-ca/excel-help/copy-your-macros-to-a-personal-macro-workbook-HA102174076.aspx
This is nice because you can also tie it to keyboard shortcut or just have it always ready for you to use.
The disadvantage is that it will only be set up per user session per computer. What you can do is have that code all set up in a module and then import it into your personal workbook if you change session or if someone else has to do it.
Once it's done, you will not have to include the module in your files your receive again.