finding variables in textboxes - vba

I have an issue when using textboxes that contains variables in word2016.
I would like to preform a search for a variable that may exist within textbox's text, and during certain instances I would like to replace it with a new variable. An example textbox is shown below :
I want to search for the DOCPROPERTY "Checked By". This DOCPROPERTY does not exist in the textbox but the text "Checked By" does , but my code as attached below finds it regardless. Since it is not a DOCPROPERTY, it should not return true.
' ************************************************************
' ********* finding docproperties in text, headers and textboxes
' **************************************************************
Public Function findProperty(doc As Document, findText As String) As Boolean
Dim rngStory As word.Range
Dim oFld As word.Field
Dim objShape As Shape
Dim temp As String
Dim temp2() As String
Dim element As Variant
ActiveWindow.View.ShowFieldCodes = True
If findText = "_DocumentTitle" Then
findProperty = True
Exit Function
End If
findProperty = False
For Each objShape In ActiveDocument.Shapes
If objShape.Type = msoTextBox Then
'do the required action
temp2 = Split(objShape.TextFrame.TextRange.Text, "DOCPROPERTY")
For Each element In temp2
temp = replace(element, "DOCPROPERTY", "")
temp = replace(temp, "\* MERGEFORMAT", "")
temp = replace(temp, """", "")
If InStr(UCase(temp), Trim(UCase(findText))) > 0 Then
findProperty = True
Exit Function
End If
Next
End If
Next objShape
For Each rngStory In doc.StoryRanges
Do
For Each oFld In rngStory.Fields
'If oFld.Type = wdFieldDocProperty Then
'Dig a little deeper and see what the field code contains.
'Formatting of property is a pain....
temp = replace(oFld.Code.Text, "DOCPROPERTY", "")
temp = replace(temp, "\* MERGEFORMAT", "")
temp = replace(temp, """", "")
If Trim(UCase(temp)) = Trim(UCase(findText)) Then
findProperty = True
Exit Function
End If
Next oFld
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next rngStory
ActiveWindow.View.ShowFieldCodes = False
End Function

You're splitting a string into an array using "DOCPROPERTY" as the delimiter, so there is no need to replace "DOCPROPERTY" with vbNullString, ("").
temp2 = Split(objShape.TextFrame.TextRange.Text, "DOCPROPERTY")
For Each element In temp2
temp = replace(element, "DOCPROPERTY", "")
I think that you can test the size of temp2, if it is bigger than 0 then there was a "DOCPROPERTY" found, if it is 0 then there was no "DOCPROPERTY" found.
if UBound(temp2) > 0 then findProperty = True
But probably a better way is to look at Field(n).code which returns the field code text and then you know exactly what you have...

With the suggestion from #SlowLearner is the working code presented below.
Public Function findProperty(doc As Document, findText As String) As Boolean
Dim rngStory As word.Range
Dim oFld As word.Field
Dim objShape As Shape
Dim element As Variant
findProperty = False 'default false
'**************************************************************************************
'**************** ALL DOCUMENTS NEED A TITLE EVEN IF ITS NOT IN USE *******************
'**************************************************************************************
If findText = "_DocumentTitle" Then
findProperty = True
Exit Function
End If
For Each objShape In ActiveDocument.Shapes
If objShape.Type = msoTextBox Then
'do the required action
For Each element In objShape.TextFrame.TextRange.Fields
If InStr(UCase(element.Code.Text), Trim(UCase(findText))) > 0 Then
findProperty = True
Exit Function
End If
Next element
End If
Next objShape
For Each rngStory In doc.StoryRanges
Do
For Each oFld In rngStory.Fields
'Dig a little deeper and see what the field code contains.
If InStr(UCase(oFld.Code.Text), Trim(UCase(findText))) > 0 Then
findProperty = True
Exit Function
End If
Next oFld
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next rngStory
End Function

Related

Using VBA code how to extract Non HTML data content residing under each heading from a word document

How to extract text and non text data content (ex: Tables, pictures) associated with each heading irrespective of heading style?
With below code I am able to reach out to each header, post that I am failing to extract content associated with that heading:
Option Explicit
Sub Main()
Dim strFile As String
Dim oWord As Word.Application
Dim oWdoc As Word.Document
Dim oPar As Word.Paragraph
Dim rng As Word.Range
strFile = "C:\Users\SQVA\Desktop\My_Work\MyTest3.docx"
'Set oWord = CreateObject("Word.Application")
Set oWord = New Word.Application
Set oWdoc = oWord.Documents.Open(strFile)
Call Get_Heading_Name(oWord, oWdoc, strFile, rng)
Call Close_Word(oWord, oWdoc)
End Sub
Sub Get_Heading_Name(oWord As Word.Application, oWdoc As Word.Document, strFile As String, rng As Word.Range)
oWord.Visible = True
Dim astrHeadings As Variant
Dim strText As String
Dim intItem As Integer
Set rng = oWdoc.Content
astrHeadings = _
oWdoc.GetCrossReferenceItems(wdRefTypeHeading)
For intItem = LBound(astrHeadings) To UBound(astrHeadings)
strText = Trim$(astrHeadings(intItem))
'Debug.Print CStr(strText)
'Debug.Print astrHeadings(intItem).
Dim my_String As String
Dim intLevel
If CStr(strText) <> "" Then
my_String = Right(strText, Len(strText) - InStr(strText, " "))
intLevel = GetLevel(CStr(astrHeadings(intItem)))
' Call GetHeadingNextText(oWdoc, my_String)
' Debug.Print my_String
' Debug.Print intLevel
' rng.Style = "Heading " & intLevel
Dim sTextSearch() As String
Dim StrHdTxt1
Dim nStart As Long, nEnd As Long, n As Long, k As Long
Dim wdTable
Dim wdTbl As Word.Table, wdCell As Word.cell, wdCellRng As Word.Range
Dim wdIshp As Word.InlineShape, wdShp As Word.Shape, StrHdTxt As String
oWdoc.Range(0, 0).Select
With oWord.Selection.Find
.Style = oWdoc.Styles("Heading " & intLevel)
.Text = my_String
If .Execute Then
'Debug.Print "Found"
Call SelectHeadingandContent(oWdoc, oWord)
End If
End With
End If
Next intItem
End Sub
Sub Close_Word(oWord As Word.Application, oWdoc As Word.Document)
oWdoc.Close SaveChanges:=wdDoNotSaveChanges
oWord.Quit
Set oWdoc = Nothing
Set oWord = Nothing
End Sub
Private Function GetLevel(strItem As String) As Integer
' Return the heading level of a header from the
' array returned by Word.
' The number of leading spaces indicates the
' outline level (2 spaces per level: H1 has
' 0 spaces, H2 has 2 spaces, H3 has 4 spaces.
Dim strTemp As String
Dim strOriginal As String
Dim longDiff As Integer
' Get rid of all trailing spaces.
strOriginal = RTrim$(strItem)
' Trim leading spaces, and then compare with
' the original.
strTemp = LTrim$(strOriginal)
' Subtract to find the number of
' leading spaces in the original string.
longDiff = Len(strOriginal) - Len(strTemp)
GetLevel = (longDiff / 2) + 1
End Function
Sub SelectHeadingandContent(oWdoc As Word.Document, oWord As Word.Application)
Dim headStyle 'As Style
' Checks that you have selected a heading. If you have selected multiple paragraphs,checks only the first one. If you have selected a heading, makes sure the whole paragraph is selected and records the style. If not, exits the subroutine.
If oWdoc.Styles(oWord.Selection.Paragraphs(1).Style).ParagraphFormat.OutlineLevel < wdOutlineLevelBodyText Then
Set headStyle = oWord.Selection.Paragraphs(oWord.Selection.Paragraphs.Count).Style
oWord.Selection.Expand wdParagraph
Else: Exit Sub
End If
' Turns off screen updating so the the screen does not flicker.
Application.ScreenUpdating = False
' Loops through the paragraphs following your selection, and incorporates them into the selection as long as they have a higher outline level than the selected heading (which corresponds to a lower position in the document hierarchy). Exits the loop if there are no more paragraphs in the document.
Dim My_Text As String
My_Text = ""
Do While oWdoc.Styles(oWord.Selection.Paragraphs(oWord.Selection.Paragraphs.Count).Next.Style).ParagraphFormat.OutlineLevel > headStyle.ParagraphFormat.OutlineLevel
'Debug.Print oWord.Selection.Paragraphs(oWord.Selection.Paragraphs.Count).Range.Text
oWord.Selection.MoveEnd wdParagraph
' Debug.Print oWord.Selection.Paragraphs(oWord.Selection.Paragraphs.Count).Range.Text
My_Text = My_Text + vbCr + oWord.Selection.Paragraphs(oWord.Selection.Paragraphs.Count).Range.Text
If oWord.Selection.Paragraphs(oWord.Selection.Paragraphs.Count).Next Is Nothing Then Exit Do
Loop
Debug.Print My_Text
' Turns screen updating back on.
Application.ScreenUpdating = True
End Sub
You can loop through all the Heading1 ranges and their 'non-text' objects, as you call them, with code like:
Sub Read_Heading_Contents()
Dim wdApp As New Word.Application, wdDoc As Word.Document, wdRng As Word.Range
Dim wdTbl As Word.Table, wdCell As Word.Cell, wdCellRng As Word.Range
Dim wdIshp As Word.InlineShape, wdShp As Word.Shape, StrHdTxt As String
Const strFile As String = "C:\Users\SQVA\Desktop\My_Work\MyTest3.docx"
With wdApp
.Visible = True
Set wdDoc = .Documents.Open(Filename:=strFile, ReadOnly:=True, AddToRecentFiles:=False, Visible:=False)
With wdDoc
With .Range
With .Find
.Style = wdStyleHeading1
.Text = ""
.Wrap = wdFindStop
.Execute
End With
If .Find.Found = False Then
MsgBox "No 'Heading 1' style found."
Else
Do While .Find.Found = True
StrHdTxt = .Duplicate.Text: MsgBox StrHdTxt
Set wdRng = .Duplicate.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
For Each wdTable In .Tables
With wdTbl
For Each wdCell In .Range.Cells
Set wdCellRng = wdCell.Range
wdCellRng.End = wdCellRng.End - 1
MsgBox wdCellRng.Text
Next
End With
Next
For Each wdIshp In wdRng.InlineShapes
With wdIshp
If Not .TextEffect Is Nothing Then
MsgBox .TextEffect.Text
End If
End With
Next
For Each wdShp In wdRng.ShapeRange
With wdShp
If Not .TextFrame Is Nothing Then
MsgBox .TextFrame.TextRange.Text
End If
End With
Next
.Collapse wdCollapseEnd
.Find.Execute
Loop
End If
End With
.Close SaveChanges:=wdDoNotSaveChanges
End With
.Quit
End With
Set wdRng = Nothing: Set wdDoc = Nothing: Set wdApp = Nothing
End Sub
The above code includes message boxes to display the heading names and whatever it finds in the heading range's 'non-text' content. I'll leave it to you to turn the textbox output into whatever else you want it to be. Of course, not all inline & floating shapes have text; the loops find those, too, but I have no idea how you intend to 'read' those.

Add hyperlinks to linked images

I'm trying to add hyperlinks to images, which were added via IncludePicture fields.
For example, this is an image:
{ IncludePicture "C:\\Test\\Image 1.png" \d }
And so, it should be added hyperlink to it:
C:\\Test\\Image 1.png
After that, I can click on my image in document with mouse, and it will be opened in file manager.
Here is the code. For some reason, it doesn't properly work. How it should be fixed?
Sub AddHyperlinksToImages()
On Error Resume Next
Application.ScreenUpdating = False
Dim iShp As InlineShape
For Each iShp In ActiveDocument.InlineShapes
iShp.Hyperlink.Address = iShp.LinkFormat.SourceFullName 'Doesn't work
'Just for testing
'fullPath = iShp.LinkFormat.SourceFullName
'MsgBox fullPath
Next
Application.ScreenUpdating = True
End Sub
Please try this code.
Sub AddHyperlinksToImages()
' 22 Sep 2017
Dim Fld As Field
Dim FilePath As String
Dim Tmp As String
Dim i As Integer
Application.ScreenUpdating = False
ActiveDocument.Fields.Update
For Each Fld In ActiveDocument.Fields
With Fld
If InStr(1, Trim(.Code), "includepicture", vbTextCompare) = 1 Then
If .InlineShape.Hyperlink Is Nothing Then
i = InStr(.Code, Chr(34))
If i Then
FilePath = Replace(Mid(.Code, i + 1), "\\", "\")
i = InStr(FilePath, "\*")
If i Then FilePath = Left(FilePath, i - 1)
Do While Len(FilePath) > 1
i = Asc(Right(FilePath, 1))
FilePath = Left(FilePath, Len(FilePath) - 1)
If i = 34 Then Exit Do
Loop
If i > 1 Then ActiveDocument.Hyperlinks.Add .InlineShape, FilePath
End If
End If
End If
End With
Next Fld
Application.ScreenUpdating = True
End Sub

VBA invalid qualifier with string.copy

I'm writing a code that loops through the textboxes of a word document. These textboxes contain a picture and a caption. So far, I have written a code that gets the caption from the textbox (which I checked through MsgBox caption).
I want to copy the caption, clear the textbox of all content, and then paste the old caption back in (because I'm trying to replace the pictures with an updated one). However, I keep getting an error with caption.Copy and have no idea why. It says that caption is an "Invalid Qualifier." I did some digging around online but haven't solved my problem.
This was the most-related thing I found: Invalid Qualifier error in Visual Basic 6.0
Anyway, here's my code. Any help would be appreciated!
Sub ReplaceImages()
Dim str As String
Dim captionTag As String
Dim imageTag As String
'Dim objShape As Variant Type Mismatch?
Dim fileName As String
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
'Select directory to match .PNG to figure in document
Set SelectFolder = Application.FileDialog(msoFileDialogFolderPicker)
With SelectFolder
.Title = "Select Directory"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo ResetSettings
sPath = .SelectedItems(1) & "\"
End With
sFile = Dir(sPath & "*png")
Do While sFile <> ""
fileName = sFile
MsgBox fileName
imageTag = BetweenParentheses(fileName)
For Each objShape In ActiveDocument.Shapes
If objShape.Type = msoTextBox Then
Set shapePicture = objShape
str = objShape.TextFrame.TextRange.Text
If InStr(str, "(") > 0 Then
captionTag = BetweenParentheses(objShape.TextFrame.TextRange)
If captionTag = imageTag Then
If InStr(str, "Figure") > 0 Then
Dim firstTerm As String
Dim secondTerm As String
Dim caption As String
firstTerm = "F"
secondTerm = ")"
Dim startPos As Long
Dim stopPos As Long
Dim nextPosition As Long
nextPosition = 1
caption = objShape.TextFrame.TextRange.Text
Do Until nextPosition = 0
startPos = InStr(nextPosition, caption, firstTerm, vbTextCompare) - 1
stopPos = InStr(startPos, caption, secondTerm, vbTextCompare) + 1
caption = Mid$(caption, startPos + Len(firstTerm), stopPos - startPos - Len(firstTerm))
nextPosition = InStr(stopPos, caption, firstTerm, vbTextCompare)
Loop
caption.Copy 'This is where the error is
End If
End If
End If
End If
Next objShape
sFile = Dir
Loop
ResetSettings:
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
The caption variable is a string while the Copy method only applies to objects in the Word object model.
You store the text from the TextFrame into the caption variable:
caption = objShape.TextFrame.TextRange.Text
And then manipulate it inside your loop.
If you want to keep the value of the caption variable, then assign the value to another variable:
Dim someOtherVariable As String
someOtherVariable = caption
There might be some different between Excel and Word VBA in embedded shapes, but the following should be easy enough to adopt to word:
Sub test()
Dim shp As Shape, s As String
Set shp = ActiveSheet.Shapes(1)
s = shp.TextFrame2.TextRange.Text ' this is a string which doesn't have a Copy method
Debug.Print s
'but:
shp.TextFrame2.TextRange.Copy 'copies to clipboard!
End Sub
You can double check that the text is in the clipboard by pasting it directly into the immediate window (or wherever).

MS Word macro, how to adapt so it get data from my Excel file?

My project is to change words/phrases in the whole Bible, and I have the Word doc with the Bible in it, and separately, I have an Excel file with Column A oldwords that need to be found by the macro, Column B newwords that will replace the old ones.
I was gracefully provided with this macro, which works well in MS Word 2010, but as you can see, it is setup to use Data arrays. It even says, "'Note: Data arrays are used in this example. In practice the data could come from a Word table, Excel worksheet or other data source.". So, I need to adapt it so that it gets the data from my Excel file where I have all the words/phrases that need to be changed.
There are 2 class modules and 1 standard module. But at the end of this post, I'm also including yet more code that should do what I want (get data from an Excel file) but it needs to be adapted to work in my macro. The problem is, I don't know how to adapt it. In other words: I don't know what needs to be replaced in the module/s in order to make it work.
Also, I believe my Excel file needs to be simple. Column A Old wordorphrase // Column B New wordorphrase. Would that work fine just like that?
Here is everything I have (separately):
First class module I was told to name it "clsTerm" and paste this code:
Option Explicit
Private English As String
Private Hebrew As String
Private FNT As String
Property Let EnglishTerm(strVal As String)
English = strVal
End Property
Property Get EnglishTerm() As String
EnglishTerm = English
End Property
Property Let HebrewTerm(strVal As String)
Hebrew = strVal
End Property
Property Get HebrewTerm() As String
HebrewTerm = Hebrew
End Property
Property Let FootnoteText(strVal As String)
FNT = strVal
End Property
Property Get FootnoteText() As String
FootnoteText = FNT
End Property
Second class module I was told to name it "clsTerms" and paste this code:
Option Explicit
Private colTerms As Collection
Private lngCount As Long
Property Get Items() As Collection
Set Items = colTerms
End Property
Property Set Items(oCol As Collection)
Set colTerms = oCol
End Property
Property Get Count() As Long
If Not colTerms Is Nothing Then
Count = colTerms.Count
Else
Count = 0
End If
End Property
Then I was told to create a standard module and name it "Anything I like" and paste this code:
Option Explicit
Dim m_oCol1 As Collection
Dim m_oCol2 As Collection
Sub ReplaceWordsAndDefineFootnotes()
Dim clsTL As clsTerms
Dim lngIndex As Long
Set clsTL = New clsTerms
Set clsTL.Items = DefinedTerms
Set m_oCol1 = New Collection
For lngIndex = 1 To clsTL.Count
'Replace each defined English word with it Hebrew equivelent.
ReplaceWords clsTL.Items(lngIndex).EnglishTerm, clsTL.Items(lngIndex).HebrewTerm
Next lngIndex
Underline_And_DefineFootnote
For lngIndex = 1 To clsTL.Count
'Replace temporary footnote text with with class defined footnote text.
FixFootnotes clsTL.Items(lngIndex).HebrewTerm, clsTL.Items(lngIndex).FootnoteText
Next lngIndex
lbl_Exit:
Exit Sub
End Sub
Function DefinedTerms() As Collection
Dim arrEng() As String
Dim arrHeb() As String
Dim lngIndex As Long
Dim oCol As Collection
Dim Term As clsTerm
'Note: Data arrays are used in this example. In practice the data could come from a Word table, Excel worksheet or other data source.
arrEng = Split("God,heaven,earth,waters,good", ",")
arrHeb = Split("Elohim,shamayim,aretz,mayim,tov", ",")
Set oCol = New Collection
'Put data in the collection.
For lngIndex = 0 To UBound(arrEng)
Set Term = New clsTerm
Term.EnglishTerm = arrEng(lngIndex)
Term.HebrewTerm = arrHeb(lngIndex)
Term.FootnoteText = arrEng(lngIndex) & ":" & arrHeb(lngIndex)
oCol.Add Term, Term.EnglishTerm
Next lngIndex
Set DefinedTerms = oCol
lbl_Exit:
Exit Function
End Function
Sub ReplaceWords(ByVal strFind As String, ByVal strReplaceWith As String)
Dim oRng As Word.Range
'Add each term processed to a collection.
m_oCol1.Add UCase(strReplaceWith), UCase(strReplaceWith)
Set oRng = ActiveDocument.Range
'Replace each instance of the English word with its Hebrew equivalent.
With oRng.Find
.Text = strFind
.Replacement.Text = strReplaceWith
.MatchWholeWord = True
.MatchCase = False
.Execute Replace:=wdReplaceAll
End With
lbl_Exit:
Exit Sub
End Sub
Sub Underline_And_DefineFootnote()
Dim oRng As Word.Range
Dim lngIndex As Long
Dim oWord As Word.Range
Dim strWord As String
Dim lngCounter As Long
Dim lngPages As Long
With ActiveDocument
Set oRng = .Range
lngPages = .ComputeStatistics(wdStatisticPages)
For lngIndex = 1 To lngPages
Reprocess:
Set m_oCol2 = New Collection
Set oRng = oRng.GoTo(What:=wdGoToPage, Name:=lngIndex)
Set oRng = oRng.GoTo(What:=wdGoToBookmark, Name:="\page")
lngCounter = 1
With oRng
For Each oWord In oRng.Words
'Modify the word range to strip off white space. We want only the text portion of the word range.
strWord = UCase(Trim(oWord.Text))
oWord.Collapse wdCollapseStart
oWord.MoveEnd wdCharacter, Len(strWord)
If oWord.Characters.Last = Chr(160) Then oWord.MoveEnd wdCharacter, -1
'We need to know if the text defined by the word range is a word we want to process.
'We added all of those words to a collection during the find and replace process.
'If we try to add one of those words to the collection again then it will error and we will know _
we are dealing with a word we want to process.
On Error Resume Next
m_oCol1.Add strWord, strWord
If Err.Number <> 0 Then
On Error GoTo 0
On Error Resume Next
'We only want to underline and footnote the first instance of the term on each page.
'So add the term and key to a collection.
m_oCol2.Add strWord, strWord
If Err.Number = 0 Then
'There was no error so underline the term and footnote it.
oWord.Font.Underline = 1
On Error GoTo 0
ActiveDocument.Footnotes.Add oWord, CStr(lngCounter), LCase(strWord)
lngCounter = lngCounter + 1
End If
Else
'The word wasn't a word we want to process so remove it from the collection.
m_oCol1.Remove m_oCol1.Count
End If
Next oWord
End With
'Since processing words will add footnotes, the length of the document will increase.
'I'm using this method to reenter the processing loop.
lngPages = .ComputeStatistics(wdStatisticPages)
If lngIndex < lngPages Then
lngIndex = lngIndex + 1
GoTo Reprocess
End If
Next lngIndex
End With
Set oRng = Nothing
End Sub
Sub FixFootnotes(ByVal strFind As String, ByVal strReplaceWith As String)
Dim oRng As Word.Range
m_oCol1.Add UCase(strReplaceWith), UCase(strReplaceWith)
Set oRng = ActiveDocument.StoryRanges(wdFootnotesStory)
With oRng.Find
.Text = strFind
.Replacement.Text = strReplaceWith
.MatchWholeWord = True
.MatchCase = False 'True
.Execute Replace:=wdReplaceAll
End With
lbl_Exit:
Exit Sub
End Sub
And finally, this is the code that I need to adapt into my macro, in order to make it get the data from the Excel file:
Sub ListFromExcel()
Dim lngIndex As Long
Dim arrWords As Variant
'Find words in column 1, Replace words in column 2
arrWords = GetListArray(You file path and name)
For lngIndex = 2 To UBound(arrWords, 1)
Debug.Print arrWords(lngIndex, 1)
Debug.Print arrWords(lngIndex, 2)
Next
End Sub
Function GetListArray(ByRef strFileName As String) As Variant
Dim xlapp As Object
Dim xlbook As Object
Dim xlsheet As Object
Dim bAppStart As Boolean
On Error Resume Next
Set xlapp = GetObject(, "Excel.Application")
If Err Then
bAppStart = True
Set xlapp = CreateObject("Excel.Application")
End If
On Error GoTo 0
Set xlbook = xlapp.Workbooks.Open(FileName:=strFileName)
Set xlsheet = xlbook.Worksheets(1)
GetListArray = xlsheet.Range("A1").CurrentRegion.Value
xlbook.Close
If bAppStart = True Then xlapp.Quit
Set xlapp = Nothing
Set xlbook = Nothing
Set xlsheet = Nothing
lbl_Exit:
Exit Function
End Function
Change the first sub to this
Sub ReplaceWordsAndDefineFootnotes()
Dim clsTL As clsTerms
Dim lngIndex As Long
Set clsTL = New clsTerms
clsTL.FillFromExcel
Set m_oCol1 = New Collection
For lngIndex = 1 To clsTL.Count
'Replace each defined English word with it Hebrew equivelent.
ReplaceWords clsTL.Items(lngIndex).EnglishTerm, clsTL.Items(lngIndex).HebrewTerm
Next lngIndex
Underline_And_DefineFootnote
For lngIndex = 1 To clsTL.Count
'Replace temporary footnote text with with class defined footnote text.
FixFootnotes clsTL.Items(lngIndex).HebrewTerm, clsTL.Items(lngIndex).FootnoteText
Next lngIndex
lbl_Exit:
Exit Sub
End Sub
Add this to the clsTerms class
Public Sub FillFromExcel()
Dim xlApp As Object
Dim xlWb As Object
Dim vaWords As Variant
Dim cTerm As clsTerm
Dim i As Long
Const sFILE As String = "C:\Users\Dick\Documents\My Dropbox\Excel\wordlist.xlsx"
Const xlUP As Long = -4162
Set colTerms = New Collection
Set xlApp = CreateObject("Excel.Application")
Set xlWb = xlApp.Workbooks.Open(sFILE, , True)
With xlWb.Worksheets(1)
vaWords = .Range("A1", .Cells(.Rows.Count, 2).End(xlUP)).Value
End With
For i = LBound(vaWords, 1) To UBound(vaWords, 1)
Set cTerm = New clsTerm
cTerm.EnglishTerm = vaWords(i, 1)
cTerm.HebrewTerm = vaWords(i, 2)
cTerm.FootnoteText = vaWords(i, 1) & ":" & vaWords(i, 2)
colTerms.Add cTerm
Next i
xlWb.Close False
xlApp.Quit
End Sub

Modifying Microsoft Word VBA macro to call additional text from external file and add to footnote

I'm working with an MS Word macro that currently calls data from an external Excel file, for a find/replace procedure in a long MS Word text. In my Excel file, Columns A has the words I want to find and Column B the words to replace with. Each change the macro performs, gets underlined and also a footnote is created on the text.
Now I need to have the macro add yet additional information about the change and also put it in the footnote. I have what I want to add, ready to go in Columns C of my Excel sheet.
More simply put: My code is already getting data from Columns A and B and putting it in the footnote. So, all I need to do now is, tell it to get the data ALSO from Column C. How do I do that?
Here's the full code:
1 standard module:
Option Explicit
Dim m_oCol1 As Collection
Dim m_oCol2 As Collection
Sub ReplaceWordsAndDefineFootnotes()
Dim clsTL As clsTerms
Dim lngIndex As Long
Set clsTL = New clsTerms
clsTL.FillFromExcel
Set m_oCol1 = New Collection
For lngIndex = 1 To clsTL.Count
'Replace each defined English word with it Hebrew equivelent.
ReplaceWords clsTL.Items(lngIndex).English, clsTL.Items(lngIndex).Hebrew
Next lngIndex
Underline_And_DefineFootnote
For lngIndex = 1 To clsTL.Count
'Replace temporary footnote text with with class defined footnote text.
FixFootnotes clsTL.Items(lngIndex).Hebrew, clsTL.Items(lngIndex).Footnote
Next lngIndex
lbl_Exit:
Exit Sub
End Sub
Function DefinedTerms() As Collection
Dim arrEng() As String
Dim arrHeb() As String
Dim lngIndex As Long
Dim oCol As Collection
Dim Term As clsTerm
'Note: Data arrays are used in this example. In practice the data could come from a Word table, Excel worksheet or other data source.
'arrEng = Split("God,heaven,earth,waters,good", ",")
'arrHeb = Split("Elohim,shamayim,aretz,mayim,tov", ",")
Set oCol = New Collection
'Put data in the collection.
For lngIndex = 0 To UBound(arrEng)
Set Term = New clsTerm
Term.English = arrEng(lngIndex)
Term.Hebrew = arrHeb(lngIndex)
Term.Footnote = arrEng(lngIndex) & ":" & arrHeb(lngIndex)
'Term.FootnoteText = varWords(lngIndex, 3) & ":" & varWords(lngIndex, 1)
oCol.Add Term, Term.English
Next lngIndex
Set DefinedTerms = oCol
lbl_Exit:
Exit Function
End Function
Sub ReplaceWords(ByVal strFind As String, ByVal strReplaceWith As String)
Dim oRng As Word.Range
'Add each term processed to a collection.
m_oCol1.Add UCase(strReplaceWith), UCase(strReplaceWith)
Set oRng = ActiveDocument.Range
'Replace each instance of the English word with its Hebrew equivalent.
With oRng.Find
.Text = strFind
.Replacement.Text = strReplaceWith
.MatchWholeWord = True
.MatchCase = False
.Execute Replace:=wdReplaceAll
End With
lbl_Exit:
Exit Sub
End Sub
Sub Underline_And_DefineFootnote()
Dim oRng As Word.Range
Dim lngIndex As Long
Dim oWord As Word.Range
Dim strWord As String
Dim lngCounter As Long
Dim lngPages As Long
With ActiveDocument
Set oRng = .Range
lngPages = .ComputeStatistics(wdStatisticPages)
For lngIndex = 1 To lngPages
Reprocess:
Set m_oCol2 = New Collection
Set oRng = oRng.GoTo(What:=wdGoToPage, Name:=lngIndex)
Set oRng = oRng.GoTo(What:=wdGoToBookmark, Name:="\page")
lngCounter = 1
With oRng
For Each oWord In oRng.Words
'Modify the word range to strip off white space. We want only the text portion of the word range.
strWord = UCase(Trim(oWord.Text))
oWord.Collapse wdCollapseStart
oWord.MoveEnd wdCharacter, Len(strWord)
If oWord.Characters.Last = Chr(160) Then oWord.MoveEnd wdCharacter, -1
'We need to know if the text defined by the word range is a word we want to process.
'We added all of those words to a collection during the find and replace process.
'If we try to add one of those words to the collection again then it will error and we will know _
we are dealing with a word we want to process.
On Error Resume Next
m_oCol1.Add strWord, strWord
If Err.Number <> 0 Then
On Error GoTo 0
On Error Resume Next
'We only want to underline and footnote the first instance of the term on each page.
'So add the term and key to a collection.
m_oCol2.Add strWord, strWord
oWord.Font.Underline = 1
If Err.Number = 0 Then
'There was no error so underline the term and footnote it.
'oWord.Font.Underline = 1
On Error GoTo 0
ActiveDocument.Footnotes.Add oWord, CStr(lngCounter), LCase(strWord)
lngCounter = lngCounter + 1
End If
Else
'The word wasn't a word we want to process so remove it from the collection.
m_oCol1.Remove m_oCol1.Count
End If
Next oWord
End With
'Since processing words will add footnotes, the length of the document will increase.
'I'm using this method to reenter the processing loop.
lngPages = .ComputeStatistics(wdStatisticPages)
If lngIndex < lngPages Then
lngIndex = lngIndex + 1
GoTo Reprocess
End If
Next lngIndex
End With
Set oRng = Nothing
End Sub
Sub FixFootnotes(ByVal strFind As String, ByVal strReplaceWith As String)
Dim oRng As Word.Range
m_oCol1.Add UCase(strReplaceWith), UCase(strReplaceWith)
Set oRng = ActiveDocument.StoryRanges(wdFootnotesStory)
With oRng.Find
.Text = strFind
.Replacement.Text = strReplaceWith
.MatchWholeWord = True
.MatchCase = False 'True
.Execute Replace:=wdReplaceAll
End With
lbl_Exit:
Exit Sub
End Sub
1 of 2 class modules (clsTerm):
Option Explicit
Private msEnglish As String
Private msHebrew As String
Private msFootnote As String
Public Property Let English(ByVal sEnglish As String): msEnglish = sEnglish: End Property
Public Property Get English() As String: English = msEnglish: End Property
Public Property Let Hebrew(ByVal sHebrew As String): msHebrew = sHebrew: End Property
Public Property Get Hebrew() As String: Hebrew = msHebrew: End Property
Public Property Let Footnote(ByVal sFootnote As String): msFootnote = sFootnote: End Property
Public Property Get Footnote() As String
Footnote = msEnglish & ":" & msHebrew & " - " & msFootnote
End Property
2 of 2 class modules (clsTerms):
Option Explicit
Private mcolTerms As Collection
Private lngCount As Long
Property Get Items() As Collection
Set Items = mcolTerms
End Property
Property Set Items(oCol As Collection)
Set mcolTerms = oCol
End Property
Property Get Count() As Long
If Not mcolTerms Is Nothing Then
Count = mcolTerms.Count
Else
Count = 0
End If
End Property
Public Sub FillFromExcel()
Dim xlApp As Object
Dim xlWb As Object
Dim vaWords As Variant
Dim cTerm As clsTerm
Dim i As Long
Const sFILE As String = "C:\Documents and Settings\Administrator\Desktop\Macro Latest Accomplishments\this_feeds_AlexfromZackMacro.xlsx"
Const xlUP As Long = -4162
Set mcolTerms = New Collection
Set xlApp = CreateObject("Excel.Application")
Set xlWb = xlApp.Workbooks.Open(sFILE, , True)
With xlWb.Worksheets(1)
'changed 2 to 3 to get column c
vaWords = .Range("A1", .Cells(.Rows.Count, 3).End(xlUP)).Value
End With
'change footnote to store column c
For i = LBound(vaWords, 1) To UBound(vaWords, 1)
Set cTerm = New clsTerm
cTerm.English = vaWords(i, 1)
cTerm.Hebrew = vaWords(i, 2)
cTerm.Footnote = vaWords(i, 3)
mcolTerms.Add cTerm
Next i
xlWb.Close False
xlApp.Quit
End Sub
Come of my variable names may have changed since the last answer, so you'll need to make it all mesh together. Change your Term class to this
Option Explicit
Private msEnglish As String
Private msHebrew As String
Private msFootnote As String
Public Property Let English(ByVal sEnglish As String): msEnglish = sEnglish: End Property
Public Property Get English() As String: English = msEnglish: End Property
Public Property Let Hebrew(ByVal sHebrew As String): msHebrew = sHebrew: End Property
Public Property Get Hebrew() As String: Hebrew = msHebrew: End Property
Public Property Let Footnote(ByVal sFootnote As String): msFootnote = sFootnote: End Property
Public Property Get Footnote() As String
Footnote = msEnglish & ":" & msHebrew & " - " & msFootnote
End Property
This makes the Let part of Footnote a place to store what you have in column C. The Get part then let's you define how you want to output the footnote. In this example, I'm reading in column C (in the next section), but when I get the footnote property, it concatenates some other terms - it's not a straight read-back of what is in column C. You can change the Get part of Footnote to make it whatever you want.
Next you need to change how the Excel file is read in.
Public Sub FillFromExcel()
Dim xlApp As Object
Dim xlWb As Object
Dim vaWords As Variant
Dim clsTerm As cTerm
Dim i As Long
Const sFILE As String = "C:\Users\Dick\Documents\My Dropbox\Excel\wordlist.xlsx"
Const xlUP As Long = -4162
Set mcolTerms = New Collection
Set xlApp = CreateObject("Excel.Application")
Set xlWb = xlApp.Workbooks.Open(sFILE, , True)
With xlWb.Worksheets(1)
'changed 2 to 3 to get column c
vaWords = .Range("A1", .Cells(.Rows.Count, 3).End(xlUP)).Value
End With
'change footnote to store column c
For i = LBound(vaWords, 1) To UBound(vaWords, 1)
Set clsTerm = New cTerm
clsTerm.English = vaWords(i, 1)
clsTerm.Hebrew = vaWords(i, 2)
clsTerm.Footnote = vaWords(i, 3)
mcolTerms.Add clsTerm
Next i
xlWb.Close False
xlApp.Quit
End Sub
I increased the range to include Column C. Before, Footnote was a concatenation of A and B. Now it is whatever is in column C and the concatenation is done in the class, where it should be.
I didn't save the file from the last question, so some of the variables and property names may have changed. Hopefully it's clear enough that you can adapt it.