The Visio Product Team uses Visio for a number of tasks. A common task is creating storyboards to show how a user might construct a diagram. If you’ve ever seen one of those “behind the scenes” documentaries for a movie, you’ve seen the storyboarding process. Each step the user takes is captured on a separate page in a Visio drawing. Then the entire document is shown as a presentation to replay the construction of the diagram.
It seems that quite a few people use Visio for this sort of task. Visio is a pretty good tool for mocking up application or web interfaces. One common request is to make copying between pages work better. In these types of documents, each page is almost identical to the one before it. One of the nicer touches in PowerPoint is that objects copied from one page (slide) to another are pasted in exactly the same position. In Visio objects are pasted to the center of the screen making repositioning a chore. Visio needs a smarter paste behavior.
A little bit of code can solve the problem for Visio. Actually, there are two ways to solve the problem. You could write your own procedures to perform copy and paste with better logic for positioning. Alternatively, you could handle the event notifications from Visio when copy and paste occur and fix up the positioning after the fact. The attached Visio document has VBA code to illustrate both methods.
Create your own copy / paste commands
When invoking Visio’s Copy and Paste command through automation, Visio allows you to specify whether the position of the selection on the page should be preserved using the visCopyPasteNoTranslate flag. Thus you can create your own macros that do copy and paste:
Public Sub CopyNoTranslate()
Application.ActiveWindow.Selection.Copy (visCopyPasteNoTranslate)
End Sub
Public Sub PasteNoTranslate()
Application.ActivePage.Paste (visCopyPasteNoTranslate)
End Sub
In fact you can assign Ctrl-C and Ctrl-V as the accelerator keys for these macros to override Visio’s copy / paste. If the real goal is to support copying to a new page, this macro will perform the necessary steps:
Public Sub CopyToNewPage()
Dim vsoSelection As Visio.Selection
'Make sure we are in a drawing window
If Application.ActiveWindow.Type = Visio.VisWinTypes.visDrawing Then
'If there is no selection, select all shapes on the page
Set vsoSelection = Application.ActiveWindow.Selection
vsoSelection.IterationMode = 0
If vsoSelection.Count = 0 Then
vsoSelection.SelectAll
End If
'Copy selection to new page
vsoSelection.Copy Visio.visCopyPasteNoTranslate
Application.ActiveDocument.Pages.Add
Application.ActivePage.Paste Visio.visCopyPasteNoTranslate
End If
End Sub
Respond to Visio’s copy / paste events
This method uses an event handler to detect copy and paste actions. The position of the selection is read during the copy action. The new position of the selection (which will be placed in the center of the window) is read during the paste action. The current page is recorded during each action as well. If the copy and paste actions occur on different pages, the code moves the selection back to its previous coordinates following the paste action.
Private Sub vsoApp_EnterScope(ByVal app As IVApplication, ByVal nScopeID As Long, ByVal bstrDescription As String)
'Check for Copy action in a drawing window
If nScopeID = 1021 And app.ActiveWindow.Type = Visio.VisWinTypes.visDrawing Then
Dim vsoSelection As Visio.Selection
Set vsoSelection = app.ActiveWindow.Selection
If vsoSelection.Count > 0 Then
'Find the shape in the selection at the bottom of the z-order
Dim bottomShape As Integer
bottomShape = GetBottomShape(vsoSelection)
'Record scope ID and selection information
nCopyScopeID = nScopeID
gCopyPageID = app.ActivePage.PageSheet.UniqueID(Visio.visGetOrMakeGUID)
dShapePosX = vsoSelection(bottomShape).CellsU("PinX").ResultIU
dShapePosY = vsoSelection(bottomShape).CellsU("PinY").ResultIU
End If
End If
End Sub
Private Sub vsoApp_ExitScope(ByVal app As IVApplication, ByVal nScopeID As Long, ByVal bstrDescription As String, ByVal bErrOrCancelled As Boolean)
'Check for Paste action in a drawing window
If nScopeID = 1022 And app.ActiveWindow.Type = Visio.VisWinTypes.visDrawing Then
If nCopyScopeID <> 0 Then
Dim vsoSelection As Visio.Selection
Set vsoSelection = app.ActiveWindow.Selection
Dim activePageID As String
activePageID = app.ActivePage.PageSheet.UniqueID(Visio.visGetOrMakeGUID)
If vsoSelection.Count > 0 And gCopyPageID <> activePageID Then
'Find the shape in the selection at the bottom of the z-order
Dim bottomShape As Integer
bottomShape = GetBottomShape(vsoSelection)
'Fix up position of selection by offsetting from the bottom shape
Dim posX As Double, posY As Double
posX = vsoSelection(bottomShape).CellsU("PinX").ResultIU
posY = vsoSelection(bottomShape).CellsU("PinY").ResultIU
vsoSelection.Move dShapePosX - posX, dShapePosY - posY
End If
End If
End If
End Sub
The full code for this method is found in the attached document. We use the EnterScope and ExitScope events to detect the copy and paste actions. The key problem to solve is recording the current position of the selected shapes. Visio selections do not have coordinates associated with them, so the coordinates of one of the shapes within the selection must be used. The trick is to make sure that the same shape is scanned in both the copy and paste actions. Visio does not necessarily preserve the order of shapes in the selection during paste. This code finds the bottommost shape in the z-order (layering order) to ensure that the same shape is scanned each time.
To use this method, open the SmartPaste.vsd file in Visio along with the other documents you are working on. Once you enable macros for the file, SmartPaste will work with your regular Copy and Paste actions.
If you find this type of behavior useful or find it counter-productive, please let us know.