Page Index Toggle Pages: 1 Send TopicPrint
Hot Topic (More than 10 Replies) DiagramLinkCollection Modified On Nested Looping (Read 5535 times)
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
DiagramLinkCollection Modified On Nested Looping
Apr 22nd, 2010 at 1:37pm
Print Post  
I have come across an issue wherein a ShapeNode's Incoming/Outgoing DiagramLinkCollection is being modified while I am performing a nested loop that checks Tag objects on links and their anchor points (I am not trying to delete or modify the links in the collections in any way while performing my loops).  During the looping the order of the links inside the collections is changing (I am not programmatically doing this) which causes a collection modified exception.

Here is the code that has been giving me problems:

Code
Select All
Private Function GetListOfFlowPaths(ByRef StartNode As MindFusion.Diagramming.ShapeNode, _
						    ByVal EntityNum As Entity1Or2) As System.Collections.Generic.List(Of HEXDesignII.FlowPath)
	  Dim ListOfFlowPaths As New System.Collections.Generic.List(Of HEXDesignII.FlowPath)

	  Dim FlwPth As HEXDesignII.FlowPath
	  Dim LastLink As MindFusion.Diagramming.DiagramLink
	  Dim NextLink As MindFusion.Diagramming.DiagramLink
	  Dim LastNode As MindFusion.Diagramming.ShapeNode = StartNode
	  Dim NextNode As MindFusion.Diagramming.ShapeNode
	  Dim iLink As MindFusion.Diagramming.DiagramLink

For Each iLink In StartNode.IncomingLinks
		If GetLinkType(iLink) = LinkType.InletStart Then
		NextLink = GetOutletLink(iLink)
		If CType(NextLink.Tag, Entity1Or2) = EntityNum Then
			  'Found the starting link
			  LastLink = NextLink
			  'Get the FlowPath from the StartNode
			  FlwPth = GetFlowPath(LastLink, StartNode, False)
			  ListOfFlowPaths.Add(FlwPth)
			  LastNode = StartNode
			  Exit For
		    End If
		End If
	  Next iLink

'Start the loop to define the list of flow paths for the defined entity flow
	  If GetLinkType(NextLink) <> LinkType.OutletEnd Then
		'handles the 1 template case
		Dim PathFinished As Boolean = False

		Do
		    If LastLink IsNot Nothing Then
			  NextNode = LastLink.Destination
			  NextLink = GetOutletLink(LastLink)
			  If GetLinkType(NextLink) = LinkType.Outlet Then
				'Found the final node in the path
				FlwPth = GetFlowPath(LastLink, NextNode, True)
				PathFinished = True
			  Else
				If CType(NextLink.Tag, Entity1Or2) <> EntityNum Then
				    MsgBox("Assigned flow does not match between the inlet and outlet ports of at least one template", MsgBoxStyle.OkOnly, "HEXDesign Warning")
				Else
				    FlwPth = GetFlowPath(NextLink, NextNode, False)
				End If
			  End If
			  ListOfFlowPaths.Add(FlwPth)
			  LastLink = NextLink
			  LastNode = NextNode
		    End If

		Loop Until PathFinished = True
	  End If
	  Return ListOfFlowPaths
    End Function

 Private Function GetOutletLink(ByRef InletLink As MindFusion.Diagramming.DiagramLink) As MindFusion.Diagramming.DiagramLink
 'This routine can be called to get the flow connection link on the matching outlet port of the same node as the inlet link argument
	  Dim OutletLink As MindFusion.Diagramming.DiagramLink
	  Dim InltDest As Integer = InletLink.DestinationAnchor
	  For Each nodeLink As MindFusion.Diagramming.DiagramLink In InletLink.Destination.OutgoingLinks
		If nodeLink.Visible Then
		    If InletLink.DestinationAnchor = GetAnchorIndex(InletLink.Destination, AnchorType.Inlet1) And _
		    nodeLink.OriginAnchor = GetAnchorIndex(nodeLink.Origin, AnchorType.Outlet1) Then
			  OutletLink = nodeLink
			  Exit For
		    ElseIf InletLink.DestinationAnchor = GetAnchorIndex(InletLink.Destination, AnchorType.Inlet2) And _
		    nodeLink.OriginAnchor = GetAnchorIndex(nodeLink.Origin, AnchorType.Outlet2) Then
			  OutletLink = nodeLink
			  Exit For
		    End If
		End If
	  Next nodeLink

	  Return OutletLink
    End Function

Private Function GetAnchorIndex(ByRef node As MindFusion.Diagramming.ShapeNode, ByVal AncType As AnchorType) As Integer
	  Dim index As Integer = -1
	  Dim counter As Integer

	  For Each AnchorPt As MindFusion.Diagramming.AnchorPoint In node.AnchorPattern.Points
		If AnchorPt.Tag = AncType Then
		    index = counter
		    Exit For
		End If
		counter += 1
	  Next
Return index
    End Function
 



The problem seems to only occur for this code structure where my nested looping and GetAnchorIndex() code is performed in separate functions. 

While I have found a temporary patch to fix the error, the underlying problem still exists and could potentially influence future software development with FlowChart.  I would greatly appreciate any insight or support that MindFusion can provide on this matter.

Thank you in advance.

- Chris
  
Back to top
 
IP Logged
 
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #1 - Apr 22nd, 2010 at 1:37pm
Print Post  
When I combine all the code into one large function as follows, the collections are not modified:

Code
Select All
 Private Function GetListOfFlowPaths(ByRef StartNode As MindFusion.Diagramming.ShapeNode, _
						    ByVal EntityNum As Entity1Or2) As System.Collections.Generic.List(Of HEXDesignII.FlowPath)
	  Dim ListOfFlowPaths As New System.Collections.Generic.List(Of HEXDesignII.FlowPath)

	  Dim FlwPth As HEXDesignII.FlowPath
	  Dim LastLink As MindFusion.Diagramming.DiagramLink
	  Dim NextLink As MindFusion.Diagramming.DiagramLink
	  Dim LastNode As MindFusion.Diagramming.ShapeNode = StartNode
	  Dim NextNode As MindFusion.Diagramming.ShapeNode
	  Dim iLink As MindFusion.Diagramming.DiagramLink

	  For Each iLink In StartNode.IncomingLinks
		If GetLinkType(iLink) = LinkType.Inlet Then
		    Dim InltDest As Integer = iLink.DestinationAnchor
		    Dim nodeLink As MindFusion.Diagramming.DiagramLink
		    Dim destind_1, OrINt_1, destind_2, OrINt_2 As Integer
		    For Each nodeLink In iLink.Destination.OutgoingLinks
			  If nodeLink.Visible Then
				    destind_1 = -1
				Dim i As Integer = 0
				For Each pt As MindFusion.Diagramming.AnchorPoint In iLink.Destination.AnchorPattern.Points
				    If pt.Tag = AnchorType.Inlet1 Then
					  destind_1 = i
					  Exit For
				    End If
				    i += 1
				Next pt
				OrINt_1 = -1
				i = 0
				For Each pt As MindFusion.Diagramming.AnchorPoint In nodeLink.Origin.AnchorPattern.Points
				    If pt.Tag = AnchorType.Outlet1 Then
					  OrINt_1 = i
					  Exit For
				    End If
				    i += 1
				Next pt
				destind_2 = -1
				i = 0
				For Each pt As MindFusion.Diagramming.AnchorPoint In iLink.Destination.AnchorPattern.Points
				    If pt.Tag = AnchorType.Inlet2 Then
					  destind_2 = i
					  Exit For
				    End If
				    i += 1
				Next pt
				OrINt_2 = -1
				i = 0
				For Each pt As MindFusion.Diagramming.AnchorPoint In nodeLink.Origin.AnchorPattern.Points
				    If pt.Tag = AnchorType.Outlet2 Then
					  OrINt_2 = i
					  Exit For
				    End If
				    i += 1
				Next pt
				If iLink.DestinationAnchor = destind_1 And _
				nodeLink.OriginAnchor = OrINt_1 Then
				    NextLink = nodeLink
				    Exit For
				ElseIf iLink.DestinationAnchor = destind_2 And _
				nodeLink.OriginAnchor = OrINt_2 Then
				    NextLink = nodeLink
				    Exit For
				End If
			  End If
		    Next nodeLink

		    If CType(NextLink.Tag, Entity1Or2) = EntityNum Then
			  'Found the starting link
			  LastLink = NextLink
			  'Get the FlowPath from the StartNode
			  FlwPth = GetFlowPath(LastLink, StartNode, False)
			  ListOfFlowPaths.Add(FlwPth)
			  LastNode = StartNode
			  Exit For
		    End If
		End If
	  Next iLink

	  'Start the loop to define the list of flow paths for the defined entity flow
	  If GetLinkType(NextLink) <> LinkType.Outlet Then
		'handles the 1 template case
		Dim PathFinished As Boolean = False

		Do
		    If LastLink IsNot Nothing Then
			  NextNode = LastLink.Destination
			  NextLink = GetOutletLink(LastLink)
			  If GetLinkType(NextLink) = LinkType.Outlet Then
				'Found the final node in the path
				FlwPth = GetFlowPath(LastLink, NextNode, True)
				PathFinished = True
			  Else
				If CType(NextLink.Tag, Entity1Or2) <> EntityNum Then
				    MsgBox("Assigned flow does not match between the inlet and outlet ports of at least one template", MsgBoxStyle.OkOnly, "HEXDesign Warning")
				Else
				    FlwPth = GetFlowPath(NextLink, NextNode, False)
				End If
			  End If
			  ListOfFlowPaths.Add(FlwPth)
			  LastLink = NextLink
			  LastNode = NextNode
		    End If

		Loop Until PathFinished = True
	  End If
	  Return ListOfFlowPaths
    End Function  


  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #2 - Apr 22nd, 2010 at 3:15pm
Print Post  
What does the GetFlowPath method do? Does it take ByRef arguments too? ByRef might change the variable passed as argument if you assign to it from within the method.
  
Back to top
 
IP Logged
 
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #3 - Apr 22nd, 2010 at 4:04pm
Print Post  
Stoyo,

Thank you for your response. The GetFlowPath() function takes in a DiagramLink as a ByRef argument but does not manipulate any of its members, only checks anchor indices. Based on the anchor point index the function returns an object I have defined that exists in the tag of the connected ShapeNode.

Here is the code:

Code
Select All
Private Function GetFlowPath(ByRef Link As MindFusion.Diagramming.DiagramLink, ByRef Node As MindFusion.Diagramming.ShapeNode, ByVal IsFinalNode As Boolean) As FlowPath
	  Dim ThisFlowPath As FlowPath

	  Dim PathTemplate As PairedPathsTemplate
	  PathTemplate = CType(Node.Tag, PairedPathsTemplate)
	  If IsFinalNode Then
		If Link.DestinationAnchor = GetAnchorIndex(Node, AnchorType.Inlet1) Then
		    ThisFlowPath = PathTemplate.GetPath1
		ElseIf Link.DestinationAnchor = GetAnchorIndex(Node, AnchorType.Inlet2) Then
		    ThisFlowPath = PathTemplate.GetPath2
		End If
	  Else
		If Link.OriginAnchor = GetAnchorIndex(Node, AnchorType.Outlet1) Then
		    ThisFlowPath = PathTemplate.GetPath1
		ElseIf Link.OriginAnchor = GetAnchorIndex(Node, AnchorType.Outlet2) Then
		    ThisFlowPath = PathTemplate.GetPath2
		End If
	  End If

	  Return ThisFlowPath
    End Function 



The collection modified exception is thrown inside the GetOutletLink() function I presented in my original post.

Hope this helps.

- Chris
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #4 - Apr 22nd, 2010 at 6:19pm
Print Post  
Is this code running in the main thread?
  
Back to top
 
IP Logged
 
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #5 - Apr 22nd, 2010 at 6:23pm
Print Post  
Unless the FlowChart DLLs use multiple threads then yes, I am running on a single main thread.
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #6 - Apr 23rd, 2010 at 5:59am
Print Post  
Our code modifies IncomingLinks and OutgoingLinks only when links are connected or disconnected to / from a node. They can't be modified if you simply loop over them. If you have any conditional breakpoints set in that code, check if some breakpoint expression doesn't set a link's Origin or Destination inadvertently. If you send us a sample project that reproduces this, our developer will investigate it further.

Stoyan
  
Back to top
 
IP Logged
 
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #7 - Apr 23rd, 2010 at 2:51pm
Print Post  
Stoyan,

I have put together a sample project that replicates the error as you have requested and can send it to your developer.  Do you have a preferred means of file transfer?

- Chris
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #8 - Apr 23rd, 2010 at 2:58pm
Print Post  
Please email it to support@mindfusion.eu.
  
Back to top
 
IP Logged
 
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #9 - Apr 23rd, 2010 at 3:56pm
Print Post  
Stoyan,

I have emailed my .vb designer, resx and code files to the address you provided under the subject "Example Project That Illustrates DiagramLinkCollection Modified Error".  Please let me know if your developer discovers anything.

- Chris
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #10 - Apr 23rd, 2010 at 4:37pm
Print Post  
Remove the ByRef keyword from the methods' argument definitions and it will work. ByRef arguments are used to return values to the caller, and in this case when GetAnchorIndex returns it assigns the current value of "node" to InletLink.Destination. The Destination setter places the link at the end of the destination's IncomingLinks collection and so it gets modified.

I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
chrism
YaBB Newbies
*
Offline


I love YaBB 1G - SP1!

Posts: 10
Joined: Mar 11th, 2010
Re: DiagramLinkCollection Modified On Nested Loopi
Reply #11 - Apr 23rd, 2010 at 7:00pm
Print Post  
Stoyan,

Changing the ByRef to ByVal did fix the problem. Thank you for all the help. I always thought passing an object as an argument was ByRef regardless if it is defined as ByVal because objects are of the reference type. Also, interestingly enough, I've found that keeping the node argument in the GetAnchorIndex() function as ByRef and locally dimensioning a shape node in the calling routine as such

Code
Select All
Dim Destination as MindFusion.Diagramming.ShapeNode = InletLink.Destination 



and passing Destination (rather than InletLink.Destination) as the argument to GetAnchorIndex() does not modify the link collection even though Destination is a pointer to the memory address for the InletLink.Destination property. I guess I still have more to learn.

Thanks again.

- Chris
  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1
Send TopicPrint