So I have been doing quite a bit of debugging/troubleshooting of this and I honestly can't explain why I am seeing the behavior I am seeing. Perhaps so of the guru's here can assist.
What I am trying to do (50k view):
I have a macro to take a shape (either from a right-click action menu, or from a double-click event) and create a selection (visio.selection) of the shape and all shapes it is connected to.
What I am experiencing:
Normally, the code I am using works fine. It adds the shapes to a visio.selection object and then sets the activepage.selection to that selection object. No problem.
HOWEVER, if I have one of the connected shapes as part of a group, then the logic appears to completely walk all over itself in adding shapes to the visio.selection object. I have sat watching the selection object in the debugger and see the selected objects change and then disappear (!!??) from the object list when doing:
Call vsoSelection.Select(connectedShape, Visio.VisSelectArgs.visSelect)
I have tried changing the selection object's IterationMode behavior (e.g. visSelModeSkipSuper), but that doesn't seem to make a difference.
At this point, I am wondering whether there is some deeper layer of understanding that I am missing, or whether this might be an actual bug..
Here is a pared down code snippet for the routine:
Public Sub SelectAllConnections(pCallingShape As Visio.Shape, Optional ByVal pIncludeLinks As Boolean = False)
Dim vsoSelection As Visio.Selection
Dim lngShapeIDs() As Long 'This is the list of shape IDs to select
Dim lngShapeIDs2() As Long 'If the calling shape is a 2-D shape, then this will hold the 1-D shapes (links) connected to the shape
Dim connectedShape As Visio.Shape
Dim pShapeID as Variant
Dim i As Integer
Dim currentPage As Visio.Page
Dim currentApplication As Visio.Application
Set currentApplication = pCallingShape.Application
Set currentPage = currentApplication.ActiveWindow.Page
'Pick up the objects that are already selected
Set vsoSelection = ActiveWindow.Selection
'I tried using this below to create the selection object.. no difference.
'Set vsoSelection = ActivePage.CreateSelection(visSelTypeEmpty, visSelModeSkipSuper, pCallingShape)
'Change the iteration mode to prevent problems with selecting children of a group
'vsoSelection.IterationMode = Visio.VisSelectMode.visSelModeSkipSuper
'Make sure the passed shape is selected. (If somehow this procedure is called directly)
Call vsoSelection.Select(pCallingShape, Visio.VisSelectArgs.visSelect)
'Get the connected shapes for the shape which was passed in
If (pCallingShape.OneD) Then
' The calling shape is a line (connector), so get the shapes to add through a different method
' This currently isnt used
lngShapeIDs = pCallingShape.GluedShapes(visGluedShapesAll2D, "")
Else
lngShapeIDs = pCallingShape.ConnectedShapes(visConnectedShapesAllNodes, "")
End If
'Adding in the 1D shapes also, in case a line is connected to a line.. Would have to add code to pick up
'any 2D shape at the end of the line-line connection
If (pIncludeLinks) Then
lngShapeIDs2 = pCallingShape.GluedShapes(visGluedShapesAll1D, "")
Else
'skip adding the links
End If
'I didn't include this code, but it never changes the lngShapeIDs array in my test cases.
Call ConcatenateArrays(lngShapeIDs, lngShapeIDs2)
For Each pShapeID In lngShapeIDs
Set connectedShape = currentPage.Shapes.ItemFromID(pShapeID)
'Add to the selection object
Call vsoSelection.Select(connectedShape, Visio.VisSelectArgs.visSelect)
Next pShapeID
'Now set the selection in the window to the selection object
Application.ActiveWindow.Selection = vsoSelection
End Sub
In my debugging, I have seen the following:
lngShapeIDs: 18, 266, 452, 514
Calling shapeID is: 80
At the start, the Selection object has (because I added it at the start):
Item1: 80
After first pass in the loop the Selection Object has:
Item1: 18 (!??!?)
After second pass in the loop, the selection Object has:
Item1:18
Item2: 266
After the third pass in the loop, the selection Object has:
Item1: 452 (!!??!)
After the fourth pass in the loop, the selection Object has:
Item1: 452
Item2: 514
I believe that 18 and 262 are the ones that belong to a group.
Any thoughts or ideas?
This is probably because you can't select shapes belonging to different parents in the UI neither.
The result would be weird anyway.
You may try collecting the shapes in an other data structure (array, collection, ...).
Have you tried the shapes connected method? https://msdn.microsoft.com/en-us/vba/visio-vba/articles/shape-connectedshapes-method-visio (https://msdn.microsoft.com/en-us/vba/visio-vba/articles/shape-connectedshapes-method-visio)
Wapperdude
Quote from: wapperdude on April 11, 2018, 02:18:51 AM
Have you tried the shapes connected method? https://msdn.microsoft.com/en-us/vba/visio-vba/articles/shape-connectedshapes-method-visio (https://msdn.microsoft.com/en-us/vba/visio-vba/articles/shape-connectedshapes-method-visio)
Wapperdude
Sorry, Wapperdude, I am not understanding where you are going ... yes, I already have that in my code:
lngShapeIDs = pCallingShape.ConnectedShapes(visConnectedShapesAllNodes, "")
But that isn't the problem I am encountering. I can create the list of shape IDs just fine. It is creating a selection object with those shapes that is the problem.
Quote from: Yacine on April 10, 2018, 09:33:46 PM
This is probably because you can't select shapes belonging to different parents in the UI neither.
The result would be weird anyway.
You may try collecting the shapes in an other data structure (array, collection, ...).
Hmmm.. so I tested your theory, and (to me, at least) it looks like you _can_ create a selection manually in the UI (see attached pic). (BTW, I am using Microsoft Visio 2016 Professional). Am I missing something?
I have an array of shapeIDs already, through this routine.. but what I need is to actually make the activewindow.selection contain these shapes.
Any other thoughts/ideas?
Ah. Missed that. Sorry. I can't try your code, as V2007 doesn't have all of the VBA features that you're using...the object model has been updated a couple of times.
Wapperdude
I vaguely recall an issue with the "subselect" option...haven't tracked it down yet. But it occurs to me that you may not need it. The connects method gives you the shape ID, then, if you make that shape an activev selection, you could add it to your collection array directly without using the subsequent feature.
'Based upon ID:
' Set vsoShp = ActivePage.Shapes.ItemFromID(1)
' ActiveWindow.Select vsoShp, visSelect 'This makes the shape ACTIVE. Now add to your collection array.
Seems like it should work.
Ok, I appreciate the effort though.
So I some manual testing and debugging, after considering Yacine's post, and the pic is (I think) a core issue (probably in my understanding).
I created a group of those three shapes, clicked on the center one (720), which selected the group. I clicked on the shape again, to sub-select that shape (720).
I wrote a simple stub and set up a watch variable on ActiveWindow.Selection, and this is what I see:
The object does not have _ANY_ items in the list.. !?
So that leads me to believe that somehow the internal 'selection' structures aren't exposed to me fully.. if Visio itself clearly has that object as a selected object.
Is my thinking off base?
Quote from: wapperdude on April 11, 2018, 02:20:06 PM
I vaguely recall an issue with the "subselect" option...haven't tracked it down yet. But it occurs to me that you may not need it. The connects method gives you the shape ID, then, if you make that shape an activev selection, you could add it to your collection array directly without using the subsequent feature.
'Based upon ID:
' Set vsoShp = ActivePage.Shapes.ItemFromID(1)
' ActiveWindow.Select vsoShp, visSelect 'This makes the shape ACTIVE. Now add to your collection array.
Seems like it should work.
So, I haven't gone back to this style of adding the shapes (directly selecting them with ActiveWindow.Select).. let me try that really quick in a test.
So, this simple test yields the same error I was encountering.. (item 1 is the shape labelled as 720, which is a member of a group).
There must be something I am missing..
Although you were right with different parent selection (my bad), this path wasn't that wrong.
I draw some shapes (ID 1,2,3 - 3 being a group containing ID4).
I couldn't select the shapes 1,2,4 directly by code.
I needed to select #3 first, then #4 by:
activewindow.select ActiveWindow.Page.Shapes.ItemFromID(4), visSubSelect
Yacine, I appreciate your help! I went back and reviewed the documentation for Selection.Select and now it made a bit more sense. Once I started thinking about what I was seeing in the debugger, it seemed a bit more rational.. I am not quite confident I fully understand where it is storing the parent selections, but I understand why it is storing them differently.
So while I don't really want the SuperSelections to be included (i.e. the parent groups) in the selection, I guess it will be ok for what I am trying to do at the moment. So I threw together the following code stubs to enable me to not worry about whether it is in a group:
Public Sub test_recursive()
Dim vsoShape As Visio.Shape
Dim vsoSelection As Visio.Selection
Set vsoShape = ActivePage.Shapes.ItemFromID(1)
Set vsoSelection = ActiveWindow.Selection
Call AddSelectedShape(vsoSelection, vsoShape)
ActiveWindow.Selection = vsoSelection
End Sub
'-------------------------------------------------------------------------
' AddSelectedShape
' A recursive procedure to add a shape (and its parent if it is a group) to a selection
' If there are multiple groups nested, it will continue to walk up the groups until the top and add all the groups
'-------------------------------------------------------------------------
Public Sub AddSelectedShape1(ByRef pvsoSelection As Visio.Selection, ByRef pShape As Visio.Shape)
' If the shape's parent is a group, then we need to add the parent before we subselect the shape itself
If (pShape.Parent.Type = visTypeGroup) Then
Call AddSelectedShape(pvsoSelection, pShape.Parent)
'Now add the shape as a subselect (since we know it is a child)
pvsoSelection.Select pShape, visSubSelect
Else
'It doesn't have a group as a parent, so we can add it as a visSelect
pvsoSelection.Select pShape, visSelect
End If
End Sub
Ran the test and I saw it went through the logic properly (here are the debug messages I spit out, with two nested groups):
QuoteEntered AddSelectedShape for 1:APP.720
1:APP.720 has a parent who is a group (9:Sheet.9)
Entered AddSelectedShape for 9:Sheet.9
9:Sheet.9 has a parent who is a group (26:Sheet.26)
Entered AddSelectedShape for 26:Sheet.26
Adding 26:Sheet.26 to selection via Select
Exiting AddSelectedShape for 26:Sheet.26
Adding 9:Sheet.9 to selection via subSelect
Exiting AddSelectedShape for 9:Sheet.9
Adding 1:APP.720 to selection via subSelect
Exiting AddSelectedShape for 1:APP.720
However, when I went to the drawing, nothing was selected... Hmm..
On a whim, I swapped the use of a separate selection object with the ActiveWindow.Selection:
Public Sub test_recursive()
Dim vsoShape As Visio.Shape
Dim vsoSelection As Visio.Selection
Set vsoShape = ActivePage.Shapes.ItemFromID(1)
Set vsoSelection = ActiveWindow.Selection
Call AddSelectedShape(vsoShape)
End Sub
'-------------------------------------------------------------------------
' AddSelectedShape
' A recursive procedure to add a shape (and its parent if it is a group) to a selection
' If there are multiple groups nested, it will continue to walk up the groups until the top and add all the groups
'-------------------------------------------------------------------------
Public Sub AddSelectedShape(ByRef pShape As Visio.Shape)
' If the shape's parent is a group, then we need to add the parent before we subselect the shape itself
If (pShape.Parent.Type = visTypeGroup) Then
Call AddSelectedShape(pShape.Parent)
'Now add the shape as a subselect (since we know it is a child)
ActiveWindow.Select pShape, visSubSelect
Else
'It doesn't have a group as a parent, so we can add it as a visSelect
ActiveWindow.Select pShape, visSelect
End If
End Sub
And that worked! So I find it odd that it would work when using the ActiveWindow.Selection, but not a Visio.Selection.. ? Maybe my use of :
ActiveWindow.Selection = vsoSelection
isn't correct?
QuoteCode: [Select]
ActiveWindow.Selection = vsoSelection
isn't correct?
This is, indeed, incorrect. Active Window.Selection takes it's value directly from the page's selected objects. You cannot assign a value to it...at least, not to my knowledge. However, the opposite is valid:
Set vsoSelection = ActiveWindow.Selection
Obviously, I can't see all that you're doing, and I realise you must eliminate shapes that aren't connected to you r chosen shape, but it seems like once you get a valid ID, you can
Set vsoShp = Active Page.Shapes.Itemfromid(shape's I'd)
ActiveWindow.Select gosSip, desElect
Set selEction = Active Window.Selection.
Place this inside your loop. As the code progresses thru the shapes, the number of selected shapes increases, and so does your vsoSelection. It shouldn't matter if the identified shape is a subshape in a group or not. You just need its ID, and that comes from the connects object.
The hard work is filtering out the unwanted shapes, and seems you've handled that.
HTH
Wapperdude
Ok.. so then what would be the use/purpose/need for the Visio.Selection object ? If you cannot use it as I did, it seems to me its utility is marginal at best (to just store the activewindow.selection, say, in the process of iterating through the items).
So, I will say, however, that doing that assignment _does_ work (perhaps it wasn't meant to work...). If there are no groups at all, the code works just fine, and does, in fact, assign the active window selection equal to the contents of my vsoSelection object.
Just using the activewindow.select strategy isn't perfect -- Lets say for example that I start some process to select a bunch of shapes for the user. I start adding them, adding them, then WOAH.. I encountered something that I didn't want or is meant to stop the process. If I actively was selecting them using activewindow.select, there would be really no easy way to 'undo' the process to get back to the original state. (Not sure if that made sense).
Here's a topic re using selection method to get subshapes of a group...see replies by Thomas Winkel. Going thru that helped me recall issue with selection method...the group must be selected in order to find the subshapes. There are code examples in the post.
But, since the connections method gives you the needed shape's ID, the selection method is unnecessary. Hence, the 3 lines of code I showed. Oops, I see there are auto-correct typos....correct code...
Set vsoShp = Active Page.Shapes.ItemFromID(shape's id) ' id from connections method
ActiveWindow.Select vsoShp, visSelect ' adds shape to selected shapes on the page
Set vsoSelection = Active Window.Selection ' updates the collection of selected shapes
Thanks for the discussion Wapperdude.
Not to belabor the point, but I think I am not exactly clear in why I was trying to add the shapes to a Selection object and then (all at once) set the active selection to that selection object.
In my 'assembling' of that collection of shapes, i might encounter something (e.g. finding a shape with some flag or something) that might force me to stop what I am doing and error/notify the user. In that case, the behavior I would have wanted is to _not_ have changed what is actively selected (i.e. I wouldn't want to have added 3 out of 10 of the shapes to the active selection and leave it haphazard).
So, certainly I could just do that logic and store those shapes in just an array of shape IDs (like I am doing).. and then use the ActiveWindow.Select method to do them all at once... It just puzzles me what the use of the visio.selection object really is, besides someone using it as a temporary variable to iterate over it, instead of iterating over activewindow.select..
Thanks again for your help.
QuoteNot to belabor the point, but I think I am not exactly clear in why I was trying to add the shapes to a Selection object and then (all at once) set the active selection to that selection object.
Because that's what you said you needed...
QuoteWhat I am trying to do (50k view):
I have a macro to take a shape (either from a right-click action menu, or from a double-click event) and create a selection (visio.selection) of the shape and all shapes it is connected to.
Belaboring is Ok. Makes sure we're on the same page. So, now my turn ;)...
You start the process by selecting a shape. Then you search thru the shapes on the page to see who's connected to it. Presumably these are 1-D shapes, connectors or lines. The method of searching is the connections method. When a valid shape is discovered, you then select it. (At this point, you have a choice: add the shape to an array, add the shape to selection group, or wait until the end and update the selection group. If you using an array to store the shapes, you don't need to do anything with the selections.) My understanding from the above, the connected shapes were being selected and this "group" was stored in the his Selection variable...possibly to make list or whatever. Obviously, an array containing said shapes would also suffice.
Hope this clarifies things.
BTW, as you undoubtedly have discovered, the connection info is in the 1D shapes, not the 2D shapes.
Wapperdude
Yes.. you are right.. I guess I was biased by my original thought process.. I still puzzle about why it my ActiveWindow.Selection = vsoSelection worked previously then..
Went back and tried the line:
ActiveWindow.Selection = vsoSelection
It does indeed work...in V2007. But seems redundant, unless at some point the shapes are de-selected, and then need to be re-selected.
Here's my test code:
Sub shpSel()
Dim vsoSelection As Selection
Dim vsoShp As Shape
Dim i As Integer
Dim shpCnt As Integer
ActiveWindow.DeselectAll 'Remove all existing selections.
shpCnt = ActivePage.Shapes.Count
For i = 1 To shpCnt Step 2
Set vsoShp = ActivePage.Shapes.ItemU(i)
'Add fill color within the loop:
vsoShp.CellsU("FillForegnd").FormulaU = "RGB(255, 255, 0)"
'Create a Selection collection:
ActiveWindow.Select vsoShp, visSelect 'Adds shape to existing selection population
' Set vsoSelection = ActiveWindow.Selection 'Build/set vsoSelecton as loop progresses
Next
Set vsoSelection = ActiveWindow.Selection 'Set vsoSelection after loop complete
ActiveWindow.DeselectAll 'Pause this step, to 1st see existing selection
ActiveWindow.Selection = vsoSelection 'This works. The vsoSelection is restored.
' For Each vsoShp In vsoSelection
' ActiveWindow.Select vsoShp, visSelect
' vsoShp.CellsU("LineWeight").FormulaU = "2 pt"
' Next
''Alternative shape fill coloring:
' For Each vsoShp In vsoSelection
' vsoShp.CellsU("FillForegnd").FormulaU = "RGB(255, 255, 0)"
' Next
End Sub
Wapperdude