I am maintaining a Word related vb6 project. When we download a word document from our server and make changes on it. Then click Close.
Then
the objWord_DocumentBeforeClose event will occur.
If the downloaded file has not uploaded back to server, then we prompt a yes/no message box Do you want to check-in the document?
If clicked on 'Yes' and if click again on ctrl+w before the check-in process completed, document will close without reaching the mobjWord_DocumentBeforeCloseevent.
I have added some code in the DocumentBeforeClose event to prevent closing the document if the check-in process is running using document variables.
Could anybody please explain me why the mobjWord_DocumentBeforeClose is not reaching at the second close click?
Below is my code.
Private Sub mobjWord_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
Dim objDoc As Project.Document
Dim objApp As Project.Application
Dim strProcess As String
Set objApp = New Project.Application
If objApp.Settings.RespondToWordEvents Then
Set objDoc = objApp.GetDocument(Doc)
'Check the document variable to seen if any process is running with the document
If objDoc.IsBusy = False Then
if objDoc.NotCheckedIn
If objDoc.DownloadProperties.WasCheckedOut Then
Select Case MsgBox("Do you want to check-in the document?", vbYesNoCancel + vbQuestion)
Case vbYes
If objApp.CheckInDocument(WordDocument:=Doc) Is Nothing Then
Cancel = True
End If
fDisebleCheckIn = True
Case vbNo
fDisebleCheckIn = True
Case vbCancel
Cancel = True
End Select
End If
Else
//some code
End If
Else
Cancel = True
strProcess = ProcessInProgress(objDoc, objApp)
MsgBox objApp.GetUIString("Unable to close the document " + strProcess + "process is running"), vbOKOnly + vbInformation
End If
End If
ErrorHandler:
objApp.Quit
Set objDoc = Nothing
Set objApp = Nothing
End Sub
I am not sure I fully understand you, but in VBA, so you can adapt for your VB6, I would have the following in a normal module
Public wdCustomWordApplication As clsCustomWordApplication
Sub Setup()
Dim w As Word.Application
Set w = New Word.Application
w.Visible = True
Set wdCustomWordApplication = New clsCustomWordApplication
wdCustomWordApplication.InitialiseCustomWordApplication w
End Sub
and then a class module called clsCustomWordApplication, like so
Private WithEvents wdWordApplication As Word.Application
Public Sub InitialiseCustomWordApplication(objWord As Word.Application)
Set wdWordApplication = objWord
End Sub
Private Sub wdWordApplication_DocumentBeforeClose( _
ByVal Doc As Word.Document, Cancel As Boolean)
MsgBox "Some question"
End Sub
Hope this helps.
Related
I was on MS Word 2007 where the macros worked fine.
After upgrading to MS Word 2016 the method Document_ContentControlOnExit does not fire.
Since I write very few MS Word macros I don’t keep up on fundamental change to the architecture. Has something changed in MS Word 2016 that would cause the code to stop working?
The set of methods will detect a Content Control exit. Then update all Content Controls with the same tag name with the new value.
I have put a breakpoint in Document_ContentControlOnExit. Then made some changes to a Content Control. Nothing.
Sub SetUp()
Set eventHandler.doc = ActiveDocument
End Sub
Sub Document_Close()
Call UpdateAllFields
End Sub
Private Sub Document_ContentControlOnExit(ByVal ContentControl As ContentControl, Cancel As Boolean)
ccTag = ContentControl.Tag
ccValue = ContentControl.Range.Text
Call updateValueInAllInstances(ccTag, ccValue)
End Sub
Sub updateValueInAllInstances(ccTag, ccValue)
Dim doc As Document
Dim ccs As ContentControls
Dim cc As ContentControl
Set doc = ActiveDocument
For Each cc In doc.SelectContentControlsByTag(ccTag)
' If the author of the document did not lock the contents, then perform a auto update.
If cc.LockContents Then
Respose = MsgBox("The author of this cookbook has deliberately designed this specific content control to not be editable.", 0, "Content Control is not editable", "", 1000)
Else
cc.Range.Text = ccValue
End If
Next cc
End Sub
Sub UpdateAllFields()
Dim objStory As Range
Dim objTOC As TableOfContents
Dim objTOA As TableOfAuthorities
Dim objTOF As TableOfFigures
Dim objIndex As Index
Application.ScreenUpdating = False
Application.DisplayAlerts = wdAlertsNone
For Each objStory In ActiveDocument.StoryRanges
UpdateFieldsInStory objStory
While Not (objStory.NextStoryRange Is Nothing)
Set objStory = objStory.NextStoryRange
UpdateFieldsInStory objStory
Wend
Next
For Each objTOC In ActiveDocument.TablesOfContents
objTOC.Update
Next
For Each objTOA In ActiveDocument.TablesOfAuthorities
objTOA.Update
Next
For Each objTOF In ActiveDocument.TablesOfFigures
objTOF.Update
Next
For Each objIndex In ActiveDocument.Indexes
objIndex.Update
Next
Application.DisplayAlerts = wdAlertsAll
Application.ScreenUpdating = True
End Sub
Private Sub UpdateFieldsInStory(iobjStory As Range)
Dim objShape As Shape
With iobjStory
.Fields.Update
Select Case .StoryType
Case wdMainTextStory, wdPrimaryHeaderStory, _
wdPrimaryFooterStory, wdEvenPagesHeaderStory, _
wdEvenPagesFooterStory, wdFirstPageHeaderStory, _
wdFirstPageFooterStory
For Each objShape In .ShapeRange
With objShape.TextFrame
If .HasText Then .TextRange.Fields.Update
End With
Next
End Select
End With
End Sub
I was hoping the code was not so out of date that it would keep working.
This mistake was what Cindy Meister pointed out. When I copied the macro code from the old to the new template I failed to notice the location in the Visual Basic Editor. Once the code was in the correct place it worked.
I open Word application in VB.Net by below code:
Dim appWord As New Microsoft.Office.Interop.Word.Application
appWord.Documents.Open("path")
appWord.Visible = True
I wanna subscribe the closing event of msword and run something before closing. I read this question and this article but I really don't know how to use in VB.Net
Add a reference (Project--References...) to Microsoft Word XX.0 Object Library.
XX depends on your version of MS Office i.e. 16.0 if your MS Office is 2016.
Add a command button named Command1 to form.
Add this code to form:
Option Explicit
Public WithEvents moWord As Word.Application
Private Sub Command1_Click()
' open test document
With moWord
.Documents.Open "J:\Test.docx" ' change document path according to actual file
'.WindowState = wdWindowStateNormal
.Visible = True
End With
End Sub
Private Sub Form_Load()
Set moWord = New Word.Application
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If Not (moWord Is Nothing) Then Set moWord = Nothing
End Sub
Private Sub moWord_DocumentBeforeClose(ByVal Doc As Word.Document, Cancel As Boolean) Handles moWord.DocumentBeforeClose
If MsgBox("Do you really want to close the document?", vbQuestion + vbYesNo + vbDefaultButton2) = vbNo Then Cancel = True
End Sub
I use 2007 Outlook.
I'm trying to get a code that upon creation of a new email prompts the user to pick one of the fixed radio button options as follows [A]: , [R]:, [F:] , [!]: , Blank (Option to get subject line blank).
I want that selection to be inserted into the subject line automatically.
I found code online but it errors out towards the end of the code.
Private Sub m_colInspectors_NewInspector(ByVal Inspector As Outlook.Inspector)
I pasted this code in the ThisOutlookSession module.
Option Explicit
Private WithEvents m_colInspectors As Outlook.Inspectors
Private WithEvents CurrentInspector As Outlook.Inspector
Private Sub Application_Startup()
Set m_colInspectors = Application.Inspectors
End Sub
Private Sub CurrentInspector_Activate()
Dim oMail As Outlook.MailItem
If Len(UserForm1.SelectedSubject) Then
Set oMail = CurrentInspector.CurrentItem
oMail.Subject = UserForm1.SelectedSubject
End If
Set CurrentInspector = Nothing
End Sub
Private Sub m_colInspectors_NewInspector(ByVal Inspector As Outlook.Inspector)
If TypeOf Inspector.CurrentItem Is Outlook.MailItem Then
If Inspector.CurrentItem.EntryID = vbNullString Then
UserForm1.SelectedSubject = vbNullString
UserForm1.Show
Set CurrentInspector = Inspector
End If
End If
End Sub
I created a form with radio button and a command button where I inserted the following code.
Option Explicit
Public SelectedSubject As String
Private Sub CommandButton1_Click()
If OptionButton1.Value = True Then
SelectedSubject = "Test"
End If
Hide
End Sub
This might get you want you want. Put it under ThisOutlookSession. When the user click on Sends this triggers, meaning they are not able to change the subject line before it is sent. I am using the UserForm1 and the code you are using for that. Add as many radiobuttons as you like and just amend the OptionButton1 to 2 and the value.
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim strSubject As String
Dim Prompt$
strSubject = Item.Subject
' Show RadioButtons
UserForm1.Show
' Set Subject Line as the value from the selected RadioButton
strSubject = UserForm1.SelectedSubject
' Set the message subject
Item.Subject = strSubject
strSubject = Item.Subject
' Test if Subject Line is empty
If Len(Trim(strSubject)) = 0 Then
Prompt$ = "Subject is Empty. Are you sure you want to send the Mail?"
If MsgBox(Prompt$, vbYesNo + vbQuestion + vbMsgBoxSetForeground, "Check for Subject") = vbNo Then
Cancel = True
End If
End If
End Sub
I have the below code. Originally, the code was a VBA macro that I built. It ended up working perfectly (sending word doc as an e-mail to a desired range of recipients, iterating through each row). The function begins at the Sub SendIt_Click (very last sub) in the code. The rest is for the add-in. When I click the button in Excel, the MsgBox's work, but the code doesn't send anything. It worked in Excel VBA, but I'm at a loss as to why it isn't working here.
Update: It does open the word doc, just doesn't send e-mail.
Imports Extensibility
Imports System.Runtime.InteropServices
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Core
<GuidAttribute("209AD741-0B95-4931-80CF-4DCE33B761C9"), ProgIdAttribute("MailMerge.Connect")> _
Public Class Connect
Implements Extensibility.IDTExtensibility2
Private applicationObject As Object
Private addInInstance As Object
Dim WithEvents SendIt As CommandBarButton
Public Sub OnBeginShutdown(ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnBeginShutdown
On Error Resume Next
' Notify the user you are shutting down, and delete the button.
MsgBox("MailMerge Add-in is unloading.")
SendIt.Delete()
SendIt = Nothing
End Sub
Public Sub OnAddInsUpdate(ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnAddInsUpdate
End Sub
Public Sub OnStartupComplete(ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnStartupComplete
Dim oCommandBars As CommandBars
Dim oStandardBar As CommandBar
On Error Resume Next
' Set up a custom button on the "Standard" command bar.
oCommandBars = applicationObject.CommandBars
If oCommandBars Is Nothing Then
' Outlook has the CommandBars collection on the Explorer object.
oCommandBars = applicationObject.ActiveExplorer.CommandBars
End If
oStandardBar = oCommandBars.Item("Standard")
If oStandardBar Is Nothing Then
' Access names its main toolbar Database.
oStandardBar = oCommandBars.Item("Database")
End If
' In case the button was not deleted, use the exiting one.
SendIt = oStandardBar.Controls.Item("My Custom Button")
If SendIt Is Nothing Then
SendIt = oStandardBar.Controls.Add(1)
With SendIt
.Caption = "Send to Mail Group with Outlook"
.Style = MsoButtonStyle.msoButtonCaption
' The following items are optional, but recommended.
' The Tag property lets you quickly find the control
' and helps MSO keep track of it when more than
' one application window is visible. The property is required
' by some Office applications and should be provided.
.Tag = "MailMerge"
' The OnAction property is optional but recommended.
' It should be set to the ProgID of the add-in, so that if
' the add-in is not loaded when a user clicks the button,
' MSO loads the add-in automatically and then raises
' the Click event for the add-in to handle.
.OnAction = "!<MyCOMAddin.Connect>"
.Visible = True
End With
End If
' Display a simple message to show which application you started in.
MsgBox("Started in " & applicationObject.Name & ".")
oStandardBar = Nothing
oCommandBars = Nothing
End Sub
Public Sub OnDisconnection(ByVal RemoveMode As Extensibility.ext_DisconnectMode, ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnDisconnection
On Error Resume Next
If RemoveMode <> Extensibility.ext_DisconnectMode.ext_dm_HostShutdown Then _
Call OnBeginShutdown(custom)
applicationObject = Nothing
End Sub
Public Sub OnConnection(ByVal application As Object, ByVal connectMode As Extensibility.ext_ConnectMode, ByVal addInInst As Object, ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnConnection
MsgBox("On Connection In MailMerge")
applicationObject = application
addInInstance = addInInst
' If you aren't in startup, manually call OnStartupComplete.
If (connectMode <> Extensibility.ext_ConnectMode.ext_cm_Startup) Then _
Call OnStartupComplete(custom)
End Sub
Private Sub SendIt_Click(ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, ByRef CancelDefault As Boolean) Handles SendIt.Click
MsgBox("SendIt button was pressed!")
'Dimension variables.
Dim OL As Object, MailSendItem As Object
Dim myxl As Excel.Application
Dim ws As Excel.Worksheet
Dim wd As Word.Application
Dim toRange = InputBox("Input cell range in R1:C1 format.", "Input range", "B3:B4")
Dim subj = InputBox("Input subject.", "Input subject", "TESTING")
wd = CreateObject("Word.Application")
Dim doc As Word.Document
'On Error Resume Next
'Assigns Word file to send
wd = GetObject(, "Word.Application")
If wd Is Nothing Then
wd = CreateObject("Word.Application")
'blnWeOpenedWord = True (MAY NOT NEED THIS)
End If
doc = wd.Documents.Open _
(FileName:="H:\Thought Pieces\Small Cap Liquidity\A Closer Look at Small Cap Liquidity.doc", ReadOnly:=False)
'Set itm = doc.MailEnvelope.Item
'Starts Outlook session
OL = CreateObject("Outlook.Application")
MailSendItem = doc.MailEnvelope.Item
myxl = GetObject(, "Excel.application")
ws = myxl.ActiveSheet
'Creates message
For Each xRecipient In ws.Range(toRange)
With MailSendItem
.Subject = subj
.To = xRecipient
.Cc = xRecipient.Offset(0, 5)
.Attachments.Add("H:\Thought Pieces\Small Cap Liquidity\A Closer Look at Small Cap Liquidity.pdf")
.Send()
End With
doc.Close(SaveChanges:=0)
wd = GetObject(, "Word.Application")
doc = wd.Documents.Open _
(FileName:="H:\Thought Pieces\Small Cap Liquidity\A Closer Look at Small Cap Liquidity.doc", ReadOnly:=False)
MailSendItem = doc.MailEnvelope.Item
myxl.Application.Wait(Now + TimeValue("00:00:20"))
Next xRecipient
'Ends Outlook session
OL = Nothing
End Sub
End Class
At OP's request, what I am doing is just a postmortem summation :)
Whenever in doubt, debug the code yourself. Step through the code but in situations like this when you are testing your code for a VSTO Add-In, I generally put few message boxes in my code so that I know which line is executing and which is not.
Op followed this approach and found two lines which were the culprit.
.To = xRecipient
and
myxl.Application.Wait(Now + TimeValue("00:00:20"))
The first one failed because that field expects a string value. It was sorted using
.To = xRecipient.Value.ToString()
I would recommend doing the same for .CC field as well.
Regarding the other Now + TimeValue("00:00:20") was not being calculated correctly. That is because you have "+" sign. Try doing this in VB.Net
MessageBox.Show(Now + TimeValue("00:00:20"))
The alternative was to use
myxl.Application.Wait(Now.AddSeconds(20))
Hope this helps.
I'm wondering if there's a simple way for a Word macro to determine which button was just pressed? I have a document template with several button which should all fire a macro.
The thing is, I want to create ONE macro which is called in each button. I don't want tons of macros for each button.
Now, this macro, when the button is pressed, it inserts a picture and the size of this picture is selected based on the buttons size. Meaning, this ends up as a picture placeholder. But, I want to write the macro dynamically so that the same code will work on each button without doing more than just calling the macro.
The complete macro is already done, I just need to know this one last thing, if anyone has any info on how to accomplish this? :) Thanx in advance!
UPDATE: This is the code at the moment
Private Sub ImageButton1_Click()
PicturePlaceholder ImageButton1
End Sub
Private Sub ImageButton2_Click()
PicturePlaceholder ImageButton2
End Sub
Public Sub PicturePlaceholder(ByVal oButton As CommandButton)
Dim oShape As Word.Shape
Dim Dlg As Office.FileDialog
Dim strFilePath As String
Dim oDoc As Document
Dim rgePlace As Range
Dim buttonHeight As String
Dim buttonWidth As String
Set Dlg = Application.FileDialog(msoFileDialogFilePicker)
Set oDoc = ActiveDocument
Set rgePlace = Selection.Range.Fields(1) _
.Result.Paragraphs(1).Range
Response = MsgBox("Do you want to delete the button/Picture?", vbYesNoCancel, "Do you want an image here?")
If Response = vbYes Then rgePlace.Fields(1).Delete
If Response = vbCancel Then Exit Sub
If Response = vbNo Then
With Dlg
.AllowMultiSelect = False
If .Show() <> 0 Then
strFilePath = .SelectedItems(1)
End If
End With
If strFilePath = "" Then Exit Sub
Set oShape = oDoc.Shapes.AddPicture(FileName:=strFilePath, _
LinkToFile:=False, SaveWithDocument:=True, _
Anchor:=rgePlace)
With oShape
.Height = oButton.Height
.Width = oButton.Width
End With
rgePlace.Fields(1).Delete
End If
End Sub
OK, so they're CommandButtons in the document.
In that case, there's nothing you can do - you need to have handlers called Button1_Click, Button2_Click, etc. (or whatever the button names are).
However, you can do something like this:
Private Sub Button1_Click(...)
DoStuff Button1
End Sub
Private Sub Button2_Click(...)
DoStuff Button2
End Sub
Private Sub DoStuff(ByVal oButton As CommandButton)
' All your shared code goes here
MsgBox oButton.Caption
End Sub
See also this tech note for how to create your buttons in code.
EDIT: updated to pass CommandButton reference so that the shared function can access the button properties.
EDIT 2: updated to show complete code using InlineShapes. Note that this no longer passes in the Button object, since the width/height of the button can be obtained directly from the field.
Private Sub CommandButton1_Click()
PicturePlaceholder
End Sub
Private Sub CommandButton2_Click()
PicturePlaceholder
End Sub
Public Sub PicturePlaceholder()
' Get the selected field, which must be a button field
Dim oField As Field
Set oField = Selection.Fields(1)
Debug.Assert oField.Type = wdFieldOCX
' Ask the user what he wants to do
Select Case MsgBox("Do you want to delete the button/Picture?", vbYesNoCancel, "Do you want an image here?")
Case vbCancel
Exit Sub
Case vbYes
oField.Delete
Exit Sub
End Select
' Get the filename of the picture to be inserted
Dim strFilePath As String
With Application.FileDialog(msoFileDialogFilePicker)
.AllowMultiSelect = False
If .Show() <> 0 Then
strFilePath = .SelectedItems(1)
End If
End With
If strFilePath = "" Then
Exit Sub
End If
' Figure out where to insert the picture, and what size to make it
Dim oRange As Range
Set oRange = oField.Result
Dim sglWidth As Single
sglWidth = oField.InlineShape.Width ' oButton.Width
Dim sglHeight As Single
sglHeight = oField.InlineShape.Height ' oButton.Height
' Delete the button field
oField.Delete
' Insert and resize the picture
Dim oInlineShape As Word.InlineShape
Set oInlineShape = oRange.InlineShapes.AddPicture(FileName:=strFilePath, LinkToFile:=False, SaveWithDocument:=True, Range:=oRange)
With oInlineShape
.Width = sglWidth
.Height = sglHeight
End With
End Sub
EDIT 3: Updated as requested to use Shapes rather than InlineShapes. (Both the CommandButton and the inserted Picture are now Shapes).
Private Sub CommandButton1_Click()
PicturePlaceholder
End Sub
Private Sub CommandButton2_Click()
PicturePlaceholder
End Sub
Public Sub PicturePlaceholder()
' Get the selected shape, which must be a button shape
Debug.Assert Selection.Type = wdSelectionShape
Dim oButtonShape As Shape
Set oButtonShape = Selection.ShapeRange(1)
' Ask the user what he wants to do
Select Case MsgBox("Do you want to delete the button/Picture?", vbYesNoCancel, "Do you want an image here?")
Case vbCancel
Exit Sub
Case vbYes
oButtonShape.Delete
Exit Sub
End Select
' Get the filename of the picture to be inserted
Dim strFilePath As String
With Application.FileDialog(msoFileDialogFilePicker)
.AllowMultiSelect = False
If .Show() <> 0 Then
strFilePath = .SelectedItems(1)
End If
End With
If strFilePath = "" Then
Exit Sub
End If
' Insert the picture at the same size/position
Dim oPictureShape As Shape
Set oPictureShape = ActiveDocument.Shapes.AddPicture _
( _
FileName:=strFilePath, _
LinkToFile:=False, _
SaveWithDocument:=True, _
Left:=oButtonShape.Left, _
Top:=oButtonShape.Top, _
Width:=oButtonShape.Width, _
Height:=oButtonShape.Height, _
Anchor:=oButtonShape.Anchor _
)
' Copy across the button shape formatting
oButtonShape.PickUp
oPictureShape.Apply
' Copy across other layout details
oPictureShape.LayoutInCell = oButtonShape.LayoutInCell
oPictureShape.LockAnchor = oButtonShape.LockAnchor
oPictureShape.RelativeHorizontalPosition = oButtonShape.RelativeHorizontalPosition
oPictureShape.RelativeVerticalPosition = oButtonShape.RelativeVerticalPosition
oPictureShape.WrapFormat.Type = oButtonShape.WrapFormat.Type
oPictureShape.WrapFormat.Side = oButtonShape.WrapFormat.Side
oPictureShape.WrapFormat.DistanceTop = oButtonShape.WrapFormat.DistanceTop
oPictureShape.WrapFormat.DistanceLeft = oButtonShape.WrapFormat.DistanceLeft
oPictureShape.WrapFormat.DistanceBottom = oButtonShape.WrapFormat.DistanceBottom
oPictureShape.WrapFormat.DistanceRight = oButtonShape.WrapFormat.DistanceRight
oPictureShape.WrapFormat.AllowOverlap = oButtonShape.WrapFormat.AllowOverlap
' Delete the button shape
oButtonShape.Delete
End Sub
I assume you mean that the button is a Command Bar button (aka toolbar button).
If so, you can use Application.CommandBars.ActionControl to get a reference to the button that was clicked. From there you can examine the caption, tag, or whatever.
You could put your base macro into a separate sub and then just call the macro from each button's click event, passing as a parameter the desired size. Then the only code you would have in the buttons is the call to the base sub.
You can have a button in Vba to pass arguments when it calls the macro which takes arguments.
For eg: if you have a function called as function test(x as string)
then the button which calls the macro will have a syntax as onclick("sheetx!test", "whatever"). So that way you can either have a generic macro to be called. Hope this helps you.
Placing the following in the various click/got_focus/change events of ActiveX buttons and textboxes works for me:
MsgBox ThisDocument.ActiveWindow.Selection.Fields.Item(1).OLEFormat.Object.Name