Visio VBA copy/cut/paste intercept - vba

I have another challenge. Has anyone tried to intercept the copy/paste events? My goal is to prevent someone from cut/copy/paste a shape that is already on a sheet. If they drag it from a stencil, that is fine. I just can't have them duplicate an existing shape. To make this a bit harder, it is only the shape that I need to prevent. If the want to copy text, that is fine.
We have a order type database that contains items that need to be dropped on a Visio sheet. I cannot use the shapeID because I need to be able to update the shape from the database and I can't write the ID back to the database as it is against security policy. The way that I am tying the two together is a property named shapeKey and that value is provided by the database at the time the shape is dropped.
When a use needs to refresh the sheet from the database, I interate through the shapes, comparing the shapeKey in the shape and in the database. if there is a match, I do an update of the other properties. If there is no match, I want to delete the shape. if a user copies a shape, the shapeKey will also be duplicated and that causes problems. Lastly, they can add their own shapes from stencils and those shapes must be excluded from the delete process
I have two options:
Disable shape cut/copy/paste
Intercept the copy/cut and when they paste, change a property so that I know that it was a user pasted shape. This is preferred because it is more user friendly
I just thought if another way I that might work. Is there a way to lock the shapes to prevent the copy? If there is a lock, would that also lock the location? The user needs to be able to most the shapes around.
I thought about capturing the event but I could not find the event codes to look for. I cannot install the Visio SDK which has the Event monitor. The monitor might have shown me the code. Here is some pseudo code as to what I think would be the flow.
option 1
if select item is a shape then
msgbox "shape copying verboten. please us the stencil"
clear selected item
option 2
capture the paste event
if selected item is a shape then
vsoShape.Cells("Prop.ShapeKey").Formula = Chr(34) & "protect" & Chr(34)
Layers might work. All the database controlled shapes can be on one layers and all the user shapes on another but I haven't worked with layers before. would that work? How can I be sure that any shape pasted goes onto the user layer?

Thanks all! I did find a solution and it was really elegant. I found the idea here and changed it to what I needed:
Shape added event: https://msdn.microsoft.com/en-us/library/office/ff767288.aspx
Here is what I came up with:
Private Sub Document_ShapeAdded(ByVal vsoShape As Visio.IVShape)
If vsoShape.CellExistsU("Prop.ShapeName", 0) Then
vsoShape.CellsU("Prop.ShapeName").Formula = Chr(34) & "ShapeName" & Chr(34)
End If
End Sub
The interesting thing is that it doesn't fire if I add the shape via VBA. That is perfect for what I need but I would have thought that dropping a shape is adding a shape. I am only looking for one property because not all shapes on the stencils come from a database and for those objects, I don't need to do anything.
It was not the solution that I expected but it works really well.

Related

Visio - How to ungroup shapes but keep the same names?

I'm working on Visio (I didn't to the file) and all the shapes are grouped, there is only one shape by group and when I ungroup all the shapes all the names are modified and replaced by random names like "sheet.8331" , do you know if there is a way to ungroup all the shapes without modifying the names of shapes ? Thank you very much in advance
You could read the names prior to ungrouping (VBA), but it is generally a bad idea to use names.
You better rely on fields in the user section to identify shapes.
eg: user.MyIdentificator = "It's me!"
Retrieving the shape is as easy as:
for each shp in activepage.shapes (or in parent_shape)
if shp.cellexists("user.MyIdentificator", visExistsAnywhere) then
if shp.cells("user.MyIdentificator").ResultStr("") = "It's me!" then
'do your stuff
... closing ifs and loops
HTH,
I suspect GG made a "shape" that was a set of shapes, then dragged them onto a stencil.
You may not need to ungroup, as there is a way to "go inside of the group" and make changes. Many users that are destroying things by ungrouping are unaware of "group-editing windows".
Just right-click any group, then choose Group > Open Group, or Group > Open SomeName.
I'll add some more technical mumbo-jumbo that may help other readers who find their way to this thread.
When the newly-created master is dragged back into a drawing, Visio groups the shapes together. This is to preserve the inheritance that Visio establishes between the local master (stored in the Document Stencil of the drawing file) and each instance of the master on the pages of the document.
The inheritance is a way of saving data---shapes on pages aren't all copies of their original masters, then inherit the properties of masters. Only bits that the user changes, such as location, size, text, etc. are overridden. This inheritance can be a powerful tool, can save on file size, but can also be confusing.
When you ungroup the master, you sever it's link to the master, and the shapes become new copies with no inheritance. Visio has to give them names. I'm not sure why your original names are lost. I would think you would end up with a bunch of names that look like "NameOriginal.3", "NameOriginal.46", etc. That's what happens in my tests.
Anyway, names must be unique "at scope" - meaning at the same level of grouping. So for shapes just sitting on the page, there can only be one "Bob". If you make a copy of "Bob", he'll be "Bob.46" or something.
I can have 7000 groups that each have a subshape named "Bob", since each "Bob" exists in its own unique scope--the group shields one Bob from another. But all of those Bobs will have unique "SheetID" indices. Those can be used for doing cross-sheet formula references in the ShapeSheet.

How do I update the slide master from Slideshow Mode and refresh current slide using VBA?

I'm trying to have a counter in all slides of a powerpoint presentation.
The counter needs be controlled by a button in all slides that increases it.
I can do it for one specific slide, but when I do it with a slide master it doesn't refresh the screen during Slideshow mode. I have to exit slideshow and enter again to see the changes.
Code that doesn't refresh in Slideshow mode:
ctr = ctr + 1
ActivePresentation.Designs(1).SlideMaster.Shapes("Counter").TextFrame2.TextRange.Text = ctr
Code that works (but only applies to one slide, not all slides in presentation):
ctr = ctr + 1
ActivePresentation.Slides(1).Shapes("Counter").TextFrame2.TextRange.Text = ctr
Thanks!
If your second method works, why not loop it?
Dim sld as Slide
ctr = ctr + 1
For Each sld in ActivePresentation.Slides
sld.Shapes("Counter").TextFrame2.TextRange.Text = ctr
Next
NB: This will error on any slide which doesn't contain a shape named "Counter", and you will need to add logic to handle that condition, if it exists in your Presentation.
Bookmark this link, it's the PPT Object Model Reference which, while laborious to peruse, will explain just about anything you need.
https://msdn.microsoft.com/en-us/library/office/ff743835(v=office.14).aspx
The hardest part (as a beginner) is knowing what questions to ask, and while the Object Model doesn't help you with that immediately, the more you browse it and search it, the more familiar you'll become with the different objects at your disposal, and what you can do with each of them.
If you're new to VBA entirely, also bookmark this list of VBA Statements. This has examples & definitions for all of the control flow & logic statements you might use in putting some code together.
https://msdn.microsoft.com/en-us/library/office/jj692812(v=office.15).aspx
This answer talks about how to run a macro automatically when changing slides, we'll use that approach for your problem. You'll use the OnSlideShowPageChange event because that's an auto macro and doesn't require the more complicated Application Class event handler.
How to run a macro "OnEnterSlide" or "OnLeaveSlide" in Powerpoint VBA?
Implementing this is actually a moderately complicated problem and moreso for someone who is not familiar with VBA or PowerPoint's object model.
Now, you've mentioned a number of things which either are simply not true, or at the very least it is not intuitive or obvious why these must be true, when presented with alternatives:
For that to work I would have to use a macro to add the shape to each slide individually. But then if I wanted to change the size of the shape I have to use code for that too. Not very elegant...would rather force the refresh somehow and use a master slide for the shape
(Note that the scope of your initial problem keeps increasing and becoming more complex).
The reason is to make it easier to change the shape without using code and creating the shape in all the slides.
Sometimes this is the easiest or best way to do things, sometimes it's not. I am in no position to evaluate this with regards to your specific requirements, because you've given only 2 lines of code to examine, but from my vantage point, it seems you are hesitant to write more code because you simply don't know how.
I'm sorry but your deadline is not my problem, and I've spent a generous amount of time on this answer which I hope will at least point you in the right direction.
This is a different approach, you may consider.
Add a button named Sh1, Sh2 in slide 2, Sh3 in slide 3 etc.
and attach all this shapes to the below code
For ctr = 1 To 3
If ActivePresentation.Slides(ctr).Shapes("sh" & ctr).TextFrame2.TextRange.Text = ctr Then
ActivePresentation.SlideShowWindow.View.Next
End If
Exit Sub
Next
End Sub
This would check the current slide with the counter, if matches, moves next else exits.

VBA Word : SmartArts and range

I'm currently working with SmartArts in Word.
The SmartArt already exists, and I would like to find a way to access a SmartArt value from a range, Office cursor (so Selection) or something akin to it.
Here's my SmartArt.
To be exact, the SmartArtLayout is Application.SmartArtLayouts(88)
What I can do already :
Create a SmartArt on my Document, keep it as a Shape to work on it.
Looping on all the Nodes of my SmartArt, selecting the ones I want to keep, and inserting the ones I want to keep in a database, with a link to their parent.
Now I'm blocked on something.
I want to find a Way to edit/delete a particular node, both in DB and in the SmartArt.
It would be possible if I made a Userform that put every Shape Text in a drop-down list, and I then looped on all shapes to find the shape with the chosen text in it.
If noone answers, that'll probably be what I am going to do.
But I don't find this way natural or practical.
So I'm looking for a method to be able to find the Node that have the cursor on it. I would've done something like that :
'Putting my SmartArt in SAShape
For Each Node In SAShape.SmartArt.AllNodes
If (Selection.Range.InRange(Node.Range)) Then
'my code
End If
Next Node
But It'd have been too easy... The shapes don't seem to have a range.
Is there a way to get the Node with focus, or to get the node we clicked on?
Thanks in advance!
Cordially,
firt It not like Question but it is task but i will give you some hint to help you
1) to get any node in any level loop will be like this
Set oShape = ActiveSheet.Shapes(1)
For i = 1 To oShape.SmartArt.AllNodes.Count
oShape.SmartArt.AllNodes(i).TextFrame2.TextRange.Text = "Sample " & i
Next
2)To Get specific node for delete or edit you must determine an id i can not do that perfect but i assign the id to alt text when i add alt text i can find any node
2.1) when shearch
nod.TextFrame2.TextRange.Text = str
nod.Shapes(1).AlternativeText = index
2.2) when check for search
If oShape.SmartArt.AllNodes(i).Shapes(1).AlternativeText = index
and the imge will show where alt store
that i think will help you in some way for do what you need but you must understand how smart art tree change after item delete
smartart

How can i set the name of a textbox in publisher?

I want to set the name of the text box so it can be easily accessed by code.
e.g
I am looking for an editing field similar to this
Thanks
There's a properties Window that can be accessed for each of the controls on the UI. There you may rename the controls. (Since you do not seem to have a VBA code yet and you want to rename the control from UI)
The other alternative. Record a macro, do some changes to the textbox (e.g. resize, change text etc). Then check the programme assigned default name of the textbox from the VBA editor. As you said, you can access the control via this default name and utilizing your VBA code (as you said), rename the textbox.
If you really want to be editing a worksheet object in Publisher you will have to get the OLEobject of the Shape and interpret it as an Excel.Application.
If you are just looking for a placeholder solution for Publisher documents, you could simply create a textbox that contains a certain string, then loop through all pages, all shapes on each page where HasTextFrame = msoTrue, and compare shape.TextFrame.TextRange.Text to your placeholder string. If it's the one you're after, you can do anything you want with the shape in question.
Sorry for the vague answer, but your images don't work anymore.
Edit: you can work with Shape.Name for your comparison (you mentioned this property in a comment), but I have no idea how you'd set the value from the interface, without using VBA, in the first place, so if you're making templates the approach I outlined above might be easier for users (see https://msdn.microsoft.com/EN-US/library/office/ff939233.aspx for Shape.Name). There is also a .Name property for page objects (https://msdn.microsoft.com/EN-US/library/office/ff940382.aspx), so you should be able to do something like ActiveDocument.Pages("page_name").Shapes("shape_name").TextRange.Text = "your content" once you've figured out how to actually set the name values
Edit 2:
You can also try to use search and replace as per Replacing Text in Microsoft Publisher Using Powershell if you don't need to do anything advanced beyond placing some text
Edit 3: Given the title of your question, unless you can figure something out with Publisher's interface, you can set the .Name property of the selected text box (or other shape) with dim shape = Selection.ShapeRange.TextFrame.Parent and shape.Name = "your_name". You can set the name of the selected page with ActiveDocument.ActiveView.ActivePage.Name="your_name". (Create a VBA macro that prompts you for names and you should be good to go)

Dynamically update text in powerpoint using VBA

I have a report that is generated in PowerPoint, and underneath many of the graphs, there is text that tells the reader to refer to pages in the appendix. I would like to be able to dynamically reference these slides.
For example, under a graph I might have the text "Please see appendix page 54", but I need the 54 to be linked to a slide so that if I insert another slide it will say 55.
Is this possible to do in VBA? I do not expect somebody to write my code for me, I would just like to know if this is a reasonable thing to do before I spend hours attempting to do it.
Side note: I feel horrible asking a question about MS Office on here, but since I believe it would need to be implemented in VBA (I don't think this functionality is built in by default) I think that it is a relevant question.
No need to feel horrible asking this here.
How one might do this:
In PPT, shapes, slides and even the presentation itself can have an associated tag collection; named string values. For example, assuming a reference to the shape in oSh, you can do:
oSh.Tags.Add "AssociatedSlideId", "293"
In this case, you'd apply this tag to your graph; the 293 would be the SlideID of the slide you want to reference. Each slide has a unique SlideID assigned when it's created; the SlideID won't change when you move the slide around/add/delete slides.
To read the tag from the shape:
Debug.Print oSh.Tags("AssociatedSlideId")
In this case, that'd return "293". Feed that to FindBySlideID to get the SlideIndex of the slide (ie, the ordinal position of the slide in the presentation). Or ask it for SlideNumber if you want the number of the slide that'll appear in number placeholders (usually, but not always the same as slide index).
Debug.Print ActivePresentation.Slides.FindBySlideID(clng("293")).SlideIndex
You might also tag the textbox or other shape that you want to use to hold the reference, then write a function along the lines of:
Function ShapeTaggedWith(oSl as Slide, sTagName as String, sTagValue as String) as Shape
This would iterate through the shapes on slide oSl looking for one with a tag named sTagName, value = sTagValue and return it to the caller if found.
Now you can find the shape that's nominated as your caption for the graph, let's call it, and change its text to match the SlideIndex (or SlideNumber) of the slide the chart's supposed to reference.
Hope that's all moderately clear; if not, that's why the StackOverflow gods gave us comments.