Does anyone know how to ungroup SmartArt element via VBA?
Sub UngroupSmartArt()
Dim shapeWithSmartArt As Shape
Set shapeWithSmartArt = ActivePresentation.Slides(2).Shapes(2)
shapeWithSmartArt.Ungroup
End Sub
I get an error for this code:
"This member can only be accessed for a group."
It doesn't make any sense to me, because it's easily possible to do it in powerpoint itself (Right click on SmartArt -> Group -> Ungroup). It's driving me nuts :)
Can anyone help me with ungrouping SmartArt element/shape?
I also took a look on similar question, but it doesn't work properly, because ungrouped result is different in comparison to the one made via powerpoint itself.
Please help me out. I would really appreciate any help!
It is simply impossible to do it via VB code. That is also statement from Microsoft. Disadvantage of using SmartArt is also that user cannot record any actions with macro (using Excel) which are performed on this type of object/element.
It is also impossible to change width or height property of SmartArt nodes via VB, this was actually the reason why I wanted to change SmartArt element to shapes, because you can easily change width and height property of the shape via code.
Microsoft and their developers should really consider to make SmarArt element more developer-friendly, because I noticed I'm not the only one with these issues.
EDIT: Solution found! There is a way to execute commands from the powerpoint ribbon. You need to select your shape first, afterwards execute CommandBars.ExecuteMso with the action: SmartArtConvertToShapes.
Sub UngroupSmartArt()
Dim shapeWithSmartArt As Shape
Set shapeWithSmartArt = ActivePresentation.Slides(2).Shapes(2)
shapeWithSmartArt.Select
CommandBars.ExecuteMso("SmartArtConvertToShapes")
End Sub
This still doesn't change the fact and my point of view: Microsoft should really consider to make SmartArt element more friendly to developers!
Related
i'am currently trying to add a small function to PowerPoint using VBA, my goal is to create a gadget that works kind of like Photoshop graphics layer.
My plan is to add a layer name Tag on each shape that is drawn by user, so later I can parse through every item by loop and preform lock/unlock, show/hide shapes based on it's tag value, such as:
Sub add_shape_with_layer_tag()
Set islide = ActivePresentation.Slides(1)
Set ishape = islide.Shapes.AddShape(msoShapeRectangle, 5, 5, 80, 60)
ishape.Tags.Add "Layer", "1"
End Sub
Sub show_hide_layer_one_shapes()
Dim active_slide As Slide
Set active_slide = ActiveWindow.View.Slide
For Each ishape In active_slide.Shapes
If ishape.Tags("Layer") = "1" Then
ishape.Visible = Not (ishape.Visible)
End If
Next ishape
End Sub
However, I couldn't found a way to achieve this, so I would like to ask whether there's a method that provides following functions?
override add shape function so I can sneak tag value in to shape every time user drawn a shape
catch add shape event (if this thing did exist) so i can add tag to the last added item
a way to set a default tag value to shape, just like setting default shape color/line width
or is there any better options that is also viable?
thanks.
20200728
To John, thanks for the advice, I did found some interesting event that might be helpful for some of my other projects, however I couldn't found events that able to trigger after custom function while shape is added.
To Steve, my plan is to add a modeless userform with list UI that manage layers, the shape tag and shape fill texture/color will be determined based on what list item that currently selected.
As to saving settings, I'll use VBComponents.CodeModule to dump existing settings in userforms to a VBA module and store as text, so in theory I should able to make this function self contained in one file.
Not totally an answer, sorry SO, but comments don't allow enough scope for this. So ...
To Steve, my plan is to add a modeless userform with list UI that manage layers, the shape tag and shape fill texture/color will be determined based on what list item that currently selected.
Ah, so you're creating your own "pseudo-layers". That wasn't clear. Thanks for the add'l info. As it happens, I have a selection manager add-in that works very similarly to what you're proposing.
To John, thanks for the advice, I did found some interesting event that might be helpful for some of my other projects, however I couldn't found events that able to trigger after custom function while shape is added.
The SelectionChange event should get you there. When it fires, you'll need to check first to see if the current selection is a shape or something else. If a shape, check to see if its .Index = current slide's .Shapes.Count and also to see if you've already tagged it. If no tag AND if it's the correct index, it'll be a newly added shape. If you're tagging ALL shapes, you may only need to check to see if the .Tag(name) is blank.
As to saving settings, I'll use VBComponents.CodeModule to dump existing settings in userforms to a VBA module and store as text, so in theory I should able to make this function self contained in one file.
Why not save any needed slide or presentation level info as further slide- or presentation-level tags? PPT can absorb quite a lot of info as tags w/o getting cranky.
I am new to VBA in PowerPoint and I am trying to develop an interactive program for my class where a text box grouped with a bubble shape floats across the screen and they have to click it if they notice it contains a certain grapheme. I want the clicked text boxes to copy to another slide so that I can assess if the children have been successful or just clicked anything they saw.
I've pieced together this code from other sites:
Sub copyobject()
With ActivePresentation
Set myshape1 = ActivePresentation.Slides(2).Shapes(group5)
myshape1.ActionSettings(ppMouseClick).Action = myshape1.Copy
.Slides(3).myshape1.Paste
End With
End Sub
It doesn't appear to be working though. Can anybody help me out?
The error message I receive is 'compile error: method or data member not found' with .myshape1 highlighted.
If anybody could give me a completed code for this or correct what I have created I would greatly appreciate it.
Many thanks
Since the name of the object is "group5", I assume it's a group. But you can't apply an Action Setting to a group, only to a single object. This works for a single shape:
Sub copyobject()
With ActivePresentation
Set myshape1 = .Slides(2).Shapes("Rectangle 1")
myshape1.ActionSettings(ppMouseClick).Action = myshape1.Copy
.Slides(3).Shapes.Paste
End With
End Sub
BTW, an excellent VBA book for what you're doing is David Markovitz's Powerful PowerPoint for Educators. His support site for the book also has lots of free code and samples.
I am new to VBA and trying to use smartArt.Nodes to dynamically generate an organizational chart based on some cell data. I am able to generate the chart with no problems. Now, I would like to be able to display more details of specific nodes of the chart by clicking on them. I am aware that we can convert smartArts into Shapes with the Drawing Tools in Excel and then use .onAction on them like:
ActiveSheet.Shapes(name1).OnAction = "detail"
However, is there a way to achieve the same thing with smartArt.nodes using VBA? Say I have a node called "nodes1" I have tried:
nodes1.Shapes.OnAction = "detail"
or
Dim objShape As Shape
Dim SmartArtNod As SmartArtNode
Set objShape = ActiveSheet.Shapes(1)
Set SmartArtNod = objShape.SmartArt.AllNodes(1)
SmartArtNod.Shapes(1).OnAction = "detail"
and other combinations, but none of them seem to work...
Thank you for your help!
Hard to prove a negative, but I believe this is not possible. Testing with Excel 2013, I can confirm the following do not work:
Trying to trap the Selection of the SmartArt using
Worksheet_SelectionChanged does not fire. If you change the
selection from a cell to the SmartArt object, you will not get an
event. You will get an event however when you lose focus on the
SmartArt and go back to the cell.
You can select a SmartArt object and then debug to check what is selected. The object is of type
Object/Shape and the watch window indicates that it exposes an
OnAction property, but attempting to set this property will throw
an error.
I take the two issues above to mean that is will be very difficult (if not impossible) to get an OnAction event to fire from the SmartArt.
I have a form in Access with over 500 shapes that each represent a specific record. Each shape has the control name of Box1, Box2 etc. In VBA I am trying to capture the shape name so if the user clicks the shape called Box502 for example it will go to record 502. Since it is a shape, the other methods I have used are not capturing this control name. If I can capture the name, I can remove “Box” from it and get the number to use to go to that record.
If I use a command button instead of a shape I can use the code below and get it to do what I want, but there is also code that changes the color of the shape so that does not work on the command button.
Private Sub Box2_Click()
Dim strActiveCtl As String
strActiveCtl = Replace(Me.ActiveControl.Name, "Box", "")
DoCmd.GoToRecord acDataForm, "Form1", acGoTo, [strActiveCtl]
End Sub
If I use this line of code on the shape, it is grabbing another control and not what I had clicked on.
On click I was using the code builder and this line of code, but it is capturing another control and not what I am clicking on.
Dim strActiveCtl As String
strActiveCtl = Screen.ActiveControl.Name
The reason there are these little boxes are because they basically relate to a service that is monitored. There are about 550 services monitored. When the service has been noted as reviewed the shape goes from red to green. The team wanted to be able to just click on the shape as well and have it take to them to the service it was associated with.
I can't for the life of me envision a Form with over 500 shape boxes that each represent a different record. Egads.
But maybe you have a unique reason for doing this. So I won't question this design philosophy other than to say most people use "sub-forms" and "list boxes" when trying to identify individual records like you are doing. You may be able to save yourself lots of headaches by reading up on how to design forms using those methods. (As well as many other methods that avoid putting over 500 shape boxes on your form.)
However, if this is a design that you have to adhere to... there are a few things you need to know about Access and the way it resolves the active control as well as what happens when a control is clicked on.
Whenever you click on a control or shape, if you have the control properties set so it can accept "Focus", Access instantly makes that the "active control".
So if you then go to click on something else... your shape will lose it's status of being the "active control" and the new thing will now be the new "active control".
So there is no way to click on a shape and then click on something else to launch your code that you want to have performed on the previous "active control". Access doesn't keep a memory of past "active controls".
So this brings up the question of: "What can you do to figure out if a control or shape was clicked on?"
Luckily (or unluckily) for you, when a shape is clicked on, it checks if code has been written for it's "On Click" property.
Each and every shape you put on the Form will have it's very own "On Click" property. Which means you can write a function that you can place in every single shape you create. You can pass to that function the number of which "Box" it was that called it.
This is obviously very cumbersome. (Which is why I'd suggest using "sub-forms" or "list boxes".) But it's the only way you are going to capture the Box number that is different for each of the 500 boxes since there is no way to launch other code without losing the "Focus" on which box you were on.
So if you want to continue with your "500 shapes on one Form each addressing a different record"... you could write a VBA function that calls your record with an input parameter that tells what "Box" called it. Like this:
Private Sub subGoToNewRecord(intActiveCtl As Integer)
DoCmd.GoToRecord acDataForm, "Form1", acGoTo, intActiveCtl
End Sub
Then for each Shape Box you can write:
Private Sub Box1_Click()
subGoToNewRecord 1
End Sub
Private Sub Box2_Click()
subGoToNewRecord 2
End Sub
.
.
.
Private Sub Box502_Click()
subGoToNewRecord 502
End Sub
This will get you what you want. (Although, some shapes require you to click on the outline of the shape in order make it the "active control". So you may need to make your shape boundary thick and tell users to click on the boundary line.)
Once again, I have no idea why you'd want to do this. I'd suggest again to look into what "sub-forms" are and to look into what "List Boxes" are. They are much better suited to helping you select a desired record and then make that the "Active Record".
Hope that helps. :)
Well I was wondering whether we can somehow create custom looking text boxes that act as an input box and is linked to VBA.
As far as I am aware the standard procedure would entail adding an ActiveX Textbox Control and then using the TextBox1_Change event to add the code as to what needs to happen when the user enters something in to.
Sadly the look of the default textbox isn't matching the way I want by spreadsheet to look. So is there any way to change how it looks or have something replace it while serving the same purpose?
One thing I could think of and have tried is inserting a shape (blue):
Shape http://im52.gulfup.com/qD2F0B.png
I can get the text that is in the shape using VBA by:
InputText = Shapes("Rounded Rectangle 1").TextFrame.Characters.Text
But I don't suppose there is a way to detect a change of shape text event?
Suggestions / Workarounds are welcome!
Thanks
There are limitations on what you can change on an ActiveX TextBox, such as Font/Color/Border/SpecialEffects, but the basic rectangle shape cannot be changed.
However you can make the TextBox transparent by BackStyle property and group it to a shape (bring the TB forward) and still use the TextBox1_Change method for changes.
If you need to access the value in the TextBox somewhere else, a quick way is to use TextBox1.LinkedCell and below to set the value to a cell, or a Named Range.
Private Sub TextBox1_Change()
' Same Sheet as TextBox1
ActiveSheet.Range(TextBox1.LinkedCell).Value = TextBox1.Value
' Or Below for Named Range
ThisWorkbook.Names(TextBox1.LinkedCell).RefersToRange.Value = TextBox1.Value
End Sub