I have a global template with a customized ribbon. When I try to open a document, I receive error 4248, which claims the command can't run because no document is open.
The code in the onLoad sub is checking to make sure that the active document isn't the template or Normal.dotm because I don't want document variables written to those files via the called procedures. This worked fine in Word 2013, but I'm noticing that in Word 2016, it seems the ribbon onLoad procedure is running before a document in Word is open.
I've pinpointed the issue to the ActiveDocument.Name line, where I can trap the error and resume next, but that doesn't help me run the additional code after the determination is made that this isn't the global template document or Normal.dotm.
Public myRibbonNewNormal As IRibbonUI
Public bVisible As Boolean
Dim bDocSaved As Boolean
Sub onLoad_newNormal(ribbon As IRibbonUI)
Set myRibbonNewNormal = ribbon
On Error GoTo onLoadError
If ActiveDocument.Name = "Styles.dotm" Or ActiveDocument.Name =
"newNormal.dotm" Then
Exit Sub
ElseIf ActiveDocument.ReadOnly Then
Exit Sub
Else
'Call checkDocType
Call uncheckUpdateStyles
Call removeClientFooter
Call checkTemplate
If bDocSaved <> True Then
Call preventSave
Else
'do nothing
End If
End If
Exit Sub
onLoadError:
If Err.Number = 0 Then
Resume Next
ElseIf Err.Number = 5 Then
Resume Next
ElseIf Err.Number = 5825 Then
Resume Next
ElseIf Err.Number = 5903 Then
Resume Next
'ElseIf Err.Number = 5155 Then
' Resume Next
ElseIf Err.Number = 4248 Then
Resume Next
Else
MsgBox "This error is in the onLoad sub in the newNormal RibbonControl" _
& vbCrLf & vbCfLf & "Error: " & Err.Number & vbCrLf & Err.Description, ,
"Error"
End If
End Sub
(Please excuse my error handling. I was in a hurry when I wrote this code and just haven't gotten back around to doing it properly.)
Is there a way for me to wait to execute the onLoad sub until Word has opened the document (by simple double-click)? I'm sure I'm missing something here, but I'm struggling to find any information about this online.
Move the code to an application level event handler. The code will run each time a new document is opened, but that can be remedied if needed (see code comments).
Create a new class module and name the class module EventMngr.
Place the following code in the class module:
Public WithEvents appevent As Application
Private Sub appevent_DocumentOpen(ByVal Doc As Document)
'This code will only allow the event to fire once
Static hasOpened As Boolean
If hasOpened Then Exit Sub Else hasOpened = True
'Place Code Here...
End Sub
Then you can place this line in a normal module before any other subs/functions
'application level event handler
Public myEventHandler As New EventMngr
And last, place this in your Ribbon OnLoad sub:
'application event handler
Set myEventHandler.appevent = Application
Similar to the previous answer but possibly simpler is to add autoopen and autonew procedures to your code and capture the activedocument in those procedures rather than the onload procedure.
Related
Yes, Access is used and it cannot be changed.
I have a form class object, let's say:
Option Compare Database
Private Sub cmdCalculate_Click()
Dim Employee As String
Employee = InputBox("Enter Name of Employee")
If InStr(Employee, "Eka") > 0 Then
Call Hello
Else
Call Hello2
End If
End Sub
And I have one module. As you can see, I call each procedure from my form.
Option Compare Database
Option Explicit
Public Sub Hello()
MsgBox "Hello Eka"
End Sub
Public Sub Hello2()
MsgBox "Hello stranger"
End Sub
The issue I have is with the error handling implementation as here we have the subsequent procedures which I call. I tried to add a simple On Error GoTo - see below to an individual sub-procedure to display a nice message and break the entire script execution but yes, the sub procedure will show a nice message, you click OK to close it and the main script just continues running. Can you, please, direct me to a source where I can read more on the potential solution or assist with it? I found something about global error handling, but not sure if it is relevant.
Private Sub cmdCalculate_Click()
On Error GoTo errormessage
#TO-DO. VBA Code
Exit Sub
errormessage:
MsgBox "An error has occured. Please check your work."
End Sub
Here is a small sample.
MainProcedure calls SubProcedure1 and this calls SubProcedure2.
In SubProcedure2 there will be a division by zero error.
SubProcedure2 handles this error and reraise it to the upper procedure in the call stack: SubProcedure1.
SubProcedure1 also handles it and also reraises it, now to MainProcedure.
MainProcedure now shows the error. It can stop execution now if you want that.
Remark1: VBA unfortunately has no call stack information you could read at runtime. So in my example I always add the current procedurename as a new line to the top of the source property of the error.
So finally you can see where the error happened and how the call stack was.
But that is just an example.
Remark2: If you, for example, wouldn't place an active error handler in SubProcedure1 the error would bubble up itself to MainProcedure, but then you couldn't add your call stack information.
Public Sub MainProcedure()
On Error GoTo Catch
SubProcedure1
Finally:
Exit Sub
Catch:
MsgBox Err.Number & " : " & Err.Description & vbNewLine & vbNewLine & "- MainProcedure" & vbNewLine & Err.Source, vbCritical
Resume Finally
End Sub
Public Sub SubProcedure1()
On Error GoTo Catch
SubProcedure2
Finally:
Exit Sub
Catch:
Err.Raise Err.Number, "- SubProcedure2" & vbNewLine & Err.Source, Err.Description
Resume Finally
End Sub
Public Sub SubProcedure2()
On Error GoTo Catch
Dim value As Long
value = 0
Dim value2 As Long
value2 = 1 / value
Finally:
Exit Sub
Catch:
Err.Raise Err.Number, "- SubProcedure2" & vbNewLine & Err.Source, Err.Description
Resume Finally
End Sub
The code I currently works as follows: I type in a UID and then a message box shows the slack of a task. However, it's not possible to edit the Microsoft Project file while the message box is open.
Is there another object I can use in VBA to show the same output but allow me to work on the project file while having the output out? And, is it possible to have the output be in real time? In other words, if I make changes in my schedule, can I see the output constantly change if the slack changes as I make changes without having to run the application again?
Sub SlackFinder()
Dim User_UID, User_ID As Integer
Dim Slack As Variant
Dim NewSlack As Variant
User_UID = InputBox("Enter UID for slack:")
If User_UID = "" Then Exit Sub
On Error GoTo Error_Not_Found
User_ID = ActiveProject.Tasks.UniqueID(User_UID).ID
On Error GoTo Error_Collapsed
Slack = ActiveProject.Tasks.UniqueID(User_UID).TotalSlack
NewSlack = Slack / 480
MsgBox "Total Slack: " & NewSlack
Exit Sub
Error_Not_Found:
MsgBox "UID " & User_UID & " not found in " & ActiveProject.Name
Exit Sub
Error_Collapsed:
MsgBox "UID is present but cannot be selected. Perhaps it is collapsed?", vbOKOnly, "COLLAPSED UID?"
Exit Sub
End Sub
You can show real-time slack using a modeless userform. Create a userform in VBA, for example something that has a textbox for entering the task UID and a label to display the Total Slack value:
Then add this code to the UserForm module:
Private Sub UID_Change()
UpdateTotalSlack
End Sub
Sub UpdateTotalSlack()
On Error Resume Next
Me.TSlack = "Total Slack = " & ActiveProject.Tasks.UniqueID(Me.UID).TotalSlack / 480
End Sub
Add this to the Project module:
Sub ShowSlack()
UserForm1.Show False
End Sub
Private Sub Project_Change(ByVal pj As Project)
UserForm1.UpdateTotalSlack
End Sub
To start, call the ShowSlack procedure. This shows the user form modelessly (e.g. it floats above the MS Project window, allowing you to make changes in the schedule). Enter a Task UID in the textbox and the Total Slack will be displayed immediately and updated whenever changes are made to the schedule (thanks to the Change event code).
Project Module:
Private Sub Project_Change(ByVal pj As Project)
MsgBox "hi"
UserForm10.UpdateTotalSlack
End Sub
Module 29:
Sub ShowSlack()
UserForm10.Show False
End Sub
Userform10:
Dim User_UID As Variant
Dim TSlack As Variant
Private Sub TextBox3_Change()
User_UID = UserForm10.TextBox3.Value
UpdateTotalSlack
End Sub
Sub UpdateTotalSlack()
On Error Resume Next
If Not User_UID = "" Then
TSlack = ActiveProject.Tasks.UniqueID(User_UID).TotalSlack / 480
Else
TSlack = ""
End If
UserForm10.Label1.Caption = TSlack
End Sub
I currently have a code that on startup checks if the option "Trust access to the VBA project object model" is enabled or not.
In the case that it is not enabled, I need the program to open the macro security settings for easy access for the user.
I've made some code that does this in most cases, but I've encountered one problem that I don't know how to get around.
The code is as follows:
Private Sub Workbook_Open
If Not VBATrusted() Then
MsgBox "Trust access to the VBA project object model is not enabled" & vbNewLine & vbNewLine & _
"Please allow access by ticking the checkbox in the window that appears after clicking Ok"
Application.CommandBars.ExecuteMso ("MacroSecurity")
End If
End Sub
Function VBATrusted() As Boolean
On Error Resume Next
VBATrusted = (Application.VBE.VBProjects.Count) > 0
End Function
This code does its job unless the macro settings are as default "Disable all macros with notification", in which case I activate the macro and then get a run-time error "-2147467259 (80004005) Method 'ExecuteMso' of object '_CommandBars' failed"
This error will only occur on the first startup, as I do not have to activate the macros on consecutive startups, unless I move the file location.
I've tried pausing the macro for two seconds, but that did nothing for me, and neither did an error handler that tried to grab the error and then try executing the Application.CommandBars.ExecuteMso ("MacroSecurity") line again. It ended up with the same error.
The debugger tells me that the error is in the Application.CommandBars.ExecuteMso ("MacroSecurity") line, but that's probably not much of a surprise with that error message.
Thinking outside the box here...
What if you put a big message on the sheet which tells the user to activate macros, and then have an auto start macro delete or hide that message. That would bring the message to those who need it, but not the others.
Simple solution as proposed by #CLR in comments above, but didn't work when I initially tested it (user error!). All code goes in ThisWorkbook module:
Option Explicit
Private Sub Workbook_Open()
If Not VBATrusted() Then
MsgBox "Trust access to the VBA project object model is not enabled. " & vbNewLine & _
"Please allow access by ticking the checkbox in the window that appears"
Application.OnTime Now + TimeValue("00:00:01"), "ThisWorkbook.SetSecurity"
End If
End Sub
Function VBATrusted() As Boolean
On Error Resume Next
VBATrusted = (Application.VBE.VBProjects.Count) > 0
End Function
Sub SetSecurity(Optional foo)
Application.CommandBars.ExecuteMso "MacroSecurity"
End Sub
Slightly more elaborate: Add an MSForms.CommandButton to the worksheet which will open the security settings pane after user clicks it. Have the MsgBox prompt the user to click the button and then change the security settings.
In Module1, the Button's click event handler:
Option Explicit
Sub Button1_Click()
Call ThisWorkbook.SetSecurity
End Sub
In ThisWorkbook module:
Option Explicit
Private Sub Workbook_Open()
If Not VBATrusted() Then
MsgBox "Trust access to the VBA project object model is not enabled. " & vbNewLine & _
"Please allow access by:" & vbNewLine & vbNewLine & _
"1. Clicking the button on this sheet" & vbNewLine & _
"2. Ticking the checkbox in the window that appears"
End If
End Sub
Function VBATrusted() As Boolean
On Error Resume Next
VBATrusted = (Application.VBE.VBProjects.Count) > 0
End Function
Sub SetSecurity(Optional foo)
Application.CommandBars.ExecuteMso "MacroSecurity"
End Sub
The script below works great but I have to manually run the Initialize_handler routine every time I open Outlook for it to work.
Public WithEvents myOlApp As Outlook.Application
Public Sub Initialize_handler()
Set myOlApp = CreateObject("Outlook.Application")
End Sub
Private Sub myOlApp_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim prompt As String
prompt = "Are you sure you want to send " & Item.Subject & "?"
If MsgBox(prompt, vbYesNo + vbQuestion, "Sample") = vbNo Then
Cancel = True
End If
End Sub
As far as I can see to make this work automatically I should be able to add the below script to ThisOutlookSession. However this doesn't work and I have no idea why.
My macro security is set properly and it is running the code on startup but for some reason it doesn't work until I manually open the VBA editor click into Initialize_handler and press F5.
Private Sub Application_Startup()
Initialize_handler
End Sub
The convoluted method described here https://msdn.microsoft.com/en-us/library/office/ff865076.aspx indicates "The sample code must be placed in a class module".
I suggest you use the special class module ThisOutlookSession only. You could experiment with your own class module but if you just want this to work then replace all your code with this in ThisOutlookSession.
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim prompt As String
prompt = "Are you sure you want to send " & Item.Subject & "?"
If MsgBox(prompt, vbYesNo + vbQuestion, "Sample") = vbNo Then
Cancel = True
End If
End Sub
You can instead put it directly in ThisOutlookSession:
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
MsgBox "Sent somthing!"
End Sub
As simple as that. Note you need to change your outMail.Display to outMail.Display (True) and there you are, full code:
...
...
...
outMail.Display (True)
On Error Resume Next
bSent = outMail.sent 'This will NOT SEND. Used to get error.
If Err <> 0 Then
'Email was sent. Put followed actions here.
Else
'Email was not sent. Put followed actions here.
End If
On Error GoTo 0
Pros:
You get what you want
It's simple.
Cons:
Excel (or any other runtime you are running this code from) will freeze until you cancel or send email.
I have textbox and command button in the form1.
I will input path in the textbox
After the clicking the command button, workbook from the path location should open and need to the require macros code like copying , etc
when i tried using the . I getting error saying that file1.xlsx not found . plz help
Private Sub CommandButton1_Click()
Set wb1 = Workbooks.Open("file1")
End Sub
Private Sub TextBox1_Change()
Dim file1 As String
file1 = TextBox1.Value
End Sub
Private Sub UserForm_Click()
End Sub
The suggestions in the comments above will get you where you need to go, but if I may suggest... rather than using a textbox for the user to enter the name in, use the GetOpenFilename dialog. In this way you can ensure that the path is valid and the file actually exists. It also gives the user a nice GUI which is more like what they're used to for a File -> Open dialog. Something like this:
Private Sub CommandButton1_Click()
Dim vnt As Variant
On Error GoTo ErrorHandler
vnt = Application.GetOpenFilename("Excel Files (*.xlsx; *.xls; *.xlsm),*.xlsx;*.xls;*.xlsm", 1, "Please select the file to open")
If vnt = False Then Exit Sub
Application.Workbooks.Open (vnt)
ExitPoint:
Exit Sub
ErrorHandler:
MsgBox "Error " & Err.Number & vbCrLf & Err.Description
Resume ExitPoint
End Sub