GetCrossReferenceItems in msword and VBA showing only limited content - vba

I want to make a special list of figures with use of VBA and here I am using the function
myFigures = ActiveDocument.GetCrossReferenceItems(Referencetype:="Figure")
In my word document there are 20 figures, but myFigures only contains the first 10 figures (see my code below.).
I search the internet and found that others had the same problem, but I have not found any solutions.
My word is 2003 version
Please help me ....
Sub List()
Dim i As Long
Dim LowerValFig, UpperValFig As Integer
Dim myTables, myFigures as Variant
If ActiveDocument.Bookmarks.Count >= 1 Then
myFigures = ActiveDocument.GetCrossReferenceItems(Referencetype:="Figure")
' Test size...
LowerValFig = LBound(myFigures) 'Get the lower boundry number.
UpperValFig = UBound(myFigures) 'Get the upper boundry number
' Do something ....
For i = LBound(myFigures) To UBound(myFigures) ‘ should be 1…20, but is onlu 1…10
'Do something ....
Next i
End If
MsgBox ("Done ....")
End Sub*

Definitely something flaky with that. If I run the following code on a document that contains 32 Figure captions, the message boxes both display 32. However, if I uncomment the For Next loop, they only display 12 and the iteration ceases after the 12th item.
Dim i As Long
Dim myFigures As Variant
myFigures = ActiveDocument.GetCrossReferenceItems("Figure")
MsgBox myFigures(UBound(myFigures))
MsgBox UBound(myFigures)
'For i = 1 To UBound(myFigures)
' MsgBox myFigures(i)
'Next i

I had the same problem with my custom cross-refference dialog and solved it by invoking the dialog after each command ActiveDocument.GetCrossReferenceItems(YourCaptionName).
So you type:
varRefItemsFigure1 = ActiveDocument.GetCrossReferenceItems(g_strCaptionLabelFigure1)
For k = 1 To UBound(varRefItemsFigure1)
frmBwtRefDialog.ListBoxFigures.AddItem varRefItemsFigure1(k)
Next
and then:
frmBwtRefDialog.Show vbModeless
Thus the dialog invoked several times instead of one, but it works fast and don't do any trouble. I used this for one year and didn't see any errors.
Enjoy!

Frankly I feel bad about calling this an "answer", but here's what I did in the same situation. It would appear that entering the debugger and stepping through the GetCrossReferenceItems always returns the correct value. Inspired by this I tried various ways of giving control back to Word (DoEvents; running next segment using Application.OnTime) but to no avail. Eventually the only thing I found that worked was to invoke the debugger between assignments, so I have:
availRefs =
ActiveDocument.GetCrossReferenceItems(wdRefTypeNumberedItem):Stop
availTables =
ActiveDocument.GetCrossReferenceItems(wdCaptionTable):Stop
availFigures = ActiveDocument.GetCrossReferenceItems(wdCaptionFigure)
It's not pretty but, as I'm the only person who'll be running this, it kind of works for my purposes.

Related

VB.Net equivalent for (CustomizationContext) in VBA

I'm busy with some word automation and have run into an issue whereby a context menu within a document has items in, that I wish to remove.
Once the document is open, through vba I can remove these items by running the following code;
[VBA]
Dim oContextMenu As CommandBar
Dim oContextMenuItem As CommandBarControl
'Make changes to the ActiceDocument only (this is needed to make any changes to this document).
CustomizationContext = ActiveDocument
For Each oContextMenu In ActiveDocument.CommandBars
If oContextMenu.Type = MsoBarType.msoBarTypePopup Then 'Loop through all the context menus of type (msoBarTypePopup)
For Each oContextMenuItem In oContextMenu.Controls
If (InStr(oContextMenuItem.Caption, "Smokeball")) Then
oContextMenuItem.Delete
End If
Next
End If
Next
If I execute this code and check the document, all contextMenu sub items that contain the text "smokeball" are removed.
When I try move this code to my VB.NET solution (I have no choice of language, so VB it is), I get errors on the CustomizationContext = ActiveDocument line (this line has to be there for it to affect the current document).
The error I get is CustomizationContext' is not a by reference property.
Does anyone know how to get just that ONE line equivalent for vb.net?
Thanks in advance.
EDIT: In case you need to see the vb.net sub:
Private Sub RemoveUnwantedContextMenuItems()
Dim oContextMenu As CommandBar
Dim oContextMenuItem As CommandBarControl
'Make changes to the ActiceDocument only (this is needed to make any changes to this document).
WordApplication.CustomizationContext = WordApplication.ActiveDocument 'This is the error.
For Each oContextMenu In WordApplication.CommandBars
If oContextMenu.Type = MsoBarType.msoBarTypePopup Then 'Loop through all the context menus of type (msoBarTypePopup)
For Each oContextMenuItem In oContextMenu.Controls
If (InStr(oContextMenuItem.Caption, "Smokeball")) Then
oContextMenuItem.Delete()
End If
Next
End If
Next
End Sub
PS - I have also already tried using the .AttachedTemplate as well as .Normal / .NormalTemplate
Jules pointed me in the right direction with his sample code.
After lots of playing around I noticed that somewhere in the solution, the [TYPE] of WordApplication was getting changed to a dynamic type of sorts, hence, it couldn't use CustomizationContext.
My solution was this:
I changed this line;
WordApplication.CustomizationContext = WordApplication.ActiveDocument
To this:
CType(WordApplication, Microsoft.Office.Interop.Word.Application).CustomizationContext = WordApplication.ActiveDocument
Forcing the types to be correct.
Simple solution but took some time.
Thanks to Jules for pointing me in the right direction.
(Points should go to you).

Functions give error only in macro-called dialog

I am writing and re-writing Excel VBA code to make some engineering analysis easier for myself and the others at my company. So the goal is to make it easy for others, not to be integrated into other code or purposes outside our own Excel work.
The problem: a few of the macros will add a custom function to a worksheet cell, bring up the function dialog for inputs, but then when completed, come up with a Value error, stating A value used in the formula is of the wrong data type. Based on this, I believe the issue arises when my function uses a Variant or Range type variable. Some of these functions only give this error via certain input types.
HOWEVER, in every single case, using the function dialog Not in the macro works fine, as does typing the formula in manually. Additionally, all you have to do is double-click on the error cell to edit it, change nothing, press enter, and it's fine!
My macros themselves are as simple as:
Sub NewSTC_dialog()
ActiveCell.FormulaR1C1 = "=sheetname.xlsm!NewSTC()"
Application.Dialogs(xlDialogFunctionWizard).Show
End Sub
My functions have more to them, but the situation is different for different functions, and the functions themselves work fine, so it is preferable to have a solution that applies to the macros rather than within the functions. If I must, I can go through each situation individually or come up with a simplified function that has the same issue to give as an example.
My best workaround so far (I cannot re-find the website were I found this particular solution):
Sub Recalculate()
ActiveCell.Replace What:="=", Replacement:="="
End Sub
Set to a convenient shortcut, and it works great. However, as mentioned above, the goal is to make this easy for everyone else, which would mean not requiring anything additional to have the formula work. Unfortunately, calling this from within the initial macro will not work. In fact, nothing located after the dialog call will run. I tried using On Error Resume Next and it still won't run anything after the dialog call.
Some options searched and tried:
How to refresh cells in Excel 2010 using VBA? and links within that - as mentioned, nothing located after the dialog call will run, thus won't reach these adjustments. Putting before the dialog call also doesn't help. Putting the dialog call in between the false and true statements makes the false apply, then never runs the true statement.
http://www.excelforum.com/excel-general/471632-formula-do-not-work-until-edited.html - Excel is not set to manual
http://blog.contextures.com/archives/2012/02/16/excel-formulas-not-calculating/ - SUMIF is not involved
This is all on Excel 2010, Windows 7 Professional.
EDIT:
Here is an example that creates this behavior. This is similar to the behavior of two of my functions, but not to all of them, so again, I would much prefer the solution be in the macro, not the function. This is just for anybody else's testing purposes.
Function LetNum(input1 As Variant, input2 As Variant) As Integer
If input1 = "A" Or input1 = "a" Then
input1 = 100
ElseIf input1 = 10 Then
input1 = -10
End If
If input2 = "B" Or input2 = "b" Then
input2 = 200
ElseIf input2 = 10 Then
input2 = -1000
End If
LetNum = input1 + input2
End Function
Sub LetNum_dialog()
ActiveCell.FormulaR1C1 = "=LetNum()"
Application.Dialogs(xlDialogFunctionWizard).Show
End Sub
In this situation, typing A and 2 into the dialog box in that order works, but 2 into the second then A into the first doesn't. Additionally, typing B in the second box never works. In all situations, clicking to edit, changing nothing, and hitting enter, fixes it. Additionally, any situation in the dialog box if you bring it up without the macro works fine.
Also, I'm aware this case works better if you input it with quotes, but excel automatically changes it correctly, and not all my functions are string issues.
EDIT 2:
Tim's answer is a great step in the right direction! However, if anybody can come up with a solution that doesn't involve ignoring all errors, that would be more ideal due to some desired error-throwing being ignored. I will look into handling errors differently in the meantime.
The issue seems to be that any error triggered in your UDF effectively "disables" the Function Wizard: note that in cases where you get #VALUE! error, the Show() method never returns any value, and any lines following the call to Show() are not executed.
The only thing which resolved this in my testing was to add a line to the function which skips any errors when the Function Wizard is open:
Dim bSettingUp As Boolean
Function LetNum(input1 As Variant, input2 As Variant) As Integer
If bSettingUp Then On Error Resume Next
If input1 = "A" Or input1 = "a" Then
input1 = 100
ElseIf input1 = 10 Then
input1 = -10
End If
If input2 = "B" Or input2 = "b" Then
input2 = 200
ElseIf input2 = 10 Then
input2 = -1000
End If
LetNum = input1 + input2
End Function
Sub LetNum_dialog()
ActiveCell.Formula = "=LetNum()"
bSettingUp = True
Debug.Print Application.Dialogs(xlDialogFunctionWizard).Show()
bSettingUp = False
End Sub
I'm aware that you'd rather not alter your functions ,but this was the only work-around I could find.

VBA Excel Break Points and Stop do not work

Any idea why inserting break points and stop no longer stops my vba code from running?
The code runs ok all the way to the end (I tested it) but ignores break points and Stop.
Also step into just makes the code run in it's entirety, ignoring break points and stops.
When I close the workbook where the issue seems to originate from the same issue occurs in other macro workbooks.
if I completely close excel and re-open it with a normally working macro workbook the issue doesn't occur until I re-open the problem work book.
I added breakpoints on:
TotP1 = 0
of the following code:
Option Explicit
Private Country As String
Private Measure As String
Private P1 As String
Private P2 As String
Private TotP1 As Double
Private TotP2 As Double
Sub VennDisplayIt()
Dim SI() As String
Dim SICount As Integer
Dim x As Integer
Dim OSh As Worksheet
Dim BrandListBox As Object
Dim VennGroup As Shape
TotP1 = 0
TotP2 = 0
Set OSh = ThisWorkbook.Sheets("Venn")
Set BrandListBox = OSh.OLEObjects("BrandListBox").Object
ReDim SI(2, 0)
For x = 0 To BrandListBox.ListCount - 1
If BrandListBox.Selected(x) = True Then
'If UBound(SI) < 4 Then
ReDim Preserve SI(2, UBound(SI, 2) + 1)
SI(1, UBound(SI, 2)) = BrandListBox.List(x)
SI(2, UBound(SI, 2)) = x + 1
'End If
End If
Next x
If UBound(SI, 2) < 2 Then
BrandListBox.Selected(BrandListBox.ListIndex) = True
Exit Sub
ElseIf UBound(SI, 2) > 4 Then
BrandListBox.Selected(BrandListBox.ListIndex) = False
Exit Sub
End If
For x = 1 To UBound(SI, 2)
OSh.Range("o8").Offset(x, 0).Value = SI(1, x)
OSh.Range("o8").Offset(x + 5, 0).Value = SI(1, x)
Next x
For x = UBound(SI, 2) + 1 To 4
OSh.Range("o8").Offset(x, 0).Value = ""
OSh.Range("o8").Offset(x + 5, 0).Value = ""
Next x
SICount = UBound(SI, 2)
For x = 1 To OSh.Shapes.Count
If Right(OSh.Shapes(x).Name, 5) = "Group" Then
If LCase(OSh.Shapes(x).Name) = SICount & "waygroup" Then
Set VennGroup = OSh.Shapes(x)
OSh.Shapes(x).Visible = True
Else
OSh.Shapes(x).Visible = False
End If
End If
Next x
For x = 1 To SICount
VennGroup.GroupItems.Item(SICount & "WayBrand" & x).DrawingObject.Text = SI(1, x)
Next x
Country = ThisWorkbook.Sheets("Venn").Range("D4").Value
Measure = ThisWorkbook.Sheets("Venn").Range("E32").Value
P2 = ThisWorkbook.Sheets("Venn").Range("E31").Value
P1 = ThisWorkbook.Sheets("Selections").Range("B5").Value
End Sub
I've never heard of Stop not working, but I've heard about and experienced the breakpoint thing many times. When you compile VBA, it creates a p-code, which is used by the interpreter. You have the VBA syntax layer that you can see and the p-code layer that you can't see. When breakpoints stop working, it's because the p-code was corrupted. I don't know how or why it happened, but it did.
The fix is to export, remove, and reimport all of your modules. Exporting them creates a .bas file (plain text, really). When you re-import, the p-code is regenerated from scratch. If you have more than a couple of modules, get CodeCleaner (free add-in) and it will export and reimport automatically.
If one of the settings is unchecked then breakpoints will not work. "File/options/Current database/Application options/use access special keys" should be checked
Just to 'second' Tibo's comment: I had a problem where I had a form with about 5 different subroutines. One of them (attached to a button event) would not stop when I set a breakpoint. in 10 years of VBA writing, I've never seen that happen. Interestingly, breakpoints worked on all of the other subroutines in that form. After much head-scratching and searching, I came upon this post and Tibo's comment. I added a "Stop" command to the affected subroutine, ran the procedure (it stopped as it should have) and then breakpoints began working again! Hope this helps someone in the future.
If the breakpoints are in your code, then the code should stop running as soon as it hits that line. Possible causes of your problem:
a) The code never gets to the Breakpoint.
Seems highly unlikely seeing as you're breaking on the first line of your code. Maybe step through the sub (F8) just to check it's running as it should.
b) Your breakpoints have been wiped. Any line with a breakpoint should display as highlighted in red on your IDE. Your breakpoints can easily be wiped through, e.g. closing and opening the workbook (a screenshot would be really useful).
c) your workbook is broken in some way. It would be unlikely to break something as fundamental as stopping at breakpoints and still function normally. Are you sure your code is actually running?
Just sharing a funny thing which happened to me in case it helps someone. The mistake I did was that I simply took someone's method code meant for workbook open event and pasted it on Sheet1 excel object code area. So there was no possibility of Workbook_Open method getting fired ever.
Private Sub Workbook_Open()
Stop
On Error Resume Next
Call ActiveSheet.Worksheet_Activate
On Error GoTo 0
End Sub
What I was supposed to do is paste this method after double clicking ThisWorkbook node under Microsoft Excel Objects in Project pane as shown below:
Note: Side effect of copy-pasting others code can be freaky at times.
a) Double check that your breakpoints got disabled or not.
b) Double check that you added a conditional breakpoint or not
c) If you run your macro using C# code (using, the _Run2 command), breakpoints and stop doesn't work sometimes.
I have the problems as described above:
Stop and Breakpoints not working
Single step F8 worked, but code was not highlighted in yellow
Closing VBA editor did not help
My remedy: close VBA editor again, save Excel-file, open editor, back to normal
I've never faced this problem for years. Today for the first time I started using watch expressions with stopping when true.
This happens to me once in a while and the following solves the problem for me every time:
Create a Sub with only the command Stop in it. Run it. Then try your routine, the breakpoints and Stop commands should now work correctly.
Make sure that you do not have a named Range that matches the name of the Function or Subroutine you are calling. This will look like the Function or Subroutine is failing, but it is actually failing before the routine is ever called with an 'invalid cell reference'.
I had this problem as well. My solution was put an error in the code and run it. After I cleared the error the breakpoints started working again. That was weird.
I been writing code in VBA for many years but today I came across this problem for the first time. None of the solutions I found on the web worked but here's something simply you can use instead of Stop:
ErrCatch = 1 / 0
Still doesn't solve the breakpoints not working though...
I went int my vba code, added an enter (new blank line), then ran it again.
Voila! It ran the code and stopped at the breakpoint!
Not just me then! On a few occasions the yellow hi-lite stops a few lines beyond the breakpoint! I guess the code is running so fast it can't stop in time. :) As suggested above, I find adding "stops" here and there and also exporting & re-importing helps too.

VBA to GetElementByID on a Web form

I'm having a bit of trouble with my VBA code filling in a form on an intranet page. It generally works okay but every so often, the ID's of the fields on this form change and I have to update my code but not until I've had lots of errors reported. Is there anyway I can use a wildcard, or loop through the possibilities before it tries to fill out the form? The bit of code I'm using is
WebBrowser1.Document.GetElementById("Template_ctl24_ctl00_Shelfmark" & x & "_TextField").value = Range("Q" & x + 11).value
But the ID can change to Template_ctl22_ctl00.... or Template_ctl25_ctl00.... for a reason unknown to me. I don't have control over that area - i'm really only front end.
So is there some variation on using a * wildcard?
Or looping through whether the ID is a 22, 24, 25 or whatever before it proceeds?
What can and can't you do with this sort of line of VBA code?
Thanks in advance
Paul
If you know where the element is positioned on the page it would be much better to locate it using something like getElementsByTagName.
However, you could use a simple loop. In the following I've assumed the attempt to reference a non-existing element results in Nothing, but perhaps it generates an error - I haven't tested. If so, you'll need to use error-handling code instead.
Dim elem As Variant
For x = 22 To 25
Set elem = WebBrowser1.Document.GetElementById("Template_ctl24_ctl00_Shelfmark" _
& x & "_TextField")
If Not elem Is Nothing Then
Exit For
End If
Next x
If elem Is Nothing Then
'Doh! not found
Else
'obtain elem.value
End If

Get the number of pages in a Word document

I am making lots of changes to a Word document using automation, and then running a VBA macro which - among other things - checks that the document is no more than a certain number of pages.
I'm using ActiveDocument.Information(wdNumberOfPagesInDocument) to get the number of pages, but this method is returning an incorrect result. I think this is because Word has not yet updated the pagination of the document to reflect the changes that I've made.
ActiveDocument.ComputeStatistics(wdStatisticPages) also suffers from the same issue.
I've tried sticking in a call to ActiveDocument.Repaginate, but that makes no difference.
I did have some luck with adding a paragraph to the end of the document and then deleting it again - but that hack seems to no longer work (I've recently moved from Word 2003 to Word 2010).
Is there any way I can force Word to actually repaginate, and/or wait until the repagination is complete?
I just spent a good 2 hours trying to solve this, and I have yet to see this answer on any forum so I thought I would share it.
https://msdn.microsoft.com/en-us/vba/word-vba/articles/pages-object-word?f=255&MSPPError=-2147217396
That gave me my solution combined with combing through the articles to find that most of the solutions people reference are not supported in the newest versions of Word. I don't know what version it changed in, but my assumption is that 2013 and newer can use this code to count pages:
ActiveDocument.ActiveWindow.Panes(1).Pages.Count.
I believe the way this works is ActiveDocument selects the file, ActiveWindow confirms that the file to be used is in the current window (in case the file is open in multiple windows from the view tab), Panes determines that if there is multiple windows/split panes/any other nonsense you want the "first" one to be evaluated, pages.count designates the pages object to be evaluated by counting the number of items in the collection.
Anyone more knowledgeable feel free to correct me, but this is the first method that gave me the correct page count on any document I tried!
Also I apologize but I cant figure out how to format that line into a code block. If the mods want to edit my comment to do that be my guest.
Try (maybe after ActiveDocument.Repaginate)
ActiveDocument.BuiltinDocumentProperties(wdPropertyPages)
It is causing my Word 2010 to spend half-second with "Counting words" status in status bar, while ActiveDocument.ComputeStatistics(wdStatisticPages) returns the result immediately.
Source: https://support.microsoft.com/en-us/kb/185509
After you've made all your changes, you can use OnTime to force a slight delay before reading the page statistics.
Application.OnTime When:=Now + TimeValue("00:00:02"), _
Name:="UpdateStats"
I would also update all the fields before this OnTime statement:
ActiveDocument.Range.Fields.Update
I found a possible workaround below, if not a real answer to the topic question.
Yesterday, the first ComputeStatistics line below was returning the correct total of 31 pages, but today it returns only 1.
The solution is to get rid of the Content object and the correct number of pages is returned.
Dim docMultiple As Document
Set docMultiple = ActiveDocument
lPageCount = docMultiple.Content.ComputeStatistics(wdStatisticPages) ' Returns 1
lPageCount = docMultiple.ComputeStatistics(wdStatisticPages) ' Returns correct count, 31
ActiveDocument.Range.Information(wdNumberOfPagesInDocument)
This works every time for me. It returns total physical pages in the word.
I used this from within Excel
it worked reliably on about 20 documents
none were longer than 20 pages but some were quite complex
with images and page breaks etc.
Sub GetlastPageFromInsideExcel()
Set wD = CreateObject("Word.Application")
Set myDoc = wD.Documents.Open("C:\Temp\mydocument.docx")
myDoc.Characters.Last.Select ' move to end of document
wD.Selection.Collapse ' collapse selection at end
lastPage = wD.Selection.Information(wdActiveEndPageNumber)
mydoc.close
wd.quit
Set wD = Nothing
End Sub
One problem I had in getting "ComputeStatistics" to return a correct page count was that I often work in "Web Layout" view in Word. Whenever you start Word it reverts to the last view mode used. If Word was left in "Web Layout" mode "ComputeStatistics" returned a page count of "1" page for all files processed by the script. Once I specifically set "Print Layout" view I got the correct page counts.
For example:
$MSWord.activewindow.view.type = 3 # 3 is 'wdPrintView', 6 is 'wdWebView'
$Pages = $mydoc.ComputeStatistics(2) # 2 is 'wdStatisticPages'
You can use Pages-Object and its properties such as Count. It works perfect;)
Dim objPages As Pages
Set objPage = ActiveDocument.ActiveWindow.Panes(1).Pages
QuantityOfPages = ActiveDocument.ActiveWindow.Panes(1).Pages.Count
Dim wordapp As Object
Set wordapp = CreateObject("Word.Application")
Dim doc As Object
Set doc = wordapp.Documents.Open(oFile.Path)
Dim pagesCount As Integer
pagesCount = doc.Content.Information(4) 'wdNumberOfPagesInDocument
doc.Close False
Set doc = Nothing