I'm getting a "Object variable or With block variable not set" error in my code.
This is my first crack at macro writing. I do have programming knowledge but this is new to me.
Anyway, I want to go through the presentation, and for every page that has any text in the notes section, I want to add a new slide (following it) containing that text.
Here's what I tried:
Sub SlideSort()
Dim curSlide As Slide
Dim newSld As Slide
Dim curPres As Presentation
Dim curShape As Shape
Dim i As Integer
For i = 1 To ActivePresentation.Slides.Count
curSlide = ActivePresentation.Slides(i)
For Each curShape In curSlide.NotesPage.Shapes
If curShape.Type = msoPlaceholder Then
If curShape.PlaceholderFormat.Type = ppPlaceholderBody Then
If curShape.TextFrame.TextRange <> "" Then
Set newSld = ActivePresentation.Slides.Add(Index:=i + 1, Layout:=ppLayoutText)
newSld.Shapes(2).TextFrame.TextRange = curShape.TextFrame.TextRange
i = i + 1
End If
End If
End If
Next curShape
Next i
End Sub
The line that gives the error is curSlide = ActivePresentation.Slides(i)
Use Set curSlide = ActivePresentation.Slides(i) - it's an object, and should be operated via Set.
You need to use Set here, as you have with other objects:
Set curSlide = ActivePresentation.Slides(i)
Bingo. It's a bug in the Mac version of PowerPoint. I can repro the problem on the Mac.
.PlaceholderFormat.Type isn't supported on Mac PowerPoint, though it should be.
It's not 100% reliable, but you can pick up the second shape on the notes page as the body text placeholder instead:
Sub SlideSort()
Dim curSlide As Slide
Dim newSld As Slide
Dim curPres As Presentation
Dim curShape As Shape
Dim i As Integer
For i = 1 To ActivePresentation.Slides.Count
curSlide = ActivePresentation.Slides(i)
curShape = curSlide.NotesPage.Shapes(2)
If curShape.TextFrame.TextRange <> "" Then
Set newSld = ActivePresentation.Slides.Add(Index:=i + 1, Layout:=ppLayoutText)
newSld.Shapes(2).TextFrame.TextRange = curShape.TextFrame.TextRange
i = i + 1
End If
Next i
End Sub
I suspect you may also run into issues because you're looking at Slide.Count in the loop, but by adding slides, you're modifying Slide.Count.
Related
How do I group all objects (or "shapes"?) on a slide and resize that group?
Subsequently the "big" group should be ungrouped.
My attempt fails with "function expected":
Sub Group_And_Resize()
Dim Sld As Slide
With Sld.Shapes
With .SelectAll.Group //Error here
.Width = 907
End With
End With
End Sub
By manual experimentation, I learned that some objects (or "shapes"?) cannot be added to a group, such as slide numbers which are automatically generated. Is there any possibility to exclude those from the selection?
If you want to group the shapes together first and then set the overall width to 907, you can use this code:
Sub Group_And_Resize()
Dim Sld As Slide, a As Variant, i As Integer
Set Sld = ActivePresentation.Slides(1) ' your slide
ReDim a(1 To Sld.Shapes.Count)
For i = LBound(a) To UBound(a)
a(i) = Sld.Shapes(i).Name
Next
Sld.Shapes.Range(a).Group.Width = 907
End Sub
If you want to make the width of each of the shapes on the slide = 907, you can use the following code:
Sub Group_And_Resize()
Dim Sld As Slide
Set Sld = ActivePresentation.Slides(1) ' your slide
Sld.Shapes.Range.Width = 907
End Sub
This is based on Алексей Р's answer but solves a few problems and also is more generic. It allows calling the routine on any slide in the active presentation and setting the width to any desired value. See comments for more details.
Sub Test()
With ActivePresentation
Call Group_And_Resize(.Slides(1), 200)
End With
End Sub
Sub Group_And_Resize(Sld As Slide, sngWidth As Single)
Dim a As Variant, i As Long ' Array indices are longs, not integers
Dim oGroup As Shape
' Call NonPlaceholderShapeCount to get number of
' shapes that are not placeholders, since placeholders
' cannot be grouped. Use that to ReDim the array:
ReDim a(1 To NonPlaceholderShapeCount(Sld))
For i = LBound(a) To UBound(a)
' Again, make sure we don't try to group placeholders
If Not Sld.Shapes(i).Type = msoPlaceholder Then
a(i) = Sld.Shapes(i).Name
End If
Next
' Get a reference to the new group
' since we need to set several properties on it
Set oGroup = Sld.Shapes.Range(a).Group
' This ensures that the group (and its shapes)
' aren't distorted:
oGroup.LockAspectRatio = True
' and finally, set the width
oGroup.Width = sngWidth
End Sub
Function NonPlaceholderShapeCount(Sld As Slide) As Long
' Returns the number of non-placeholder shapes on Sld
Dim x As Long
Dim lCount As Long
With Sld
For x = 1 To .Shapes.Count
If Not .Shapes(x).Type = msoPlaceholder Then
lCount = lCount + 1
End If
Next
End With
NonPlaceholderShapeCount = lCount
End Function
I have put together an e-learning module. I am still very new at vba though. I am trying to make a dynamic main menu which contains multiple text boxes. If the text in a text box matches the title of a slide, that shape should then then be hyperlinked to the corresponding slide. Ideally, the text boxes on the Main Menu would contain the names of Sections and hyperlink to the first slide in the named section, but I couldn't figure that out, so instead I made the title of the first slide in each section match the text. I've searched and searched and gotten as close as I could. I am hoping someone can help me finish it. I have gotten past several errors, and have the text hyperlinked, however all linked take the user to the last slide in the presentation instead of the proper slide. Thank you in advance for any guidance!!
Here is the code:
Sub TestMe()
'Original Source: http://www.steverindsberg.com/pptlive/FAQ00056.htm
Dim aSl As Slide 'active slide
Dim dSl As Slide 'destination slide
Dim Slde As Slide
Dim oSh As Shape
Dim aSl_ID As Integer
Dim aSl_Index As Integer
Dim dSl_ID As Integer
Dim dSl_Index As Integer
Dim sTextToFind As String
Dim hypstart As String
Dim Titl As String
Set aSl = Application.ActiveWindow.View.Slide 'active slide
aSl_Index = Application.ActiveWindow.View.Slide.SlideIndex 'active slide index
' Set ActiveSld_Index =
' Set DestinationSld_ID = oSl.SlideID
' Set DestinationSld_Index = oSl.SlideIndex
For Each oSh In aSl.Shapes
'If IsSafeToTouchText(oSh) = True Then
sTextToFind = oSh.TextFrame.TextRange.Text
'loop through slides looking for a title that matches the text box value
On Error Resume Next
Set dSl = FindSlideByTitle(sTextToFind)
' get the information required for the hyperlink
dSl_ID = CStr(dSl.SlideID)
dSl_Index = CStr(dSl.SlideIndex)
' find the text string in the body
hypstart = InStr(1, sTextToFind, sTextToFind, 1)
'make the text a hyperlink
With oSh.TextFrame.TextRange.Characters(hypstart, Len(sTextToFind)).ActionSettings(ppMouseClick).Hyperlink
.SubAddress = dSl_ID & "," & dSl_Index & "," & sTextToFind
End With
'End If
Next oSh
End Sub
Public Function FindSlideByTitle(sTextToFind As String) As Slide
'Source: http://www.steverindsberg.com/pptlive/FAQ00056.htm
Dim oSl As Slide
Dim oSh As Shape
With ActivePresentation
For Each oSl In .Slides
For Each oSh In oSl.Shapes
With oSh
'If .HasTextFrame Then
'If Not .TextFrame.TextRange.Text Is Nothing Then
'myPres.Slides(1).Shapes.Title.TextFrame.TextRange
On Error Resume Next
If UCase(.TextFrame.TextRange.Text) = UCase(sTextToFind) Then
'If UCase(.TextRange.Text) = UCase(sTextToFind) Then
Set FindSlideByTitle = oSl
'End If
End If
'End If
End With
Next
Next
End With
End Function
Public Function IsSafeToTouchText(pShape As Shape) As Boolean
'Source: http://www.steverindsberg.com/pptlive/FAQ00056.htm
On Error GoTo ErrorHandler
If pShape.HasTextFrame Then
If pShape.TextFrame.HasText Then
' Errors here if it's a bogus shape:
If Len(pShape.TextFrame.TextRange.Text) > 0 Then
' it's safe to touch it
IsSafeToTouchText = True
Exit Function
End If ' Length > 0
End If ' HasText
End If ' HasTextFrame
Normal_Exit:
IsSafeToTouchText = False
Exit Function
ErrorHandler:
IsSafeToTouchText = False
Exit Function
End Function
Here is the revised code. I have gone in circles and am now stuck. Any suggestions are much appreciated!
After I restored the original function (FindSlideByTitle), I kept getting an error on got an error on .textframe.textrange, making me think that the type of shape I used on my slide (freeform) needed TextFrame2, so I edited that, which fixed the error, but since then I've not been able to make the hyperlink work and have tried instead to use GoTo Slide by including the parent.
I even tried making an array of all freeform shapes on the slide, but I'm still new at this and perhaps I don't fully understand the concepts yet. As it currently stands, I don't get any errors, however, when I click one of the shapes, the shape's appearance changes from the click, but it doesn't go anywhere.
I have also included an image of the actual slide.
Sub TestLinkShapesToSlideTitles()
Dim aSl, dSl, oSl As Slide 'active slide, destination slide
Dim oSh As PowerPoint.Shape
Dim aSl_ID, dSl_ID As Integer
Dim aSl_Index, dSl_Index As Long
Dim dSl_Title, hypstart, Titl As String
Dim sTextToFind As String
Dim numshapes, numFreeformShapes As Long
Dim FreeformShpArray As Variant
Dim ShpRange As Object
Dim oPres As Presentation
Set aSl = Application.ActiveWindow.View.Slide 'active slide
aSl_Index = Application.ActiveWindow.View.Slide.SlideIndex 'active slide index
''''''''''''''''''''''''''''
'In this section I tried to make an array of all the freeform shapes on the slide, thinking that would help.
With aSl.Shapes
numshapes = .Count
'Continues if there are Freeform shapes on the slide
If numshapes > 1 Then
numFreeformShapes = 0
ReDim FreeformShpArray(1 To numshapes)
For i = 1 To numshapes
'Counts the number of Freeform Shapes on the Slide
If .Item(i).Type = msoFreeformShape Then
numFreeformShapes = numFreeformShapes + 1
FreeformShpArray(numFreeformShapes) = .Item(i).Name
End If
Next
'Adds Freeform Shapes to ShapeRange
If numFreeformShapes > 1 Then
ReDim Preserve FreeformShpArray(1 To numFreeformShapes)
Set ShpRange = .Range(FreeformShpArray)
'asRange.Distribute msoDistributeHorizontally, False
End If
End If
End With
''''''''''''''''''''''''''
On Error Resume Next
'Loop through all the shapes on the active slide
For Each oSh In aSl.Shapes
If oSh.Type = msoFreeform Then 'oSh.Type = 5
'If oSh.HasTextFrame Then
If oSh.TextFrame2.HasText Then 'results in -1
With oSh
sTextToFind = .TextFrame2.TextRange.Characters
'sTextToFind results in "Where to Begin"
'.TextFrame2.TextRange.Characters results in "Learn the Lingo", which is the shape after Where to Begin.
End With
End If
'End If
'If IsSafeToTouchText(oSh) = True Then
'With oSh.TextFrame
'sTextToFind = .TextRange.Characters.Text
'loop through slides looking for a title that matches the text box value
'For Each oSl In ActivePresentation.Slides
'If oSl.Shapes.HasTitle Then
'Titl = Slde.Shapes.Title.TextFrame.TextRange <<<<< I kept getting the error here...
On Error Resume Next
Set dSl = FindSlideByTitle_Original(sTextToFind)
' get the information required for the hyperlink
dSl_Title = dSl.Shapes.Title.TextFrame.TextRange
dSl_ID = dSl.SlideID
dSl_Index = dSl.SlideIndex
With oSh
.ActionSettings(ppMouseClick).Parent.Parent.View.GoToSlide dSl_Index, msoFalse 'Go to slide and don't reset animations
End With
' find the text string in the body
'hypstart = InStr(1, sTextToFind, dSl_Title, 1)
'make the text a hyperlink
'With oSh.TextFrame.TextRange.Characters(hypstart, Len(sTextToFind)).ActionSettings(ppMouseClick).Hyperlink
'.SubAddress = dSl_ID & "," & dSl_Index & "," & sTextToFind
'End With
'End With
End If
'End If
Next oSh
End Sub
Public Function FindSlideByTitle_Original(sTextToFind As String) As Slide
'Source: https://stackoverflow.com/questions/25038952/vba-powerpoint-select-a-slide-by-name
Dim oSl As Slide
For Each oSl In ActivePresentation.Slides
With oSl.Shapes.Title.TextFrame
If .HasText Then
If UCase(.TextRange.Text) = UCase(sTextToFind) Then
Set FindSlideByTitle_Original = oSl
End If
End If
End With
Next
End Function
I am currently using this code to update all links in my powerpoint presentation:
Sub UpdateLinks()
Dim ExcelFile
Dim exl As Object
Set exl = CreateObject("Excel.Application")
ExcelFile = "C:\Users\J\Documents\Reporting\Governance Physical Charts.xlsm"
Dim i As Integer
Dim k As Integer
'Go through every slide
For i = 1 To ActivePresentation.Slides.Count
With ActivePresentation.Slides(i)
'Go through every shape on every slide
For k = 1 To .Shapes.Count
On Error Resume Next
'Set the source to be the same as teh file chosen in the opening dialog box
.Shapes(k).LinkFormat.SourceFullName = ExcelFile
If .Shapes(k).LinkFormat.SourceFullName = ExcelFile Then
'If the change was successful then also set it to update automatically
.Shapes(k).LinkFormat.AutoUpdate = ppUpdateOptionAutomatic 'other option is ppUpdateOptionManual
End If
Next k
End With
Next i
End Sub
Instead of updating the link of every chart in the presentation, is it possible to have this code loop through only selected slides? Or if it's easier - is it possible to set a range? For example, only update charts on slides 15-30?
Thank you!
EDIT:
Resolution provided in comments - here is my revised code
Sub UpdateLinks()
Dim ExcelFile
Dim exl As Object
Set exl = CreateObject("Excel.Application")
Dim sld As Slide
ExcelFile = "C:\Users\J\Documents\Reporting\Governance Physical Charts.xlsm"
Dim i As Integer
Dim shp As Shape
For Each sld In ActivePresentation.Slides.Range(Array(11, 12, 13, 14, 15, 16, 17, 18))
For Each shp In sld.Shapes
On Error Resume Next
shp.LinkFormat.SourceFullName = ExcelFile
If shp.LinkFormat.SourceFullName = ExcelFile Then
shp.LinkFormat.AutoUpdate = ppUpdateOptionAutomatic 'other option is ppUpdateOptionManual
End If
Next shp
Next
End Sub
Yes you can compose custom ranges on Slides as well as on Shapes, using an Array as the index parameter. Try this:
Dim sld As Slide
For Each sld In ActivePresentation.Slides.Range(Array(1, 3, 5))
Debug.Print sld.Name
Next
Output:
Slide2
Slide4
Slide6
p.s. I had deleted a slide in the test presentation.
Since you also mentioned processing just selected slides, you can do that like so:
Sub SelectedSlides()
Dim osl As Slide
For Each osl In ActiveWindow.Selection.SlideRange
Debug.Print osl.SlideIndex
Next
End Sub
Note that this will give you the selected slides in REVERSE order of selection. That is, if you control-click slides 2,4,6, this will give you 6,4,2.
I would like to make an array (Set Selected_slds = ActivePresentation.Slides.Range) of slides. The slides are to be part of an array that can be selected from a number of Check boxes. So, the idea of the code I would like to write is:
Dim k As Integer
Dim list_of_slides As Array
Dim Selected_slds As SlideRange
For k = 1 To Count(CheckBoxs)
If CheckBox(k) = True Then
add.slide(k) to list_of_slides
End If
Next
Set Selected_slds = list_of_slides
The above is part of a longer code, where Selected_slds is used.
What I have done so far is this:
Sub Test()
Dim oSh As Shape
Dim oSl As Slide
Dim k As Integer
' Look at each shape on each slide in the active pres
For Each oSl In ActivePresentation.Slides
For Each oSh In oSl.Shapes
' Is the shape a control?
If oSh.Type = msoOLEControlObject Then
' Is it a checkbox?
If InStr(UCase(oSh.OLEFormat.ProgID), "FORMS.CHECKBOX") > 0 Then
' BINGO, found what we're after, so ...
If oSh.OLEFormat.Object.Value = True Then
MsgBox "Checked"
ElseIf oSh.OLEFormat.Object.Value = False Then
MsgBox "Uncheked"
End If
End If
End If
Next ' Shape
Next ' Slide
End Sub
Which works, but I am not sure what to do, to get it to do what I am looking for. It is coded in power point.
I am trying to replace a set of tags in the text of a powerpoint slide from Excel using VBA. I can get the slide text as follows:
Dim txt as String
txt = pptSlide.Shapes(jj).TextFrame.TextRange.Characters.text
I then run through replacing my tags with the requested values. However when I set do
pptSlide.Shapes(jj).TextFrame.TextRange.Characters.text = txt
Problem: All the formatting which the user has set up in the text box is lost.
Background:
The shape object is msoPlaceHolder and contains a range of text styles including bullet points with tags which should be replaced with numbers for instance. The VBA should be unaware of this formatting and need only concern itself with the text replacement.
Can anyone tell me on how to modify the text while keeping the style set up by the user.
Thanks.
Am using Office 2010 if that is helpful.
The solution by Krause is close but the FIND method returns a TextRange object that has to be checked. Here is a complete subroutine that replaces FROM-string with TO-string in an entire presentation, and DOESN'T mess up the formatting!
Sub Replace_in_Shapes_and_Tables(pPPTFile As Presentation, sFromStr As String, sToStr As String)
Dim sld As Slide
Dim shp As Shape
Dim i As Long
Dim j As Long
Dim m As Long
Dim trFoundText As TextRange
On Error GoTo Replace_in_Shapes_and_Tables_Error
For Each sld In pPPTFile.Slides
For Each shp In sld.Shapes
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then ' only perform action on shape if it contains the target string
Set trFoundText = shp.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.TextFrame.TextRange.Find(sFromStr).Delete
End If
End If
End If
If shp.HasTable Then
For i = 1 To shp.Table.Rows.Count
For j = 1 To shp.Table.Columns.Count
Set trFoundText = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Delete
End If
Next j
Next i
End If
Next shp
Next sld
For Each shp In pPPTFile.SlideMaster.Shapes
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then
Set trFoundText = shp.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.TextFrame.TextRange.Find(sFromStr).Delete
End If
End If
End If
If shp.HasTable Then
For i = 1 To shp.Table.Rows.Count
For j = 1 To shp.Table.Columns.Count
Set trFoundText = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Delete
End If
Next j
Next i
End If
Next shp
On Error GoTo 0
Exit Sub
Replace_in_Shapes_and_Tables_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure Replace_in_Shapes_and_Tables of Module modA_Code"
Resume
End Sub
While what Steve Rindsberg said is true I think I have come up with a decent workaround. It is by no means pretty but it gets the job done without sacrificing the formatting. It uses Find functions and Error Controlling for any text box that doesn't have the variable you are looking to change out.
i = 1
Set oPs = oPa.ActivePresentation.Slides(oPa.ActivePresentation.Slides.Count)
j = 1
Do Until i > oPa.ActivePresentation.Slides.Count
oPa.ActivePresentation.Slides(i).Select
Do Until j > oPa.ActivePresentation.Slides(i).Shapes.Count
If oPa.ActivePresentation.Slides(i).Shapes(j).HasTextFrame Then
If oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.HasText Then
On Error GoTo Err1
If oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Find("[specific search term]") = "[specific search term]" Then
m = oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Find("[specific search term]").Characters.Start
oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Characters(m).InsertBefore ([replace term])
oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Find("[specific search term]").Delete
ExitHere:
End If
End If
End If
j = j + 1
Loop
j = 1
i = i + 1
Loop
Exit Sub
Err1:
Resume ExitHere
End Sub
Hope this helps!
I found the solution using the code below. It edits the notes by replacing "string to replace" with "new string". This example is not iterative and will only replace the first occurrence but it should be fairly easy to make it iterative.
$PowerpointFile = "C:\Users\username\Documents\test.pptx"
$Powerpoint = New-Object -ComObject powerpoint.application
$ppt = $Powerpoint.presentations.open($PowerpointFile, 2, $True, $False)
$ppt.Slides[3].Shapes[2].TextFrame.TextRange.Text
$ppt.Slides[3].NotesPage.Shapes[2].TextFrame.TextRange.Text
foreach($slide in $ppt.slides){
$TextRange = $slide.NotesPage.Shapes[2].TextFrame.TextRange
$find = $TextRange.Find('string to replace').Start
$TextRange.Find('string to replace').Delete()
$TextRange.Characters($find).InsertBefore('new string')
$TextRange.Text
}
$ppt.SaveAs("C:\Users\username\Documents\test2.pptx")
$Powerpoint.Quit()