Existence of shapes in Powerpoint - vba

I would like to build a condition on a command button on a Macro enabled powerpoint presentation. If the shape exists then I would like it deleted, otherwise the button should produce a statement about the fact that there is no such shape. Currently I am having trouble with existence...! How do I get Power point to recognise the shape is null? Here is my code:
If ActivePresentation.Slides(3).Shapes("Picture") Is Nothing Then
MsgBox "no Picture"
Else
ActivePresentation.Slides(3).Shapes("Picture").Delete
MsgBox "Picture Cleared"
End If
This code only produces an error because the shape doesn't exist so the first if statement fails. Perhaps we need to check whether its in the selection pane?

Some of the other suggestions will work but in general, it's bad practice to rely on selection unless absolutely necessary. Instead, you could call a slightly different function:
Function ShapeExists(ByVal oSl as Slide, ByVal ShapeName as String) as Boolean
Dim oSh as Shape
For Each oSh in oSl.Shapes
If oSh.Name = ShapeName Then
ShapeExists = True
Exit Function
End If
Next ' Shape
' No shape here, so though it's not strictly necessary:
ShapeExists = False
End Function
You could also modify this to return a reference to the shape if found or nothing if not.
If you prefer not to use early Exit Functions, there are easy ways to write around that.

As #davidmneedham gives in the link in the comments (#TimWilliams answer), you can use a construct similar to as follows:
Option Explicit
Sub test()
Dim shp As Shape
Dim myshapeName As String
myshapeName = "Picture"
Dim sl As Slide
Set sl = ActivePresentation.Slides(1)
If shapePresent(sl, myshapeName) Then
sl.Shapes(myshapeName).Delete
Else
MsgBox myshapeName & " not present"
End If
End Sub
Private Function shapePresent(ByVal sl As Slide, ByVal myshapeName As String) As Boolean
On Error GoTo errhand
sl.Shapes(myshapeName).Select
shapePresent = True
Exit Function
errhand:
shapePresent = False
Err.Clear
End Function
Using the same format as that answer:
Private Function shapePresent(ByVal sl As Slide, ByVal myshapeName As String) As Boolean
Dim myShape As Shape
On Error Resume Next
Set myShape = sl.Shapes(myshapeName)
On Error GoTo 0
shapePresent = Not myShape Is Nothing
End Function

Related

How to highlight a shape present in a group in Visio VBA

I want to highlight the shape corresponding to a particular group. The following code is only highlighting shapes that are grouped with active page or master but not with the group present in the active page.
Sub CA_Trace_Conflict1()
PCC_CA = InputBox("Enter PCC Band")
'SCC1_CA = InputBox("Enter SCC1 Band")
Dim shp As Visio.Shape
Dim subshp As Visio.Shape
Dim connectorshape As Visio.Shape
Dim BandLinewidth As String
Dim lngShapeIDs() As Long
Dim count As Integer
Dim PCC_Flag As Integer
Dim SCC1_Flag As Integer
PCC_Flag = 0
SCC1_Flag = 0
Dim DiagramServices As Integer
DiagramServices = ActiveDocument.DiagramServicesEnabled
ActiveDocument.DiagramServicesEnabled = visServiceVersion140 + visServiceVersion150
Dim UndoScopeID1 As Long
PCC_CA_space = PCC_CA & " "
For Each shp In Visio.ActivePage.shapes
If shp.Type = 2 Then 'Check if shp is a group
For Each subshp In shp.shapes
If InStr(shp.Text, PCC_CA_space) > 0 Then
'If PCC_CA Like shp.Text Then
Set connectorshape = shp
Debug.Print shp.Parent
Application.ActiveWindow.Page.shapes.ItemFromID(shp.ID).CellsSRC(visSectionObject,visRowLine, visLineWeight).FormulaU = "5.5 pt"
' Debug.Print shp.ID
End If
Next
End If
Next
End Sub
I think you want to select a subshape within a group programmatically. Doing this in Visio is not obvious, so let me help. I'll put links to two articles on my website, plus one on Microsoft's at the end of the post. These discuss selection-related topics in further detail.
So let's tackle your problem...
Setup
Open a blank drawing in Visio
Draw two rectangles, then group them
You now have three shapes on this page.
Sheet.1 is a subshape
Sheet.2 is a subshape
Sheet.3 is the group
You can programmatically select the group like this, as you've discovered:
Public Sub SelectGroup()
'// Get the active window:
Dim win As Visio.Window
Set win = Visio.ActiveWindow
'// Deselect everything:
Call win.DeselectAll
'// Get a shape object:
Dim shp As Visio.Shape
Set shp = Visio.ActivePage.Shapes.ItemFromID(3) '<<----- Sheet.3 is the group!
'// Cause that shape to be selected in the window:
Call win.Select(shp, Visio.VisSelectArgs.visSelect)
'// Cleanup:
Set shp = Nothing
Set win = Nothing
End Sub
By the way, the Sub above is much more nitpicky and long than it has to be. But it will help to have things simple and clean, when you start adding features and behaviors. You can actually one-line the whole procedure like this--you can even paste this into the Immediate window:
Call Visio.ActiveWindow.Select(Visio.ActivePage.Shapes.ItemFromID(3), Visio.VisSelectArgs.visDeselectAll + Visio.VisSelectArgs.visSelect)
Now to subselect Sheet.1 or Sheet.2. One would think we could simply change the shp object to be one of the subshapes, ala:
'// Sheet.1 is a subshape, you'll get an error
Set shp = Visio.ActivePage.Shapes.ItemFromID(1) '<<----- ID = 1
but this won't work. In fact you'll get an "Inappropriate target object for this action" error.
To fix this, we have to pass a different argument to the Select method:
Public Sub SelectSubshape()
'// We've drawn two rectangles on a blank page, then
'// grouped them. Sheet.1 and Sheet.2 are subshapes,
'// Sheet.3 is the group.
'// Get the active window:
Dim win As Visio.Window
Set win = Visio.ActiveWindow
'// Deselect everything:
Call win.DeselectAll
'// Get a subshape object:
Dim shp As Visio.Shape
Set shp = Visio.ActivePage.Shapes.ItemFromID(2)
'// Cause that shape to be SUBSELECTED in the window.
'// Note the different argument: visSubSelect
Call win.Select(shp, Visio.VisSelectArgs.visSubSelect) ' <<------ visSubSelect!
'// Cleanup:
Set shp = Nothing
Set win = Nothing
End Sub
Voila! Subshape selected in the active window!
If you want to detect which shapes are already selected, then you'll have to fiddle with the IterationMode property of a Selection object. This is pretty confusing, plus I don't think you're asking for that right now. But knowing the term will help you search for help in the future, should you need it.
Articles
Getting a Handle on Selecting and Subselecting Visio Shapes
Detect Sub-selected Shapes Programmatically
Selection.Select method (Visio)

Moving video on every slide in powerpoint

I'm just poking around in VBA with PowerPoint and know that what I want to do can be done. I just don't know the write calls/sytanx to use!
I want a macro that will run through all the slides in a presentation and move the video object to specific spot on the slide. The spot will be the same on all slides.
It would be very helpful if someone could show me how to do this! Or at least point me in the right direction. Thanks!
Here's what I found for doing something on every slide
Sub EveryTextBoxOnSlide()
' Performs some operation on every shape that contains text on every slide
' (doesn't affect charts, tables, etc)
Dim oSh As Shape
Dim oSl As Slide
On Error GoTo ErrorHandler
For Each oSl In ActivePresentation.Slides
For Each oSh In oSl.Shapes
With oSh
If .HasTextFrame Then
If .TextFrame.HasText Then
' If font size is mixed, don't touch the font size
If .TextFrame.TextRange.Font.Size > 0 Then
.TextFrame.TextRange.Font.Size = .TextFrame.TextRange.Font.Size + 2
End If
End If
End If
End With
Next ' shape
Next ' slide
NormalExit:
Exit Sub
ErrorHandler:
Resume Next
End Sub
And this is something I found to move the object to desired location (here's where I don't know what to call the video object)
With ActiveWindow.Selection.ShapeRange
.Left = 640 'change the number for desired x position
.Top = 75 'change the number for desired y position
End With
End Sub
Sub ll()
End Sub
So basically, I want to run the last piece as a function of the first one instead of shapes with text. Does that make sense?
I've done some programming in the past, mainly with actionscript and Flash. I could probably write out some basic functions, just not sure how to run it in VBA without learning a whole new language. Which I don't want to do, since I'm an instructional designer and don't have the free time to learn it! :)
Here's a little function that'll return True if the shape you pass to it is a video, and a bit of example code to test it with:
Function IsVideo(oSh As Shape) As Boolean
If oSh.Type = msoMedia Then
If oSh.MediaType = ppMediaTypeMovie Then
IsVideo = True
Exit Function
End If
End If
' Things get a little trickier if the movie is in a placeholder
' Is it a placeholder? and is it a media object?
If oSh.Type = msoPlaceholder Then
If oSh.PlaceholderFormat.ContainedType = msoMedia Then
' it's a media object, but we don't know if it's
' a movie or sound or what, so we duplicate it
' then look at the duplicate (which is now a copy
' of the placeholder content but is not a placeholder itself)
With oSh.Duplicate
If .Type = msoMedia Then
If .MediaType = ppMediaTypeMovie Then
IsVideo = True
End If
End If
' and delete the duplicate
.Delete
End With
End If
End If
End Function
Sub thing()
Dim oSl As Slide
Dim oSh As Shape
For Each oSl In ActivePresentation.Slides
For Each oSh In oSl.Shapes
If IsVideo(oSh) Then
oSh.Left = 0
End If
Next
Next
End Sub
Now you're getting somewhere!
I don't know what kind of shape holds your video, so this modification should help you identify it.
Sub EveryTextBoxOnSlide()
Dim oSh As Shape
Dim oSl As Slide
On Error GoTo ErrorHandler
For Each oSl In ActivePresentation.Slides
For Each oSh In oSl.Shapes
With oSh
.Select
End With
Next
Next
NormalExit:
Exit Sub
ErrorHandler:
Resume Next
End Sub
While there is no need whatsoever to use the .Select in your final code, this is only here to help you identify which shape actually holds the video. Put a breakpoint (F9) on that line and run your code (F5), then debug through it with F8 to execute one line at a time and run through your loop looking at each shape until you see that your video has been selected.
Once your video has been selected look at the various properties (using the Immediate Window of the IDE) of oSh until you find the uniqueness that identifies this is your video. (There may be some property about the content type of the shape, or a link on where to find the contents that contains the text string ".avi", ".mpg", ".flv", etc - there will be some sort of identifier you can find.)
Once you've identified what makes the shape the holder of a video, replace
.Select
with
If {my video containing shape criteria is true} Then
With .Selection.ShapeRange
.Left = 640 'change the number for desired x position
.Top = 75 'change the number for desired y position
End With
End If
Changing the values (as commented) to whatever it is that you need.
Important Note: It is very good that your sample code has some error handling built in, but all this error handler is doing is sweeping any errors under the rug. This particular handler is the very long form version of On Error Resume Next which does have its uses, but in very, very limited situations.
As you get your code working, you'll actually want to put something useful in the ErrorHandler: section, but that's the topic for a whole new question.
Ok, here is how I modified the code supplied above to do this in my case:
Sub EveryTextBoxOnSlide() ' Performs some operation on every shape that contains text on every slide ' (doesn't affect charts, tables, etc) Dim oSh As Shape Dim oSl As Slide On Error GoTo ErrorHandler
For Each oSl In ActivePresentation.Slides
For Each oSh In oSl.Shapes
With oSh
If .Type = msoMedia Then
If .MediaType = ppMediaTypeMovie Then
.PictureFormat.Crop.PictureHeight = 236
.PictureFormat.Crop.PictureWidth = 314.2115
.PictureFormat.Crop.PictureOffsetX = 8.737323
.PictureFormat.Crop.PictureOffsetY = 0
.PictureFormat.Crop.ShapeHeight = 236.3478
.PictureFormat.Crop.ShapeWidth = 163
.PictureFormat.Crop.ShapeLeft = 796.6956
.PictureFormat.Crop.ShapeTop = 0
End If
End If
End With
Next ' shape Next ' slide
NormalExit: Exit Sub
ErrorHandler: Resume Next
End Sub

VBA Powerpoint: Delete Shape with specific text. Run-time error '-2147024809 (80070057)': The specified value is out of range

I have a very long ppt presentation (about 850 slides) and the second half is full of shapes with certain text that I would like to delete. Sadly, it appears that is has nothing to do with the Slide Master, so I can't use that.
I got an error:
Run-time error '-2147024809 (80070057)':
The specified value is out of range
Here's the code, I got at the moment
Sub DeleteShapeWithSpecTxt()
Dim oSl As Slides, oSh As Shapes, oTr As TextRange
Dim str As String
Dim testcomp1, testcomp2
Dim lppt, ShapeNb, k, j As Long
Dim pptAct
Set pptAct = PowerPoint.ActivePresentation
str = pptAct.Slides(335).Shapes(4).TextFrame.TextRange.Text
lppt = pptAct.Slides.Count
For k = 1 To lppt
ShapeNb = pptAct.Slides(k).Shapes.Count
For j = 1 To ShapeNb
If pptAct.Slides(k).Shapes(j).HasTextFrame And StrComp(str, pptAct.Slides(k).Shapes(j).TextFrame.TextRange.Text) = 0 Then
pptAct.Slides(k).Shapes(j).Delete
End If
Next
Next
End Sub
There are several reasons this code could raise an error. Firstly, if slide 335 or shape 4 doesn't exist (try to make those numbers dynamic or handle errors). Next, your If line will evaluate both parts so if the shape doesn't have a TextFrame, VBA will still try to evaluate the second part and hence raise an error. Finally, you also need to count backwards in any object collection that you may delete objects. You could also simplify this using the For Each Next construct and optionally pass the search text to the procedure from your main code:
Sub DeleteShapeWithSpecTxt(Optional sSearch As String)
Dim oSld As Slide
Dim oShp As Shape
Dim lShp As Long
On Error GoTo errorhandler
If sSearch = "" Then sSearch = ActivePresentation.Slides(335).Shapes(4).TextFrame.TextRange.Text
For Each oSld In ActivePresentation.Slides
' I would usually use the next line to loop through all shapes on the slide but can't in this case as shapes may be deleted
'For Each oShp In oSld.Shapes
For lShp = oSld.Shapes.Count To 1 Step -1
With oSld.Shapes(lShp)
If .HasTextFrame Then
If StrComp(sSearch, .TextFrame.TextRange.Text) = 0 Then .Delete
End If
End With
Next
Next
Exit Sub
errorhandler:
Debug.Print "Error in DeleteShapeWithSpecTxt : " & Err & ": " & Err.Description
On Error GoTo 0
End Sub
If you want to make the search text dynamic, this is a nice simple method. Just replace the If sSearch = ""... line with this:
If sSearch = "" Then sSearch = InputBox("Enter test to search for and all shapes matching the text will be deleted across this presentation:","Delete Matching Shapes","test")
#JamieG Thank you, I found the same solutions (but not as neat as your code). I was going to post it when I saw your answer
Cheers
EDIT: More precision: The dynamic setting of the string was kind of difficult (my knowledge of VBA isn't very advanced). For that reason it was a lot easier for me to select the text in a certain slide/shape.
The comment on IF was on point, as well as the backwards counting when deleting

Copying cell value to textbox vba

I have been trying to write a macro that will dynamically fill a textbox on a new sheet will the value of a cell from another sheet.
I have managed to get it working using this:
Sub copyDetail()
' Define variables
Dim pre As Worksheet
Dim des As Worksheet
Set pre = Sheets("Presentation")
Set des = Sheets("Description")
Dim i As Integer
Dim lbl As String
' Scroll through labels and copy where boolean = 1
For i = 2 To 17
If des.Cells(i, 2) = 1 Then
lbl = des.Cells(i, 11)
Sheets("Presentation").Select
ActiveSheet.Shapes.Range(Array("TextBox 1")).Select
Selection.Text = lbl
Else
End If
Next i
End Sub
I basically want to be able to do exactly what this does but without using select all the time as this changes sheets and slows down my code (I have many other sub's to run alongside this one). I've tried things like defining the textbox using this:
Dim myLabel As Object
Set myLabel = pre.Shapes.Range(Array("TextBox 1"))
But then I get an "object doesn't support this property or method" error when I try and call:
myLabel.Text = lbl
You can set the text of a TextBox like so:
ActiveSheet.Shapes("TextBox 1").TextFrame.Characters.Text = "Hello world"
You can set-up a little helper Sub in a Module to make the code re-usable:
Public Sub SetTextBoxText(ws As Worksheet, strShapeName As String, strText As String)
Dim shp As Shape
On Error Resume Next
Set shp = ws.Shapes(strShapeName)
If Not shp Is Nothing Then
shp.TextFrame.Characters.Text = strText
Else
Debug.Print "Shape not found"
End If
End Sub

Passing a specific shape as a function argument

Using Powerpoint 2010 and MS Visual Basic for applications:
I'm trying to pass a specific shape as an argument... Tried different syntax or ways of doing it but no luck, it seems to block at using the oShape Variable between the functions.
Function ClickBtn1() sets the oShape variable to the name of the shape to be modified and the calls the modifying function Incre().
Incre() set a numerical value to 12, updates the text range from the shape to it and then changes the forecolor to 10, 10, 10 and it then redraws the slide...
What I have goes like this:
Dim oShape As Shape
Dim x As Long
Sub ClickBtn1()
MsgBox "Inside ClickBtn1"
oShape = ActivePresentation.Slides(7).Shapes("ParaIcon")
Incre
End Sub
Sub Incre()
MsgBox "inside Incre"
x = 12
oShape.TextFrame.TextRange.Text = x
oShape.Fill.ForeColor.RGB = RGB(10, 10, 10)
SlideShowWindows(7).View.GotoSlide (SlideShowWindows(7).View.Slide.SlideIndex)
End Sub
I have an arrow shape with action set to "run macro ClickButton1" and a rectangle called "ParaIcon" on slide 7 of a Powerpoint document...
Any suggestions?
Thanks!
I'd do it like so instead; avoid global variables and use SET for assigning object references to variables.
Sub ClickBtn1()
Dim oShape as Shape
MsgBox "Inside ClickBtn1"
SET oShape = ActivePresentation.Slides(7).Shapes("ParaIcon")
Incre oShape
End Sub
Sub Incre(oShape as Shape)
Dim x as Long
MsgBox "inside Incre"
x = 12
' Convert numbers to string before assigning text
oShape.TextFrame.TextRange.Text = Cstr(x)
oShape.Fill.ForeColor.RGB = RGB(10, 10, 10)
SlideShowWindows(7).View.GotoSlide (SlideShowWindows(7).View.Slide.SlideIndex)
End Sub