Microsoft Word: Select and GoTo InlineShape - vba

A macro in my Word document goes through all the graphs (inlineshapes) and updates links and values. At the moment the document freezes for the whole process behind a "Please wait" form dialog. Ideally I'd have the code move throughout the document to show the user that stuff is actually happening.
How can I select and GoTo the current inlineshape within my loop below?
Private Sub UpdateFields()
PleaseWait.bar.Width = 0
PleaseWait.Show
' This routine sets the new path for external links, pointing them to the current folder.
Dim Rng As Range, Fld As Field, Shp As Shape, iShp As InlineShape, i As Long
Dim no_of_steps As Integer
Dim single_step_width As Integer
no_of_steps = 0
With ThisDocument
' Create progress bar
' a) Count total number of steps
For Each Rng In .StoryRanges
For Each iShp In Rng.InlineShapes
no_of_steps = no_of_steps + 1
Next iShp
Next Rng
' b) Divide full width of progress frame by number of steps
single_step_width = PleaseWait.frame.Width \ no_of_steps
' Go through all story ranges in the document.
For Each Rng In .StoryRanges
' Go through the inlineshapes in the story range.
For Each iShp In Rng.InlineShapes
With iShp
' Skip over inlineshapes that don't have links to external files.
If Not .LinkFormat Is Nothing Then
With .LinkFormat
' Skip links already set to current folder
If Not .SourceFullName = ThisDocument.Path & "\datagrunnlag.xlsm" Then
' Replace the link to the external file
.SourceFullName = ThisDocument.Path & "\datagrunnlag.xlsm"
On Error Resume Next
.AutoUpdate = False
.Update
On Error GoTo 0
End If
End With
End If
' Update progress bar with completed step
PleaseWait.bar.Width = PleaseWait.bar.Width + single_step_width
DoEvents
End With
Next iShp
Next Rng
End With
End Sub
Edit 05.12.2020: added all code within the sub. ScreenUpdating = False and True are being set by separate subs for MacroEntry and MacroExit.

That would be a really bad idea. Using the Selection object will just slow the operation down even more, and leave the user cursing you.
As you already appear to have a progress indicator, you are doing all that you can to keep the user informed.
Anything that involves scrolling the document makes your code run more slowly, so the best you can do is ensure your code completes in as short a time as possible. This includes avoiding use of the Selection object and turning off ScreenUpdating.

Related

How to select multiple slides in PowerPoint and then Duplicate them multiple times?

I'm trying to duplicate selected slides multiple times in the same presentation. Could someone please advise where I am going wrong? Thank you
Public Sub DuplicateSlideMultipleTimes()
Dim n As Integer
On Error Resume Next
n = InputBox("How many copies of the selected slides do you want to make?")
Dim mySlides As Slides
Set mySlides = ActiveWindow.Selection.SlideRange
If n >= 1 Then
For numtimes = 1 To n
mySlides.Copy After:=ActivePresentation.Slides(ActivePresentation.Slides.Count)
Next
End If
End Sub
Here's another approach. Instead of looping through each of the selected slides to make a duplicate, it simply copies and pastes. It also places them at the end of the presentation.
Note that mySlides has been appropriately declared as a SlideRange, as Ricardo has already pointed out.
Also note that On Error Resume Next has been removed, since it can hide errors when not used properly, as Ricardo has also pointed out.
Option Explicit
Public Sub DuplicateSlideMultipleTimes()
Dim ans As String
Dim num_copies As Long
num_copies = 0
Do
ans = InputBox("How many copies of the selected slides do you want to make?")
If Len(ans) = 0 Then Exit Sub
If IsNumeric(ans) Then
num_copies = CLng(ans)
If num_copies > 1 Then Exit Do
End If
MsgBox "Invalid entry, try again!", vbExclamation
Loop
Dim mySlides As SlideRange
Set mySlides = ActiveWindow.Selection.SlideRange
Dim i As Long
For i = 1 To num_copies
mySlides.Copy
ActivePresentation.Slides.Paste
Next i
MsgBox "Completed!", vbExclamation
End Sub
You were close.
Some highlights:
Avoid On Error Resume Next whenever possible (this will just hide where you have errors)
Declare all your variables (use Option Explicit at the top of your modules)
You have some variables types wrong
Review code's comments and adjust it to fit your needs
Code:
Option Explicit
Public Sub DuplicateSlideMultipleTimes()
Dim sourceSlide As Slide
Dim selectedSlides As SlideRange
Dim numTimes As Variant
Dim counter As Long
Dim totalCounter As Long
' Ask user for num slides
numTimes = InputBox("How many copies of the selected slides do you want to make?")
' Check if numTimes is a number otherwise, exit procedure
If Not IsNumeric(numTimes) Then Exit Sub
' Set a reference to the selected slides
Set selectedSlides = ActiveWindow.Selection.SlideRange
' Loop through each slide in the selected slides
For Each sourceSlide In selectedSlides
For counter = 1 To numTimes
' Duplicate the slide
sourceSlide.Duplicate
' Track total number of duplicated slides
totalCounter = totalCounter + 1
Next counter
Next sourceSlide
' Display message to user
MsgBox totalCounter & " duplicates generated"
End Sub
Let me know if it works

Word VBA: How to Fix FOR EACH loop to add bookmark to each sentence?

Within a Word docx: I'm trying to add a bookmark to each sentence. For example, at first sentence would be bookmark "bmarkpg01" and second sentence would be bookmark ""bmarkpg01ln01col01"". My code adds only one bookmark to first sentence and doesn't loop through to end of document.
I've tried a for each loop to attempt each sent in sentences and each bmark in bookmark.
Sub tryAddBmarkatSentence()
Dim myRange As Range
Set myRange = ActiveDocument.Content
Dim bmark As Bookmark
Application.ScreenUpdating = False
For Each MySent In ActiveDocument.Sentences
For Each bmark In ActiveDocument.Bookmarks
ActiveDocument.Bookmarks.Add Name:="pmark" & bmark.Range.Information(wdActiveEndAdjustedPageNumber), Range:=myRange 'bmark name would have added info of page, line, and col number. here as example is pagenumber.
Next
Next
End Sub
EXPECTED RESULT: Within entire document, each sentence has a corresponding bookmark and bookmark name ("bmarkpg01ln01col01", "bmarkpg01ln02col10", etc.)
ACTUAL RESULTS: only one bookmark is added to the first sentence of the document.
The following works for me, as far as the requirements in the question go.
Please remember to put Option Explicit at the top of a code page. This will force you to declare ("Dim") variables, but will also save time and trouble as it will prevent typos and warn you of other problems.
A Sentence in Word returns a Range object, so the code below delares MySent As Range. This provides the target Range for the Bookmarks.Add method.
If you won't be doing anything else with the bookmark, it's not strictly necessary to Set bkm = when adding the bookmark. I left it in since it is declared in the code in the question.
It's not necessary to loop the collection of bookmarks - espeicially since there aren't any - they're being added.
I've added some code for naming the bookmarks, as well.
Sub tryAddBmarkatSentence()
Dim doc As Word.Document
Dim MySent As Word.Range
Dim bmark As Bookmark
Application.ScreenUpdating = False
Set doc = ActiveDocument
For Each MySent In doc.Sentences
Set bmark = doc.Bookmarks.Add(Name:="bmark" & _
MySent.Information(wdActiveEndAdjustedPageNumber) & "_" &_
MySent.Information(wdFirstCharacterLineNumber) & "_" & _
MySent.Information(wdFirstCharacterColumnNumber), Range:=MySent)
'bmark name would have added info of page, line, and col number. here as example is pagenumber.
Next
End Sub
u can try like this
Sub tryAddBmarkatSentence()
Dim myRange As Range
Set myRange = ActiveDocument.Content
Dim bmark As Bookmark
Application.ScreenUpdating = False
For Each MySent In ActiveDocument.Sentences
ActiveDocument.Bookmarks.Add ... and the rest of the code.
//i dont know how you define witch bookmark is to asign to that sentence
Next
End Sub

Referring Excel objects which embedded in a MS-Word Document?

I have many Excel objects are there embedded in a MS-Word Document.
I want to calculating the grand total: with summing the totals are there in the each specified excel object and return that grand total in the MS-Word document.
Macro holder is MS-Word Document's VBA module.
Means: I need to access to an specified embedded Excel object, form the MS-Word module, then perform it active, then assign to an object-variable by -For example:- ExcelApplication = GetObject(, "Excel.Application") statement. Then try to access its appropriated total values , by -For example:- Total = Range("Table1[[#Totals],[Amount]]").Value. Point is all tables Name are in the Excel objects is Table1 which contains the Amount Columns and the Total Row.
Note is in above Excel objects, The first row which contains the Table Header is Hided.
Example
Sample File
This document have extending daily.
I need a macro in the Normal.dotm Which calculating the grand total of all specified Excel object (specified with assigning a name to them or ...) and perform returning this value with Selection.TypeText Text:= where is selected in picture below: (at the end of document)
Why I insist to have embedded Excel object?
Because I have formula for calculating Column1: A, B, C, ....
Because I have a hided Data base Sheet for data validation Items
I have Formula in Amount column for multiplying the rates and the
amount of each item-unit which is in Data base sheet
In that case, try something along the lines of:
Sub TallyXLVals()
Application.ScreenUpdating = False
Dim Rng As Range, objOLE As Word.OLEFormat, objXL As Object
Dim i As Long, lRow As Long, sValA As Single, sValB As Single, sValC As Single
Const xlCellTypeLastCell As Long = 11
With ActiveDocument
.ActiveWindow.Visible = False
For i = .InlineShapes.Count To 1 Step -1
With .InlineShapes(i)
If Not .OLEFormat Is Nothing Then
If Split(.OLEFormat.ClassType, ".")(0) = "Excel" Then
Set Rng = .Range
Set objOLE = .OLEFormat
objOLE.Activate
Set objXL = objOLE.Object
With objXL.ActiveSheet
lRow = .UsedRange.Cells.SpecialCells(xlCellTypeLastCell).Row
sValA = sValA + .Range("A" & lRow).Value
sValB = sValB + .Range("B" & lRow).Value
sValC = sValC + .Range("C" & lRow).Value
End With
objXL.Application.Undo
End If
End If
End With
Next
Call UpdateBookmark("BkMkA", Format(sValA, "$#,##0.00"))
Call UpdateBookmark("BkMkB", Format(sValB, "$#,##0.00"))
Call UpdateBookmark("BkMkC", Format(sValC, "$#,##0.00"))
.ActiveWindow.Visible = True
End With
Set objXL = Nothing: Set objOLE = Nothing: Set Rng = Nothing
Application.ScreenUpdating = True
End Sub
Sub UpdateBookmark(StrBkMk As String, StrTxt As String)
Dim BkMkRng As Range
With ActiveDocument
If .Bookmarks.Exists(StrBkMk) Then
Set BkMkRng = .Bookmarks(StrBkMk).Range
BkMkRng.Text = StrTxt
.Bookmarks.Add StrBkMk, BkMkRng
End If
End With
Set BkMkRng = Nothing
End Sub
where the locations you want the outputs to appear are bookmarked, with the names BkMkA, BkMkB, & BkMkC, respectively.
Note: Because you're activating embedded objects, there is unavoidable screen flicker.
Your own effort is insufficent. Here is code to start you off. The code will loop through all the InlineShapes in your Word document, select the first one which represents an Excel worksheet and opens that item for editing. It is the same action which you can recreate in the document by right-clicking on the embedded Excel table, selecting "Worksheet Object" and "Edit".
Private Sub OpenEmbeddedExcelInWord()
' 08 Jan 2018
Dim Shp As InlineShape
For Each Shp In ActiveDocument.InlineShapes
With Shp
If Shp.Type = wdInlineShapeEmbeddedOLEObject Then Exit For
End With
Next Shp
Shp.OLEFormat.Edit
End Sub

Using .Find won't continue, stays on same paragraph

I have a script that looks for some text, inputted by the user. The idea is to look through a document for this text, and when it's found, select the paragraph and ask the user if they want to add this paragraph to an Index.
For some reason, I can't get the script to move past the first selected paragraph. When I run it, and click "Yes" in the UserForm (equivalent of myForm.Tag = 2), it adds to the index, but then when the .Find looks for the next instance of the text, it selects the paragraph I just had highlighted. ...it doesn't continue.
Here's the code:
Sub find_Definitions()
Dim defText As String, findText$
Dim oRng As Word.Range, rng As Word.Range
Dim myForm As frmAddDefinition
Set myForm = New frmAddDefinition
Dim addDefinition$, expandParagraph&
' expandParagraph = 1
Set oRng = ActiveDocument.Range
findText = InputBox("What text would you like to search for?")
With oRng.Find
.Text = findText
While .Execute
Set rng = oRng.Paragraphs(1).Range
rng.Select
defText = oRng.Paragraphs(1).Range
myForm.Show
Select Case myForm.Tag
Case 0 ' Expand the paragraph selection
Do While CLng(expandParagraph) < 1
expandParagraph = InputBox("How many paragraphs to extend selection?")
If expandParagraph = 0 Then Exit Do
Loop
rng.MoveEnd unit:=wdParagraph, Count:=expandParagraph
rng.Select
defText = rng
ActiveDocument.Indexes.MarkEntry Range:=rng, entry:=defText, entryautotext:=defText
Case 1 ' No, do not add to the index
' do nothing
Case 2 ' Yes, add to index
ActiveDocument.Indexes.MarkEntry Range:=rng, entry:=defText, entryautotext:=defText
Case 3 ' Cancel, exit the sub
MsgBox ("Exiting macro")
GoTo lbl_Exit
End Select
Wend
End With
lbl_Exit:
Unload myForm
Set myForm = Nothing
End Sub
(FWIW, I'm pretty new to Word VBA, but very familiar with Excel VBA). Thanks for any ideas.
Note if I click "No" (equivalent of myForm.Tag = 1), then it does move on to the next instance. Hmm.
Try adding rng.Collapse wdCollapseEnd before the "Case 1" line.
Explanation: When you use Find, it executes on the given Range or Selection.
If it's successful, that Range/Selection changes to include the "found" term. In this case, you in addition change the assignment again (expanding to include the paragraph).
When your code loops the current assignment to "Range" is used - in this case, Find looks only at the selected paragraph Range. So you need to reset the Range in order to have Find continue.
To be absolutely accurate, after Collapse you could also add:
rng.End = ActiveDocument.Content.End
Note: it's more correct to use ActiveDocument.Content than ActiveDocument.Range. ActiveDocument.Range is actually a method for creating a new Range by specifying the Start and End points, while ActiveDocument.Content returns the entire main story (body) of the document as a Range object. VBA doesn't care, it defaults the method to return the main story. Other programming languages (.NET, especially C#) don't work as intuitively with Word's object model, however. So it's a good habit to use what "always" works :-)

How to Export a Table (Shape) as JPG from Powerpoint

I am able to export Charts as JPG files from Powerpoint, but haven't been able to do this with a table, which as far as I can tell is still a "Shape" which should be able to export.
This is a cleansed version of the code I use to export the Chart as JPG.
Const imgFilePath as String = "ChartImage.JPG"
Sub ExportChartJPG()
Dim cht as Variant 'this will hold the Chart/Shape object
Set cht = ActivePresentation.Slides(1).Shapes("Chart1").Chart
On Error Resume Next
Kill imgPath
On Error GoTo 0
cht.Export imgPath, "JPG"
End Sub
I figured this would be simple to modify, like:
Sub ExportChartJPG()
Dim cht as Variant 'this will hold the Chart/Shape object
Set cht = ActivePresentation.Slides(1).Shapes("Table1").Table
On Error Resume Next
Kill imgPath
On Error GoTo 0
cht.Export imgPath, "JPG"
End Sub
But this is throwing an error 13 Mismatch.
I have also tried dimensioning cht as a Shape instead of Variant, and setting cht = ActivePresentation.Slides(1).Shapes("Table1"), also unsuccessfully.
Although KazJaw's solution works, it was a bit cumbersome (copying takes additional time to process, I was getting errors I think as a result of not "waiting" long enough for the copy to complete, clipboard issues? etc.)
http://www.tech-archive.net/pdf/Archive/Office/microsoft.public.office.developer.vba/2006-10/msg00046.pdf
I open the object browser, right-click, and show hidden methods, which now allows me to use the Export method on a Shape.
Sub ExportShapeJPG()
Dim cht as Variant 'this will hold the Chart/Shape object
Set cht = ActivePresentation.Slides(1).Shapes("Table1") '<-- removed .Table and only pass the Shape itself
'Likewise, for charts, omit the .Chart:
' Set cht = ActivePresentation.Slides(1).Shapes("Chart1")
On Error Resume Next
Kill imgPath
On Error GoTo 0
cht.Export imgPath, ppShapeFormatJPG '<-- The export syntax is slightly different using ppShapeFormatJPG instead of "JPG"
End Sub
I have one quite weird idea. Look at the code where first part save a chart and second save table.
Sub ExportinChartAndTable()
Dim imgFilePath As String
imgFilePath = ActivePresentation.Path & "\chart"
Dim shp As Shape
Set shp = ActivePresentation.Slides(1).Shapes(1)
Dim shpChart As Chart
Set shpChart = shp.Chart
'exporting chart
On Error Resume Next
Kill imgFilePath
On Error GoTo 0
shpChart.Export imgFilePath & "chart.jpg", "JPG"
Stop
Dim chartPart As ChartData
Set chartPart = shpChart.ChartData
imgFilePath = ActivePresentation.Path & "\dataTable.jpg"
chartPart.Workbook.worksheets("arkusz1").Range("a1:c20").Copy
shpChart.Paste
shpChart.Shapes(1).Width = shp.Width
shpChart.Shapes(1).Height = shp.Height
On Error Resume Next
Kill imgFilePath
On Error GoTo 0
shpChart.Export imgFilePath, "JPG"
End Sub
You have to come up with idea how to check the range of the table. I hoped that CurrentRegion would work but it's not. You could use the possibility to count the amount of rows and columns in the table (it is possible). Or maybe you have fixed range so it would be easy. One more thing, you have to adjust dimension when table is resized.
EDIT due to David comment. I keep the above solution in place as could be useful for others (please refer to comments below)
Sub SolutionSecond()
Dim whereTo As String
whereTo = ActivePresentation.Path & "\table.jpg"
Dim shp As Shape
Set shp = ActivePresentation.Slides(1).Shapes(1)
Dim chrt As Shape
Set chrt = ActivePresentation.Slides(1).Shapes.AddChart
shp.Copy
'required due to excel opening proces
chrt.Select
chrt.Chart.Paste
'set dimensions here
chrt.Chart.Export whereTo, "JPG"
chrt.Delete
End Sub
This one base on the same logic. Copy table into chart which (the only kind of Shape) could be exported.