Replace a text box with a placeholder (Title or Body) - vba

I received a PowerPoint file with multiple slides which were supposed to be templates (designs - customlayouts) but instead were regular slides.
Transforming them into SlideMaster and custom layouts and replacing the titles and bodys (textboxes) with actual placeholders by hand was a pain.
So I came with this script to make the process faster.
If anybody has a better approach, it's welcome.

Had to look for a workaround to get the customlayout object.
Several things are missing, for example error handling.
To test it, copy a textbox into a slidemaster layout slide, select it and run the ReplaceWithPHTitle macro
Option Explicit
Public Sub ReplaceWithPHTitle()
ReplaceTexboxWithPlaceholder ppPlaceholderTitle
End Sub
Public Sub ReplaceWithPHBody()
ReplaceTexboxWithPlaceholder ppPlaceholderBody
End Sub
Private Sub ReplaceTexboxWithPlaceholder(ByVal placeholderType As PpPlaceholderType)
Dim targetLayout As CustomLayout
Dim activeShape As Shape
Dim newPlaceHolder As Shape
Set activeShape = ActiveWindow.Selection.ShapeRange(1)
Set targetLayout = activeShape.Parent
Set newPlaceHolder = targetLayout.Shapes.AddPlaceholder(Type:=placeholderType, Left:=activeShape.Left, Top:=activeShape.Top, Width:=activeShape.Width + 15, Height:=activeShape.Height)
With newPlaceHolder.TextFrame
.TextRange.Font.Name = activeShape.TextFrame.TextRange.Font.Name
.TextRange.Characters.Font.Color.RGB = activeShape.TextFrame.TextRange.Characters.Font.Color.RGB
.TextRange.Font.Size = activeShape.TextFrame.TextRange.Font.Size
.TextRange.Font.Bold = activeShape.TextFrame.TextRange.Font.Bold
.TextRange.ParagraphFormat.Bullet.Type = activeShape.TextFrame.TextRange.ParagraphFormat.Bullet.Type
.TextRange.ParagraphFormat.SpaceWithin = activeShape.TextFrame.TextRange.ParagraphFormat.SpaceWithin
.TextRange.ParagraphFormat.Alignment = activeShape.TextFrame.TextRange.ParagraphFormat.Alignment
.TextRange.ParagraphFormat.SpaceBefore = activeShape.TextFrame.TextRange.ParagraphFormat.SpaceBefore
.TextRange.ParagraphFormat.SpaceAfter = activeShape.TextFrame.TextRange.ParagraphFormat.SpaceAfter
.TextRange.ParagraphFormat.BaseLineAlignment = activeShape.TextFrame.TextRange.ParagraphFormat.BaseLineAlignment
.TextRange.Text = activeShape.TextFrame.TextRange.Text
End With
With newPlaceHolder.TextFrame2
.TextRange.Font.Spacing = activeShape.TextFrame2.TextRange.Font.Spacing
End With
newPlaceHolder.ZOrder msoSendToBack
newPlaceHolder.Select
End Sub
Any improvements are welcome too.

Related

VBA PowerPoint Slides set custom layout to refresh the layout

I have created a script processing many slides and at the end, some slides seem to have glitches in their layout. For example, slide numbers have moved on some slides but not on others. It can be fixed manually by re-assigned the custom layout to the slide.
How can I do this automatically?
I could just loop over all slides, find out it's custom layout and re-assign it. But how? This code seems to loop infinitely:
Dim sld As Slide
Dim layoutName As String
Dim layoutIndex As Integer
Set sld = Application.ActiveWindow.View.Slide
layoutName = sld.CustomLayout.Name
layoutIndex = getLayoutIndexByName(layoutName)
ActivePresentation.Slides(y).CustomLayout = ActivePresentation.Designs(y).SlideMaster.CustomLayouts(layoutIndex)
Function getLayoutIndexByName(xName As String) As Integer
ActivePresentation.Designs(1).SlideMaster.CustomLayouts.Item (1)
With ActivePresentation.Designs(1).SlideMaster.CustomLayouts
For i = 1 To .Count
Debug.Print ("inLoop Name: " + .Item(i).Name)
If .Item(i).Name = xName Then
getLayoutIndexByName = i
Exit Function
End If
Next
End With
End Function
To simply reapply the layout already assigned, you only need this:
ActivePresentation.Slides(y).CustomLayout = ActivePresentation.Slides(y).CustomLayout
Occasionally, that command doesn't work, then this workaround is worth a try:
DoEvents
Application.CommandBars.ExecuteMso ("SlideReset")
DoEvents
To apply a new layout, then you need to use something like this code, which is pretty similar to yours:
ActivePresentation.Slides(y).CustomLayout = ActivePresentation.Designs(1).SlideMaster.CustomLayouts(GetLayoutIndexFromName("Text Page", ActivePresentation.Designs(1)))
My version of GetLayoutIndexFromName:
Function GetLayoutIndexFromName(sLayoutName As String, oDes As Design) As Long
Dim x As Long
For x = 1 To oDes.SlideMaster.CustomLayouts.Count
If oDes.SlideMaster.CustomLayouts(x).Name = sLayoutName Then
GetLayoutIndexFromName = x
Exit Function
End If
Next
End Function

VBA in MS Visio - highlighting connectors of selected shape

After selecting a shape (f.e. square or more squares) all the connectors glued to this shape would highlight red, yellow whatever.
The found code below is not working for me, any advice? (I am not coder, so please have patience with me)
Set shpAtEnd = cnx(1).ToSheet
' use HitTest to determine whether Begin end of connector
' is outside shpAtEnd
x = shpAtEnd.HitTest(shpTaskLink.Cells("BeginX"), _
shpTaskLink.Cells("BeginY"), 0.01)
If x = visHitOutside Then
Selection.ShapeRange.Fill.ForeColor.SchemeColor = 2
Else
' do other stuff
End If
This is my first answer on stackoverflow and I hope the following VBA code can solve your problem on how to highlight connectors or connected shapes in Visio!
Public Sub HighlightConnectedShapes()
Dim vsoShape As Visio.Shape
Dim connectedShapeIDs() As Long
Dim connectorIDs() As Long
Dim intCount As Integer
' Highlight the selected shape
Set vsoShape = ActiveWindow.Selection(1)
vsoShape.CellsU("Fillforegnd").FormulaU = "RGB(146, 212, 0)"
vsoShape.Cells("LineColor").FormulaU = "RGB(168,0,0)"
vsoShape.Cells("LineWeight").Formula = "2.5 pt"
' Highlight connectors from/to the selected shape
connectorIDs = vsoShape.GluedShapes _
(visGluedShapesAll1D, "")
For intCount = 0 To UBound(connectorIDs)
ActivePage.Shapes.ItemFromID(connectorIDs(intCount)).Cells("LineColor").FormulaU = "RGB(168,0,0)"
ActivePage.Shapes.ItemFromID(connectorIDs(intCount)).Cells("LineWeight").Formula = "2.5 pt"
Next
' Highlight shapes that are connected to the selected shape
connectedShapeIDs = vsoShape.connectedShapes(visConnectedShapesAllNodes, "")
For intCount = 0 To UBound(connectedShapeIDs)
ActivePage.Shapes.ItemFromID(connectedShapeIDs(intCount)).Cells("LineColor").FormulaU = "RGB(168,0,0)"
ActivePage.Shapes.ItemFromID(connectedShapeIDs(intCount)).Cells("LineWeight").Formula = "2.5 pt"
Next
End Sub
To run the macro, you can consider associating with double-click behavior of shapes.
If you only need to highlight incoming/outgoing connectors and incoming/outgoing shapes, replace visGluedShapesAll1D with visGluedShapesIncoming1D/visGluedShapesOutgoing1D and visConnectedShapesAllNodes with visConnectedShapesIncomingNodes/visConnectedShapesOutgoingNodes.
Learn more at visgluedshapesflags and visconnectedshapesflags. Good luck!
The following code will loop though all 1d-Shapes glued to the first shape in your Selection and write their name to the Immediate window. This should be a good starting point.
Visio has no Event that fires if a Shape is selected (at least not without some workarounds), so maybe bind the macro to a keybind.
The visGluedShapesAll1D flag can be replace with another filter as described here: Microsoft Office Reference
Sub colorConnectors()
If ActiveWindow.Selection(1) Is Nothing Then Exit Sub
Dim selectedShape As Shape
Set selectedShape = ActiveWindow.Selection(1)
Dim pg As Page
Set pg = ActivePage
Dim gluedConnectorID As Variant 'variant is needed because of "For Each" Loop
For Each gluedConnectorID In selectedShape.GluedShapes(visGluedShapesAll1D, "")
Debug.Print pg.Shapes.ItemFromID(gluedConnectorID).NameU
Next gluedConnectorID
End Sub

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

Is there a way to add a callout label to a point in a chart, without using select?

Is there a way to add a callout label to a point in a chart, without using Select?
Recording a macro, I got this:
Sub Macro9()
ActiveSheet.ChartObjects("SPC").Activate
ActiveChart.FullSeriesCollection(1).Select
ActiveChart.FullSeriesCollection(1).Points(4).Select
ActiveChart.SetElement (msoElementDataLabelCallout)
End Sub
But I would rather like to avoid using Select. I tried simply using the SetElement-method on the point, but that failed. Using the HasDataLabel = True-method simply adds a datalabel.
Is there any workarounds to selecting the point and then using SetElement on the chart, or will I have to settle for something resembling the above macro?
Is this what you are trying? In the below code we have avoided .Activate/.Select completely :)
Feel free to play with .AutoShapeType property. You can also format the data label to show the values in whatever format you want.
Sub Sample()
Dim objC As ChartObject, chrt As Chart, dl As DataLabel
Dim p As Point
Set objC = Sheet1.ChartObjects(1)
Set chrt = objC.Chart
Set p = chrt.FullSeriesCollection(1).Points(4)
p.HasDataLabel = True
Set dl = p.DataLabel
With dl
.Position = xlLabelPositionOutsideEnd
.Format.AutoShapeType = msoShapeRectangularCallout
.Format.Line.Visible = msoTrue
End With
End Sub
Screenshot
As I said in a comment: I couldn't find a way to do this directly but thought I'd be able to work around it.
Turns out I was unsuccessful!
But let's cover an edge case which for some uses will have a pretty easy solution; say you don't need datalabels except for the instances where you want callout:
Sub chartTest()
Dim co As ChartObject
Dim ch As Chart
Dim i As Integer
' The point index we want shown
i = 2
Set co = Worksheets(1).ChartObjects(2)
Set ch = co.Chart
co.Activate
ch.SetElement (msoElementDataLabelCallout)
For j = 1 To s.Points.Count
' We can change this to an array check if we want several
' but not all points to have callout
If j <> i Then s.Points(j).HasDataLabel = False
Next j
End Sub
For anyone desperate, the closest I came was to create an overlay using the original chart as a template. It doesn't work accurately for arbitrary charts, however, due to positioning issues with the callout box.
But at this point, you might as well have just added a textbox or something far less involved than copying a chart, deleting half its contents and making the rest of it invisible...
But for the sake of Cthul-- I mean, science:
Sub pTest()
Dim co As ChartObject
Dim ch As Chart
Dim s As Series
Dim p As Point
Set co = Worksheets(1).ChartObjects(1)
Set ch = co.Chart
Set s = ch.SeriesCollection(1)
i = 2
Call copyChartTest(co, ch, i)
End Sub
Sub copyChartTest(ByRef co As ChartObject, ByRef cht As Chart, ByVal i As Integer)
Dim ch As Chart ' The overlay chart
Set ch = co.Duplicate.Chart
' Set callout
ch.SetElement (msoElementDataLabelCallout)
' Invisibil-ate!
With ch
.ChartArea.Fill.Visible = msoFalse
.SeriesCollection(1).Format.Line.Visible = False
.ChartTitle.Delete
.Legend.Delete
For j = 1 To .SeriesCollection(1).Points.Count
.SeriesCollection(1).Points(j).Format.Fill.Visible = msoFalse
If j <> i Then .SeriesCollection(1).Points(j).HasDataLabel = False
Next j
End With
' Align the charts
With ch
.Parent.Top = cht.Parent.Top
.Parent.Left = cht.Parent.Left
End With
End Sub
And the result: DataLabels intact with only 1 point having callout.
Have you tried this free tool http://www.appspro.com/Utilities/ChartLabeler.htm by Rob Bovey?
There is an option "manual label" which seems to be very close to what you want. I am using the version of 1996-97 which has visible VBA code. I have not checked if the latest version has.
try the below code
Sub Macro9()
ActiveSheet.ChartObjects("SPC").Activate
ActiveChart.SeriesCollection(1).Points(4).HasDataLabel = True
ActiveChart.SeriesCollection(1).Points(4).DataLabel.Text = "Point 4 Test"
End Sub

How to fill the shape with color under a given condition

Im new to Powerpoint VBA which is totally different from excel VBA. I can do it in excel but not powerpoint and need some help. I need to enter a score in the textbox. After that press a button, the shape will fill with color based on the score value. The higher the score, more shape will fill up. Below is my code:
Sub AddShape()
Dim counter As Integer
Dim TopValue As Integer
TopValue = 500
For counter = 1 To 5
Set myDocument = ActivePresentation.Slides(2)
With myDocument.Shapes.AddShape(Type:=msoShapeRectangle, Left:=144, _
Top:=TopValue, Width:=72, Height:=5)
.Name = "Rectangle" & counter
.Fill.Visible = msoFalse
.Line.DashStyle = msoLineSolid
End With
TopValue = TopValue - 50
Next counter
Dim tshape As Shape
Set tshape = ActiveWindow.Selection.SlideRange.Shapes.AddOLEObject(Left:=850, Top:=100,
Width:=90, Height:=40, ClassName:="Forms.TextBox.1", Link:=msoFalse)
End Sub
Private Sub CommandButton1_Click()
If CInt(TextBox1.Text) > 0 And CInt(TextBox1.Text) < 11 Then
ActivePresentation.Slides(2).Shapes("Rectangle1").Fill.ForeColor.RGB = RGB(255, 0, 0)
End If
End Sub
Private Sub TextBox1_Change()
Me.TextBox1.SpecialEffect = fmSpecialEffectFlat
End Sub
The code inside the Private Sub CommandButton1_Click doesn't seen to work... Please advise. I got prompt running error '424' object required. Specific code is appreciated as all the above code is direct copy from google search. I do not have much vba powerpoint knowledge.
Thank you very much.
You need to access OLE objects and their properties a bit differently from the way you work with regular shapes on slides.
Instead of TextBox1.Text, use
ActivePresentation.Slides(2).Shapes("TextBox1").OLEFormat.Object.Text