First let me start by saying that I have no idea what I'm doing. Well, some idea, but mostly I'm just winging it. I've been working on an Excel macro to run a Word mail merge from Excel for a couple of weeks and I've pieced together different codes to encompass everything I need it to do. My problem is that it works the first time but it gets stuck each subsequent time if I do not reset the macro.
The sheet I made is designed so the macro can pull all of the information it needs from the sheet (code contains no hard coded locations). This is because a folder containing the Excel form, the letter templates and the finished letters will be sent to multiple users and will therefore be saved in a different location on each user's computer. The user enters details into the Excel form which will ultimately create a formatted disciplinary letter in a Word document to send out to the player being reviewed. There may need to be multiple infractions included in the letter so the Excel form provides that option, whereas a Word form would have a bunch of unnecessary empty fields. (I initially tried setting up the letter in Word as a form but I couldn't get all of the factors that I needed to work together) A Word form also doesn't have the ability to hide fields and can't support the dependent drop down lists or the amount of text in each drop down that I require. Anyway...
The process:
User opens the Excel file and enters information into a nicely laid out, user friendly form which contains dependent drop down lists, etc as well as buttons that hide and show fields (in case the user needs to include multiple infractions being reviewed).
When the user is finished entering their information, they click a command button to run the mail merge ("Create Disciplinary Letter").
The information they select in the form is linked to another worksheet (same workbook) called "Data Sheet" that contains the columns that the merge will pull from.
The workbook also contains a "Control Sheet" worksheet which provides the location of the files and folders that the macro is to pull from.
The macro should:
Open Word merge template (it chooses the correct template based on a choice that the user makes in the form)
Run the merge
Send merged product to a new document
Close the original merge template without saving changes
Save the new document with a specific file name (based on choices the user made in the original Excel form)
Save it to a "Final Documents" folder which is located in the original folder that I send out to the user(s).
The newly saved document/letter stays open for further editing (if necessary)
New doc contains a button that will save the finished letter as a .pdf (also to a specific location) but that macro is in Word so it is not part of my problem.
The Excel form would likely be closed and reopened before a user needs to use it again, and in that case, the macro would run fine. There is a good possibility though, that the user will see the finished letter in Word, realize that they forgot the include an infraction, go back to the open Excel form to add the infraction, and click the macro button again. If this happens, the macro will get stuck at macro process #4 onward (from list above). I don't know what is in the code (or missing) that is causing this but I've been fighting with it for days and I can't find anything out there that I can apply to my issue. Or maybe I have, but I'm unaware because I'm seriously just winging it.
Sub RunMerge()
Dim bCreatedWordInstance As Boolean
Dim wdapp As Word.Application
Dim wddoc As Word.Document
Dim rng1 As Range
Dim wb As Workbook
Dim wsControl As Worksheet
Dim wsData As Worksheet
Dim strWorkbookName As String
Dim strTemplateFolder As String
Dim strTemplateName As String
Dim lngTemplateNameColumn As Long
Dim strFinalDocumentFolder As String
Dim strFinalDocumentName As String
Dim lngDocumentNameColumn As Long
Dim lngRecordKount As Long ' not used but retained for future use
Set wb = ThisWorkbook
Set wsControl = wb.Worksheets("Control Sheet")
wsControl.Activate
strTemplateFolder = wsControl.[Template_Folder].Value
strFinalDocumentFolder = wsControl.[Document_Folder].Value
Set wsData = wb.Worksheets(wsControl.[Data_Sheet].Value)
wsData.Activate
lngTemplateNameColumn = wsData.[Template_Name].Column
lngDocumentNameColumn = wsData.[Document_Name].Column
Set rng1 = wsData.Range("B1:B8")
strTemplateName = strTemplateFolder & "\" & wsData.Cells(2, lngTemplateNameColumn) & ".doc"
strFinalDocumentName = strFinalDocumentFolder & "\" & wsData.Cells(2, lngDocumentNameColumn)
strWorkbookName = ThisWorkbook.Path & "\" & ThisWorkbook.Name\
On Error Resume Next
' Create a Word Application instance
bCreatedWordInstance = False
Set wdapp = GetObject(, "Word.application")
If wdapp Is Nothing Then
Err.Clear
Set wdapp = CreateObject("Word.Application")
bCreatedWordInstance = True
End If
If wdapp Is Nothing Then
MsgBox "Could not start Word"
Err.Clear
On Error GoTo 0
Exit Sub
End If
' Let Word trap the errors
On Error GoTo 0
' Set to True if you want to see the Word Doc flash past during construction
wdapp.Visible = True
' check that template exists
If Dir(strTemplateName) = "" Then
MsgBox strTemplateName & " not found"
End If
Set wddoc = wdapp.Documents.Open(strTemplateName)
If wddoc Is Nothing Then Set wddoc = wdapp.Documents.Open(strTemplateName)
wddoc.Activate
With wddoc
.MailMerge.OpenDataSource Name:=strWorkbookName, SQLStatement:="SELECT * FROM `Data Sheet$`"
With wddoc.MailMerge 'With ActiveDocument.MailMerge
.Destination = wdSendToNewDocument
.SuppressBlankLines = True
.Execute Pause:=False
End With
End With
' Save new file
ActiveDocument.SaveAs strFinalDocumentName
' Close the New Mail Merged Document
If bCreatedWordInstance Then
wddoc.Close savechanges:=wdDoNotSaveChanges
Set wddoc = Nothing
End If
0:
Set wdapp = Nothing
Set rng1 = Nothing
Set wsData = Nothing
Set wsControl = Nothing
Set wb = Nothing
End Sub
It gets stuck the second time around on:
' Save new file
ActiveDocument.SaveAs strFinalDocumentName
Related
I am looking to create an add-in for outlook that works in Outlook 2010 and Office 365.
What I want it to do is Save an email in MSG format to a folder on our network, that I specify, and then move that email to an archive folder within outlook.
I don't know a lot about programming (Just the basics). I am hoping someone can point me in the right direction on how to begin this project and what resources I can use to do this.
Any help is much appreciated.
Thanks
See Walkthrough: Creating Your First VSTO Add-In for Outlook for getting started.
What I want it to do is Save an email in MSG format to a folder on our network, that I specify, and then move that email to an archive folder within outlook.
To save the email to a folder you need to use the SaveAs method of the MailItem class which saves the Microsoft Outlook item to the specified path and in the format of the specified file type. If the file type is not specified, the MSG format (.msg) is used. For example:
Sub SaveAsTXT()
Dim myItem As Outlook.Inspector
Dim objItem As Object
Set myItem = Application.ActiveInspector
If Not TypeName(myItem) = "Nothing" Then
Set objItem = myItem.CurrentItem
strname = objItem.Subject
'Prompt the user for confirmation
Dim strPrompt As String
strPrompt = "Are you sure you want to save the item? " &; _
"If a file with the same name already exists, " &; _
"it will be overwritten with this copy of the file."
If MsgBox(strPrompt, vbYesNo + vbQuestion) = vbYes Then
objItem.SaveAs Environ("HOMEPATH") &; "\My Documents\" &; strname &; ".txt", olTXT
End If
Else
MsgBox "There is no current active inspector."
End If
End Sub
To move an Outlook item to another folder you need to use the Move method which moves a Microsoft Outlook item to a new folder.
Finally, you may find the Selecting an API or technology for developing solutions for Outlook article helpful. It explains possible options for extending Outlook.
Create a COM addin using VSTO. Start at https://msdn.microsoft.com/en-us/library/ms268878.aspx
I have created a table of contents (TOC) to my Microsoft Office Word 2013 document using different types of headings linked to the TOC. When I try to modify my TOC 1 style so that only the text inside that TOC paragraph would be bolded, it makes the whole paragraph bolded. What I want to achieve is this:
ONLY THE TEXT HERE IS BOLDED................................1
rather than bolding the dot leader lines nor the page number like this
THE WHOLE PARAGRAPH IS BOLDED.........................1
I can do this manually, but everytime I update my TOC, all these changes go away (everything is bolded/not bolded again). How can I bold only only the text (not dot leaders or page number) inside TOC-style?
Fields throw away edits when updated - there's little you can do to change this. There is a * MergeFormat switch that can be added to field codes in order to retain formatting. The major drawback, however, is that it "remembers" formatting by character location. So if the text changes (gets shorter or longer) the formatting "goes crazy". Therefore, I don't think that will help you.
So all you can really do is re-apply the formatting after each TOC update. The fastest way to do that is using code (a macro, for example).
Here's a macro that works in a quick test on my system. It searches for each TAB character in the TableOfContents. Then, based on the additional information from the OP that only the second tab should be recognized, uses a second range to check whether the position AFTER the tab is within one centimeter of the right margin.
If that's the case, it repositions the find-target Range from that point back towards the beginning of the paragraph, then applies the formatting.
Note 1: You can change the "cushion" used to determine the distance to the right margin by changing the formula assigned to PageNumLimit.
Note 2: I defined a STYLE named Toc1_Text and use that, rather than formatting with BOLD directly. Doesn't really matter, but it feels "cleaner" to me than direct formatting :-)
Sub FormatTextInTOC()
Dim rngFind As word.Range, rngFound As word.Range
Dim bFound As Boolean
Dim toc As word.TableOfContents
Dim infoH As Double, pageNumLimit As Double
Set toc = ActiveDocument.TablesOfContents(1)
toc.Update
Set rngFind = toc.Range
pageNumLimit = rngFind.Sections(1).PageSetup.RightMargin _
- CentimetersToPoints(1)
With rngFind.Find
.ClearFormatting
.Text = vbTab
.Style = word.WdBuiltinStyle.wdStyleTOC1
Do
bFound = .Execute
If bFound Then
Set rngFound = rngFind.Duplicate
'rngFound.Select
rngFound.Collapse wdCollapseEnd
infoH = rngFound.Information(_
wdHorizontalPositionRelativeToTextBoundary)
If infoH >= pageNumLimit Then
'rngFind.Select
rngFind.Collapse wdCollapseStart
rngFind.MoveStart wdParagraph, -1
rngFind.Style = "Toc1_Text"
rngFind.Start = rngFind.Paragraphs(1).Range.End
'rngFind.Select
End If
End If
Loop While bFound
End With
End Sub
I created a new style in word(through "create a style...") and I want to assign this style to 200 word files. it is very time consuming to open them one by one and assign the style to them. Is there any way to assign the style to them without opening them?
The easiest way to do this is using Word VBA.
Place the 200 Word files that will be assigned the style into a directory that contains no other files. Then create a .dotm (Word 2007 or higher) or .dot (Word 2003 or lower) template file in a different location. Create the style to be copied in the template file and also place the following code into a module within that same template file (ALT-F11 to access the editor):
Sub BatchCopyStyles()
'Make sure that the template that contains the style to be copied and this code
'is open and acting as the active document before running this macro
Dim file As Variant
Dim folderPath As String 'path to files receiving the style
Dim targetPath As String
Dim templateFile As String 'file that contains style and this code
Dim styleTemplate As Document
folderPath = "C:\Users\Joe\Desktop\TargetFolder\"
templateFile = "C:\Users\Joe\Desktop\CopyStyle.dotm"
Set styleTemplate = ActiveDocument
file = Dir(folderPath)
While (file <> "")
Set file = Documents.Open(FileName:=folderPath & file)
styleTemplate.Activate
targetPath = folderPath & file
Application.OrganizerCopy Source:=templateFile, _
Destination:=targetPath, _
Name:="StyleToCopy", _
Object:=wdOrganizerObjectStyles
file.Close wdSaveChanges
file = Dir
Wend
End Sub
Edit the code for the correct paths, file name, style name, etc. With the file containing this code and the style to be assigned as the Active Document, run the macro from the VBA editor (F5). This will open each file, copy the style, and then close the file. Opening and closing a document 200 times won't be pretty, but it should do the job.
is it possible to put a password on a single word inside a word document?
What I want is to write a system documentation from our IT structure. I want to put the passwords in the document as well. Now I want that you have to enter a "MASTER" password in order to unlock to passwords inside the document. If you do not enter the password, the passwords inside the document should be invisible or something like that.
Is that possible?
Yes, this is possible. To make this work we have to use a combination of Word's inherent document protection, its hidden text attribute, and a couple of VBA tricks.
First, format all the passwords in the document as hidden text. (For those who are unfamiliar, hidden text is only visible when the Show/Hide function is set to true.)
Then add code to make sure that the hidden text will not display and also to protect the document from being edited whenever the document is opened:
Private Sub Document_Open()
AddProtection
End Sub
Sub AddProtection()
With ActiveDocument
.ActiveWindow.View.ShowAll = False
.ActiveWindow.View.ShowHiddenText = False
.Application.Options.PrintHiddenText = False
.Protect Type:=wdAllowOnlyReading, NoReset:=True, Password:="DesiredPassword"
End With
End Sub
Because Word users can normally display hidden text at any given time, we also need to take control of this function. Most menu and Ribbon commands can be intercepted by creating a module containing subroutines named for the intercepted commands. Naming a Sub ShowAll will allow us to control this function and only display hidden text when a password is entered:
Sub ShowAll()
If ActiveDocument.ProtectionType = wdAllowOnlyReading Then
'Do nothing
Else 'restore the Ribbon's toggle function
If ActiveDocument.ActiveWindow.View.ShowAll = True Then
ActiveDocument.ActiveWindow.View.ShowAll = False
Else
ActiveDocument.ActiveWindow.View.ShowAll = True
End If
End If
End Sub
Finally, we add some code to prompt the user for a password and, if entered correctly, display the text that was formerly hidden:
Sub RemoveProtection()
Dim strPassword As String
strPassword = InputBox("Enter document password.")
ActiveDocument.Unprotect Password:=strPassword
If ActiveDocument.ProtectionType = wdNoProtection Then
ActiveDocument.ActiveWindow.View.ShowHiddenText = True
End If
End Sub
Once all the VBA code is entered, right-click on the module in the IDE, select Project Properties, and assign a separate password on the Protection tab. This will stop power users from getting to the embedded password or changing the code.
All that's left to do is create a QAT button in Word (that is only visible for this document) and assign the RemoveProtection sub to it. Whenever the document is opened the passwords will be hidden and protected from editing, but can then be revealed by clicking on the QAT button and entering the correct password.
EDIT
When I first answered this question, I failed to consider that Word has a hidden text option that can be turned on separateley from the Show All option. Additionally, hidden text can be printed via a printing option. I have added code in AddProtection above to turn off these settings.
Further testing also revealed that a user in Word 2007 could manually go into Office Orb|Options|Display to reveal the hidden text by changing the Show all formatting marks or Hidden text options manually. To avoid this, a Ribbon customization would need to be created and loaded with the document.
Finally, it is worth noting that although it is great fun to bend Word to one's will in order to make it accomplish tasks like this, the level of protection is not as good as encrypting the passwords separately and then unecrypting before revealing the contents or even using Word's document password feature to encrypt the document's entire contents.
I have an mdb file made by ms access. It got a form inside and inside the form there are one large textbox.
The intention of making this textbox is to show the progress of some work by adding messages inside the textbox:
txtStatus.value = txtStatus.value & "Doing something..." & vbCrLf
txtStatus.value = txtStatus.value & "Done." & vbCrLf
But the problem is, when the height of the text > height of the textbox, the new message is not displayed automatically. The textbox has a scroll bar, but I have to scroll it manually. I would like to auto scroll to the bottom whenever new text pop up.
I tried to add this code(copied from internet) in the On Change property, but the code failed, it does nothing:
Private Sub txtStatus_Change()
txtStatus.SelStart = Len(txt) - 1
End Sub
I wish there would be some simple and beautiful way to achieve this. I don't want to add some code which only work on some computers due to its dependence on the windows platform's kernel/etc.
You can do it via a call to a sub;
AppendText "Bla de bla bla."
.
.
sub AppendText(strText As String)
with txtStatus
.setfocus '//required
.value = .value & strText & vbNewLine
.selstart = len(.Value)
end with
end sub
There is a workaround to the design flaw mentioned by steve lewy in his comment on the original post. It is possible to have a text box that appears to do both of the following:
When the contents are too large for the box, and the box does not
have the focus, the box displays the last part of its contents,
rather than the first part of it.
When the box has the focus, it can scroll to any part of the text,
but it initially shows only the last part of the text, with the
cursor at the end of the text.
This is accomplished by actually having two identically-sized, overlaid text boxes, where one is visible only when the focus is elsewhere, while the other is visible only when it has the focus.
Here’s an example of how to do it in Access 2010.
Create a new Access database, and create a memo field named LongNote in its only table. Fill LongNote with some examples of long text. Create a form for editing that table.
Create a text box called BackBox with the desired size and font, too small to completely show a typical value of its data source, LongNote. (Instead of creating this box, you can rename the default text box created on the form.)
Make an exact copy of that box called FrontBox. Set the data source of FrontBox to be either the entire contents of BackBox or the last part of the contents, as shown below. The size of the last part, measured in characters, depends on the size of the box and its font, as well as on the kind of text to be displayed. It needs to be chosen by trial and error to reliably allow that many characters to be displayed in the box. For instance, here’s the formula for a box that can reasonably hold only 250 characters:
=iif(Len([BackBox])>=250,"... " & Right([BackBox],246),[BackBox])
If the whole value is too large to be shown, three dots precede the part that is shown to indicate that it’s incomplete.
Create another text box called OtherBox, just to have somewhere you can click besides the two boxes already mentioned, so neither of them has the focus. Also create a tiny (0.0097 x 0.0097) text box called FocusTrap, which is used to avoid selecting the entire contents of whatever text box gets the focus when the form is displayed (because text selected that way is hard to read).
Enter the following event-handling VBA code:
' Prevent all text boxes from being focused when a new record becomes
' current, because the focus will select the whole text and make it ugly
Private Sub Form_Current()
FocusTrap.SetFocus
End Sub
Private Sub Form_Open(Cancel As Integer)
FocusTrap.SetFocus
End Sub
' When FrontBox receives focus, switch the focus to BackBox,
' which can display the entire text
Private Sub FrontBox_GotFocus()
BackBox.SetFocus
FrontBox.Visible = False
End Sub
' When BackBox receives the focus, set the selection to
' the end of the text
Private Sub BackBox_GotFocus()
BackBox.SelStart = Len([LongNote])
BackBox.SelLength = 0
End Sub
' When BackBox loses focus, re-display FrontBox – if the text in
' BackBox has changed, then FrontBox will follow the change
Private Sub BackBox_LostFocus()
FrontBox.Visible = True
End Sub
Test the form. When you click on FrontBox, it should disappear, letting you work on BackBox. When you click in OtherBox to remove the focus from BackBox, FrontBox should reappear. Any edits made in BackBox should be reflected in FrontBox.
Back in design mode, move FrontBox so it exactly covers BackBox, and click Position | Bring to Front to ensure that it covers BackBox. Now test the form again. It should appear that a single text box switches between display-the-last-few-lines mode and edit-the-entire-contents mode.
Simply put the following code after linefeed or on Change event txtStatus
txtStatus.SelStart = Len(txtStatus) - 1