Creating shapes in similar fashion as with autoshape dropdown - vba

I would like to make a macro in Powerpoint that enables me to create shapes in a similar fashion as when you select the autoshapes in the autoshape overview (i.e. once you call the macro you have a possibility to click to set the coordinates and subsequently you drag and click to set the width&height). Also, I would like to give it pre-set cosmetic characteristics (e.g. certain inner margins, fill color, border style and transparancy), which will be defined in the vba code.
I am aware of .addshapes(), however, this requires coordinates and height/width as input. Moreover, I have not find any posts / documents on vba to create shapes without defined coordinates and height/width.
Anyone some ideas on how to tackle this challenge?
Many thanks in advance!
Sofar

Building on what John Korchok suggested, here's code that retrieves the just-drawn shape so that your code can resume and manipulate it...
Sub testAppComBars()
Dim SHP As Shape
Application.CommandBars.ExecuteMso ("ShapeFreeform")
Stop
Set SHP = Selection.ShapeRange(1)
With SHP.Fill
.ForeColor.RGB = RGB(192, 0, 0)
.Transparency = 0.75
End With
End Sub
I would hope there's a more elegant solution than using Stop to pause code execution while the user picks the shape's location (or in this case, draws a freeform polyline/polygon), but that's all I could come up with off the top of my head.

I was fascinated by this problem and think this might help you.
Consider that when you draw a new autoshape, you have changed the window selection, and created a new selection ShapeRange with exactly 1 item (the new shape).
So by setting a WindowSelectionChange event, you're able to apply any formatting you wish at the time of creation.
First create a class module called cPptEvents with the following:
Public WithEvents PPTEvent As Application
Private Sub PPTEvent_WindowSelectionChange(ByVal sel As Selection)
On Error GoTo Errhandler
Debug.Print "IN_PPTEvent_WindowSelectionChange"
Dim oShp As Shape
If (ActiveWindow.ViewType = ppViewNormal) Then
With sel
If .Type = ppSelectionShapes Then
If .ShapeRange.Count = 1 Then
Set oShp = .ShapeRange(1)
If oShp.Type = msoAutoShape Then
If oShp.AutoShapeType = msoShapeOval Then
If oShp.Tags("new_oval") = "" Then
oShp.Fill.ForeColor.RGB = RGB(255, 0, 0)
oShp.Tags.Add "new_oval", "true"
End If
End If
End If
End If
End If
End With
End If
Exit Sub
Errhandler:
Debug.Print "Error: " & Err.Description
End Sub
This checks the selection every time it changes. If there's an oval selected, it looks for the "new_oval" tag, which will not exist for a newly created shape. In that case, it applies a red fill, although of course once you get to this point you can call an entirely different sub, pass along the shape, and do whatever you want formatting-wise to it.
By adding that "new_oval" tag, you ensure that the formatting will not be applied to an oval that hasn't been newly created. This allows the user to make manual changes to the formatting as needed -- otherwise you're just resetting the formatting every time the user selects an oval.
Note that for the _WindowSelectionChange event to be running in the background, you have to call this at some point:
Public MyEventClassModule As New cPptEvents
'
Public Sub StartMeUp()
Set MyEventClassModule.PPTEvent = Application
End Sub
You can include that one line from StartMeUp above in whatever Ribbon_Onload sub is triggered by your addin, if you're making a new addin ribbon.
With this solution, you don't even have to give the end user a special button or set of tools to create the shapes that are being formatted. It happens invisibly whenever the user draws a new shape from the native PPT tools.

This will put your cursor in drawing mode to draw an oval. After running, you may have to click on the slide once, then the cursor will change shape and you can draw an oval:
Sub DrawOval()
Application.CommandBars.ExecuteMso ("ShapeOval")
End Sub
Other commands to substitute for ShapeOval:
ShapeRectangle
ShapeElbowConnectorArrow
ShapeStraightConnectorArrow
Get the full list in Excel spreadsheets from Microsoft Office 2016 Help Files: Office Fluent User Interface Control Identifiers
Look for the powerpointcontrols.xlsx file and search the first column with "shape"
There are 173 shapes in the menu, so you have a lot of macros to write.

Related

Bring forward an object by clicking a button in Powerpoint

I have a PPT presentation with some slides. In each slide I have some superposed images and some buttons (see example image).
I would like to "bring to front" an image when the corresponding button is clicked.
I have been triying with animations, but unfortunately there is no animation to run this action.
After some 'investigation' I think that the only way to achieve that is by a macro.
I have absolutely no experience in VBA programming, but after some search I found out the way to 'bring to front' an image of the active slide by doing:
Sub Bring_front()
Dim sld As Slide
Set sld = Application.ActiveWindow.View.Slide
sld.Shapes("NUCLEI").ZOrder msoBringToFront
End Sub`
Then I insert an action (to execute the macro) to the correspoding button and everything works fine!
The problem is that in my real presentation I have many images and buttons (like 10 for slide)... and I would like to create a macro with if/else statements in order to run an statement if a button is clicked.
I have also been searching how to do that but I have not been able to get it.
Is it possible to do something like that?:
Sub Bring_front()
Dim sld As Slide
Set sld = Application.ActiveWindow.View.Slide
if Greenbutton is clicked Then
sld.Shapes("GREEN_IMAGE").ZOrder msoBringToFront
elseif Redbutton is clicked Then
sld.Shapes("RED_IMAGE").ZOrder msoBringToFront
elseif Bluebutton is clicked Then
sld.Shapes("BLUE_IMAGE").ZOrder msoBringToFront
end
End Sub
Can someone help me with this, please?
Thank you in advance!
Maria
It's actually quite simple:
Add a new module in the VBA editor and paste this into it:
Sub MoveToTop(oSh As Shape)
Call oSh.ZOrder(msoBringToFront)
End Sub
Then assign this MoveToTop macro as an action setting on each of the shapes you want to be able to adjust.
That's it.
Well, almost. If you're on a Mac and find that this doesn't work, it's because some things in the Mac version of PPT are broken. In that case, try this, which should work ok in Windows versions of PPT also:
Sub MoveToTop(oSh as Shape)
Dim oSl as Slide
Dim oShTemp as Shape
' oSh.Parent returns a valid reference to the shape's host slide:
Set oSl = ActivePresentation.Slides(oSh.Parent.SlideIndex)
' and oSh.Name works:
MsgBox oSh.Name
' So we use those two bits to get a reference
' to the clicked shape like so
Set oShTemp = oSl.Shapes(oSh.Name)
Call oShTemp.ZOrder(msoBringToFront)
End Sub

Changing colour of text segments in a powerpoint presentation

I have a Powerpoint-Slide with pasted, formatted source code in the form of text shapes. Sadly the contrast of some part of that text is bad on a projector, so I would like to change every colour occurence for a specific font with a different colour. In this specific example I want to replace the orange colour:
Iterating over all shapes and accessing the whole text of a shape is not a problem, but I can't find any property that allows me to enumerate over the styled text segments:
Sub ChangeSourceColours()
For Each pptSlide In Application.ActivePresentation.Slides
For Each pptShape In pptSlide.Shapes
If (pptShape.Type = 1) And (pptShape.TextFrame.TextRange.Font.Name = "Consolas") Then
' Iterate over styled segments and change them if the previous colour is orangey
MsgBox pptShape.TextFrame.TextRange
End If
Next
Next
End Sub
The TextRange2 property looked helpful at a first glance, but looking at the variables in the debugger I see nothing that looks like a series of formatted segments. I would expect to find something like <span> in HTML to check and possibly change the colour.
The textFrame2.textRange.Font is valid for the whole text. If you want to access the single characters and their individual formatting, you need to access textRange.Characters.
The following routine changes the text color for all characters that have a specific color to a new color:
Sub ChangeTextColor(sh As Shape, fromColor As Long, toColor As Long)
Dim i As Long
With sh.TextFrame2.TextRange
For i = 1 To .Characters.Length
If .Characters(i).Font.Fill.ForeColor.RGB = fromColor Then
.Characters(i).Font.Fill.ForeColor.RGB = toColor
End If
Next i
End With
End Sub
You call it from your code with
Dim pptSlide as Slide
For Each pptSlide In Application.ActivePresentation.Slides
Dim pptShape As Shape
For Each pptShape In pptSlide.Shapes
If (pptShape.Type = 1) And (pptShape.TextFrame.TextRange.Font.Name = "Consolas") Then
ChangeTextColor pptShape, RGB(255, 192, 0), vbRed
End If
Next
Next
You will have to adapt the RGB-Code to the orange you are using, or instead of using RGB, you can use ObjectThemeColor. To avoid a type mismatch, you need to declare the pptShape variable as Shape - you should declare all your variables and use Option Explicit anyhow.
Note that you can use the ChangeTextColor-routine also in Excel (and probably also in Word). Shapes are defined similar in Excel and Powerpoint. Advantage in Excel: You can use the macro recorder to get some insights how a Shape can be used in Office VBA.

Unhighlighting text (and preserve all other font settings)

Thanks to 2 posts (here and here), I know how to highlight text of a textbox in PowerPoint with VBA code.
However, the problem of unhighlighting text remains unsolved. I tried to set properties of a non-highlighted textbox to TextRange2.Font (e.g. .TextFrame2.TextRange.Font.Highlight.SchemeColor = -2) but receive errors when trying so (The typed value is out of range).
Can someone help to solve this issue, please?
Additionally, when changing the highlight color
(e.g. TextRange2.Font.Highlight.RGB = RGB(255, 255, 175)) the formatting of my textbox changes, so the font is changing its color from my preset white to black and the font size gets smaller. Is there any way to preserve the original settings for the textbox? Is this happening due to the access of .TextRange2 and not .TextRange?
Thanks for your help!
In PowerPoint 2019/365 it is possible to remove highlight by using built-in Mso "TextHighlightColorPickerLicensed".
This code sample illustrates how to unhighlight text in selected shapes. It finds Runs containing highlighting, selects them and removes highlight by programmatically invoking Command Bar "Highlight" button.
Preconditions: PowerPoint 2019 or 365. Presentation must be opened with window.
Option Explicit
Sub UnhighlightTextInSelectedShape()
Dim sh As Shape
For Each sh In ActiveWindow.Selection.ShapeRange
UnhighlightTextInShape sh
Next
End Sub
Sub UnhighlightTextInShape(sh As Shape)
On Error GoTo Finish
Dim highlightIsRemoved As Boolean
Dim tf As TextFrame2
Set tf = sh.TextFrame2
Do
Dim r As TextRange2
highlightIsRemoved = True
For Each r In tf.TextRange.Runs
If r.Font.Highlight.Type <> msoColorTypeMixed Then
' Indicate that text contains highlighting
highlightIsRemoved = False
' The text to un-highlight must be selected
r.Select
If Application.CommandBars.GetEnabledMso("TextHighlightColorPickerLicensed") Then
' This Mso toggles highlighting on selected text.
' That is why selection must contain highlight of the same type
Application.CommandBars.ExecuteMso ("TextHighlightColorPickerLicensed")
' Unhighlighting May invalidate number of runs, so exit this loop
Exit For
Else
Exit Do
End If
End If
Next
Loop Until highlightIsRemoved
Finish:
If Not highlightIsRemoved Then
MsgBox "Unhighlighting is not supported"
End If
End Sub
Sometimes Application.CommandBars.ExecuteMso() method gives access to features not available via PowerPoint API.
The MsoId is displayed in tooltip text in PowerPoint options window:

_Click() event not firing sub VBA Excel

I have added a rectangle shape to a spreadsheet. I now wish to use a macro on event RectangleA_Click(). I know the shape's name is RectangleA as I have the below sub:
Sub f()
Dim Shape As Shape
For Each Shape In ActiveSheet.Shapes
Debug.Print Shape.Name
Next
End Sub
I have written the code:
Private Sub RectangleA_Click()
MsgBox "hello"
End Sub
In the relevant sheet object space in the editor. On clicking the shape the subroutine is not called.
I am aware that I can assign this using right click> assign macro, but would prefer to avoid this as it seems like an unnecessary step. I'm also aware that there are Active X controls which automatically assign the macro, again I would prefer to avoid this if possible.
Something like this may work:
Sub AssignMacroToShape()
Set shpTemp = ActiveSheet.Shapes.AddShape(msoShapeRectangle, 1680#, 594#, 120#, 74.25)
shpTemp.OnAction = ActiveWorkbook.Name & "!Macro Name"
End Sub
But it will be much easier just to right click > Assign Macro.
This above code is useful if you need to create the shape then assign a macro to it after creation.
You can look into the OnAction event to get the answer to specifics if this doesn't help.

VBA: Code not running after ToggleFormsDesign

I have the following code in VBA (MS Word), that is meant to run after I click in a button, named cmdFormPreencher inserted in my Document:
Private Sub cmdFormPreencher_Click()
'
If ActiveDocument.FormsDesign = False Then
ActiveDocument.ToggleFormsDesign
End If
'
ThisDocument.cmdFormPreencher.Select
ThisDocument.cmdFormPreencher.Delete
ActiveDocument.ToggleFormsDesign
'
UserForm2.Show
End Sub
The purpose of the code above is to delete that button inserted in my document.
But when I run the code only the button is selected. When I tried to figure out what is happening by debugging, it showed me the code runs until ActiveDocument.ToggleFormsDesign and not running the code remaining
Is this a bug of VBA, or am I doing something wrong? If so, how can I get around this problem?
Thanks!
Note: The ActiveX button is not in Header and Footer. The Text Wrap is set to In Front of Text
Edit:
When I try to run a macro, activating FormDesign, Selecting the ActiveX button and then deleting, I get this code:
Sub Macro1()
'
' Macro1 Macro
'
'
ActiveDocument.ToggleFormsDesign
ActiveDocument.Shapes("Control 52").Select
Selection.ShapeRange.Delete
ActiveDocument.ToggleFormsDesign
End Sub
But when I run this code nothing happens...
This is by design. When an Office application is in Design Mode code should not run on an ActiveX object that's part of the document.
I take it this is an ActiveX button and in that case, it's a member of the InlineShapes or Shapes collection - Word handles it like a graphic object. It should be enough to delete the graphical representation, which you can do by changing it to display as an icon instead of a button.
For example, for an InlineShape:
Sub DeleteActiveX()
Dim ils As word.InlineShape
Set ils = ActiveDocument.InlineShapes(1)
ils.OLEFormat.DisplayAsIcon = True
ils.Delete
End Sub
You just have to figure out how to identify the InlineShape or Shape. You could bookmark an InlineShape; a Shape has a Name property.
EDIT: Since according to subsequent information provided in Comments you have a Shape object, rather than an InlineShape, the following approach should work:
Dim shp As word.Shape
Set shp = ActiveDocument.Shapes("Shape Name") 'Index value can also be used
shp.Delete
Note that Word will automatically assign something to the Shape.Name property, but in the case of ActiveX controls these names can change for apparently no reason. So if you identify a control using its name instead of the index value it's much better to assign a name yourself, which Word will not change "on a whim".
Activate Design Mode.
Click on the control to select it
Go to the VB Editor window
Ctrl+G to put the focus in the "Immediate Window"
Type the following (substituting the name you want), then press Enter to execute:
Selection.ShapeRange(1).Name = "Name to assign"
Use this Name in the code above