I'm working within Microsoft Publisher.
I have a macro in module1 (that works) called "Export_as_Image".
I want to run this macro whenever I press F8.
I have created module2 with this code:
Sub SetKey()
Application.OnKey "{F8}", "Export_as_Image"
End Sub
When I run module2 I recieve the error "Compile error: Method or data member not found".
When I press F8 within Publisher, nothing happens.
Can anyone suggest what I may have done wrong, or a code to help me achieve what I'm looking for?
Thanks!
After some experimenting I found that it's needed to run SetKey for the first time firstly. You may call it from an event-driven sub (Workbook_Open, for example). Looks like running it for the first time, registers the key shortcut to the specified code.
Also, add the full path to the code you need to run: "Module2.Export_as_Image".
Update (reply to Charlie's comment below)
You can even move SetKey's code to Workbook_Open and delete SetKey (see image below).
First some definitions:
Access Program Name: ATS
Web tool name: C360
C360 doesn't always display readystate indicators in a timely fashion, so ATS is set to time out after five seconds to ask the user what to do next (Error 100). I'm using a form as a message box to allow for better formatting. This button (code below) is what the user presses to capture the data displayed in C360, despite not being in readystate.
I have two forms, one called Basic Ticksheet and one called Advanced Ticksheet (some of my users are a bit overstimulated if presented with too much information) that run the same functions, but display the data differently. Basic and Advanced cannot be open at the same time or read-write errors occur. As such, when Error 100 occurs, and the user presses this button, it is supposed to run the advanced code first, and if an error occurs, re-route to the basic code.
I cannot figure out why Error 2450 ('Microsoft Access Cannot find the referenced form 'Advanced Ticksheet') is firing when the user is using Basic Ticksheet. The code works flawlessly when I manually step through line-by-line. When I press 'Debug' it highlights the line beginning with: Call forms ("Basic Ticksheet"). If I step through using Debug, it also works flawlessly.
What am I doing wrong? Is there a better way to accomplish this?
Thank you in advance for any help you can offer!
Private Sub BTN_CaptureCase_Click()
DoCmd.Close acForm, "Alert Error 100", acSaveYes
Advanced_Ticksheet:
On Error GoTo Basic_Ticksheet
Call Forms("Advanced Ticksheet").Controls("Primary_Review").Form.BTN_CaptureCaseWithWM_Click
Exit Sub
Basic_Ticksheet:
Call Forms("Basic Ticksheet").Controls("BasicCapture").Form.BTN_CaptureCaseWithWM_Click
End Sub
The BTN_CaptureCaseWithWM_Click code is as follows (which calls additional code that is also marked as public):
Sub BTN_CaptureCaseWithWM_Click()
CaptureC360CaseData
ExtractCaseDetails
WMTFrameClick
ExtractWMTDetails
End Sub
The answer to this was as simple as the form containing the function wasn't actually the focus. Totally beating myself up over it, too!
I added a line to ensure the correct form was the focus before calling the code in the form. Of course, I also tooled the flow to make it work better, in the process.
Sub BTN_CaptureCase_Click()
DoCmd.Close acForm, "Alert Error 100", acSaveYes
If CurrentProject.AllForms("Basic Ticksheet").IsLoaded Then
Forms("Basic Ticksheet").SetFocus
Call Forms("Basic Ticksheet").Controls("BasicCapture").Form.BTN_CaptureCaseWithWM_Click
Application.Echo True
ElseIf CurrentProject.AllForms("Advanced Ticksheet").IsLoaded Then
Forms("Advanced Ticksheet").SetFocus
Call Forms("Advanced Ticksheet").Controls("Primary_Review").Form.BTN_CaptureCaseWithWM_Click
Application.Echo True
ElseIf CurrentProject.AllForms("Super Basic Ticksheet").IsLoaded Then
Forms("Super Basic Ticksheet").SetFocus
Call Forms("Super Basic Ticksheet").Controls("SuperBasicCapture").Form.BTN_CaptureCaseWithWM_Click
Application.Echo True
Else: MsgBox "Error 100 routing failure. Please notify ATS Administrator!"
End If
I have a sub that calls on ActiveWorkbook.RefreshAll to bring new data in from an XML source, and then performs multiple modifications to it. The problem is that not enough time is given for the RefreshAll command to finish, so the following subs and functions end up not executing correctly, which result in repeated rows not being correctly erased.
I have tried using Application.Wait and the Sleep function, but they seem to pause the refresh process too. I simply want the rest of the code to wait until the refresh process finishes before executing the rest of the code.
Any ideas on how to implement this? Right now I was only able to fix it by not calling on RefreshAll, which gives me the idea of implementing a second flow to be executed afterwards, but that's not a good workaround.
Please let me know if any of this wasn't clear. Thanks
EDIT
So I tried a few suggestions from the posts below, and this is what I was able to come up with.
Doing a "record macro" and then UNCHECKING the "Enable background refresh" in the table properties did not result in anything. I did a refresh as well afterwards. This was the result of the recorded macro:
With ActiveWorkbook.Connections("XMLTable")
.Name = "XMLTable"
.Description = ""
End With
ActiveWorkbook.Connections("XMLTable").refresh
The class ActiveWorkbook.Connections does NOT have a BackgroundQuery option so that I can set it to False. Any ideas?
Just to be clear. This is an XML file hosted on a website which Excel goes and imports into a table. I then call that data into a pivot and other things. The goal here is to allow the import process from the website to the table to finish BEFORE executing any other commands.
Thanks
EDIT2:
After a little more research, I have found this page: http://www.mrexcel.com/forum/excel-questions/564959-execute-code-after-data-connection-refresh-finished.html
It appears that an XML type of connection does not have a BackgroundQuery boolean. That option is only available for ODBC and OLEDB connections, which are types xlConnectionTypeODBC and xlConnectionTypeOLEDB, respectively. The XML connection I am using is of type xlConnectionTypeXMLMAP which does not have a BackgroundQuery option.
Does anyone have any idea on where to go from here? The only solution I have in mind right now is to make two seperate macro buttons on the excel sheet, one for refreshing and one for data modification, but I'd rather keep that option to the very last.
I had the same issue, however DoEvents didn't help me as my data connections had background-refresh enabled. Instead, using Wayne G. Dunn's answer as a jumping-off point, I created the following solution, which works just fine for me;
Sub Refresh_All_Data_Connections()
For Each objConnection In ThisWorkbook.Connections
'Get current background-refresh value
bBackground = objConnection.OLEDBConnection.BackgroundQuery
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
'Refresh this connection
objConnection.Refresh
'Set background-refresh value back to original value
objConnection.OLEDBConnection.BackgroundQuery = bBackground
Next
MsgBox "Finished refreshing all data connections"
End Sub
The MsgBox is for testing only and can be removed once you're happy the code waits.
Also, I prefer ThisWorkbook to ActiveWorkbook as I know it will target the workbook where the code resides, just in case focus changes. Nine times out of ten this won't matter, but I like to err on the side of caution.
EDIT: Just saw your edit about using an xlConnectionTypeXMLMAP connection which does not have a BackgroundQuery option, sorry. I'll leave the above for anyone (like me) looking for a way to refresh OLEDBConnection types.
Though #Wayne G. Dunn has given in code. Here is the place when you don't want to code. And uncheck to disable the background refresh.
DISCLAIMER: The code below reportedly casued some crashes! Use with care.
according to THIS answer in Excel 2010 and above CalculateUntilAsyncQueriesDone halts macros until refresh is done
ThisWorkbook.RefreshAll
Application.CalculateUntilAsyncQueriesDone
You must turn off "background refresh" for all queries. If background refresh is on, Excel works ahead while the refresh occurs and you have problems.
Data > Connections > Properties > (uncheck) enable background refresh
Here is a solution found at http://www.mrexcel.com/forum/excel-questions/510011-fails-activeworkbook-refreshall-backgroundquery-%3Dfalse.html:
Either have all the pivotcaches' backgroundquery properties set to False, or loop through all the workbook's pivotcaches:
Code:
For Each pc In ActiveWorkbook.PivotCaches
pc.BackgroundQuery = False
pc.Refresh
Next
this will leave all pivotcaches backgroundquery properties as false. You could retain each one's settings with:
Code:
For Each pc In ActiveWorkbook.PivotCaches
originalBGStatus = pc.BackgroundQuery
pc.BackgroundQuery = False
pc.Refresh
pc.BackgroundQuery = originalBGStatus
Next
This may not be ideal, but try using "Application.OnTime" to pause execution of the remaining code until enough time has elapsed to assure that all refresh processes have finished.
What if the last table in your refresh list were a faux table consisting of only a flag to indicate that the refresh is complete? This table would be deleted at the beginning of the procedure, then, using "Application.OnTime," a Sub would run every 15 seconds or so checking to see if the faux table had been populated. If populated, cease the "Application.OnTime" checker and proceed with the rest of your procedure.
A little wonky, but it should work.
Try executing:
ActiveSheet.Calculate
I use it in a worksheet in which control buttons change values of a dataset. On each click, Excel runs through this command and the graph updates immediately.
This worked for me:
ActiveWorkbook.refreshall
ActiveWorkbook.Save
When you save the workbook it's necessary to complete the refresh.
Here is a trick that has worked for me when some lines of VBA code have trouble executing because preceding lines haven't completed doing their thing. Put the preceding lines in a Sub. The act of calling the Sub to run those lines may help them finish before subsequent lines are executed. I learned of this trick from https://peltiertech.com/ and it has helped me with timing issues using the Windows clipboard.
If you're not married to using Excel Web Query, you might try opening the URL as a separate Workbook instead. Going that route lets you work on the resulting data once the web request completes, just like if you turn off "Enable background refresh."
The nice thing is though, Excel displays a progress bar during the request, instead of just freezing up / showing a load message in the destination cell.
See my answer on this question: How can I post-process the data from an Excel web query when the query is complete?
The tradeoff of that approach is you have to manage processing the data you get back yourself - Excel won't put it in a given destination for you.
We ended up going this route after we tried something pretty similar to what you seem to have been doing.
I was having this same problem, and tried all the above solutions with no success. I finally solved the problem by deleting the entire query and creating a new one.
The new one had the exact same settings as the one that didn't work (literally the same query definition as I simply copied the old one).
I have no idea why this solved the problem, but it did.
I tried a couple of those suggestions above, the best solution for me was to disable backgroundquery for each connection.
With ActiveWorkbook.Connections("Query - DL_3").OLEDBConnection
.BackgroundQuery = False
End With
For Microsoft Query you can go into Connections --> Properties and untick "Enable background refresh".
This will stop anything happening while the refresh is taking place. I needed to refresh data upon entry and then run a userform on the refreshed data, and this method worked perfectly for me.
I have had a similar requirement. After a lot of testing I found a simple but not very elegant solution (not sure if it will work for you?)...
After my macro refresh's the data that Excel is getting, I added into my macro the line "Calculate" (normally used to recalculate the workbook if you have set calculation to manual).
While I don't need to do do this, it appears by adding this in, Excel waits while the data is refreshed before continuing with the rest of my macro.
For me, "BackgroundQuery:=False" did not work alone
But adding a "DoEvents" resolved problem
.QueryTable.Refresh BackgroundQuery:=False
VBA.Interaction.DoEvents
I know, that maybe it sounds stuppid, but perhaps it can be the best and the easiest solution.
You have to create additional Excel file. It can be even empty.
Or you can use any other existing Excel file from your directories.
'Start'
Workbooks.Open("File_where_you_have_to_do_refresh.xlsx")
Workbooks("File_where_you_have_to_do_refresh.xlsx").RefreshAll
Workbooks.Open("Any_file.xlsx)
'Excell is waiting till Refresh on first file will finish'
Workbooks("Any_file.xlsx).Close False
Workbooks("File_where_you_have_to_do_refresh.xlsx").Save
or use this:
Workbooks("File_where_you_have_to_do_refresh.xlsx").Close True
It's working properly on all my files.
What I've done to solve this problem is save the workbook. This forces it to refresh before closing.
The same approach works for copying many formulas before performing the next operation.
I am dealing with a good amount of workbooks with many procedures. The deeper I go in my project, the more I try to write functions or sub to process common tasks.
First of all, as the final user will be an average Excel user, I decided to protect and centralize all my code in a personal macro workbook. It sounds to me a good idea, but maybe some of you, Excel gurus, don't agree.
Then I wrote a Public Sub ErrorHandler () to deal with errors in all my procédures. I am fully aware of the Error Bubble Up. My procedure will be soemthing like this:
' All déclarations are outside
Public Sub ErrorHandler ()
Lg1 = Err.Number ' Lg1 is Long
Select Case Lg1
Case 1004
MsgBox ("File is not fund. Please verify its path.")
Exit Sub '!EDITED! <GoTo NextCode:> is much better indeed
' ...more code with other errors...
End Select
NextCode: ' !EDITED!
' ...some cleaning and initialize variables to nothing or 0
Lg1 = 0
Err.Clear
End Sub
Then I would like to use this sub in other sub this way :
On Error Go To MyError: ' <On Error call ErrorHandler> is not permited
'...more code....
MyError:
call ErrorHandler
Now a few questions:
Is it a good practice ? If not, what would you recommend ?
In case 1004 for example, when I say Exit sub, will the ErrorHandler sub itself go to end and do the cleaning stuff, or will it stop too ?
Any good hints about common and useful user defined errors (513-65535) ?
Thank you for help.
When you do "Exit Sub" in the ErrorHandler(), it will of course leave the ErrorHandler() immidiately and return to the caller, so no cleanup is done. You can of course remove the "Exit Sub", so your cleanup will run, but it will always be the same for all cases.
The Problem I see in this approach comes after returning from ErrorHandler(). You return to the caller with absolutely no idea what ErrorHandler() did. How are you going to continue from there? Catching and Displaying the Error is only half the solution. You need to come up with a way to continue from there. If it's a "common Task" you are in, you need to cancel/abort/resume that in some way or at least return some error code to it, so it knows to end gracefully.
VBA itself is a bit bad at this situation. We use http://www.everythingaccess.com/vbwatchdog.htm in our projects to handle this problem as it provides a powerful generic error handling method, quite as you aim to do.
I am working on a project where I need to return a word document back to a certain state after it is printed. I have found a DocumentBeforePrint event but I cannot find a DocumentAfterPrint event.
Is it poorly documented or is there some other workaround that exists?
Here is one workaround based on subroutine names. I do not believe there is a specific DocumentAfterPrint event like you desire. Here's the code:
Sub FilePrint()
'To intercept File > Print and CTRL-P'
MyPrintSub
End Sub
Sub FilePrintDefault()
'To intercept the Standard toolbar button'
MyPrintSub
End Sub
Sub MyPrintSub()
Dialogs(wdDialogFilePrint).Show
'Your code here, e.g:'
MsgBox "I am done printing."
End Sub
UPDATE: Please note the gotchas in Will Rickards' answer below.
Looking at the application events I don't see it.
I don't see it in the document events either.
Please note on the workaround provided above, that is using the FilePrint and FilePrintDefault methods, you should read this site. These methods replace the built-in function. So you actually need to add code in there or have it generated for you to get word to actually print.
Also background printing can cause your code to execute before it has finished printing. If you really must run something after it has printed you'll need to disable background printing.
I don't believe any of the suggested work-arounds will work in Word 2010. However, I have had success by using the application.onTime() method at the end of the documentBeforePrint event to cause another procedure to be executed a few seconds later.