@@ -7,6 +7,7 @@ namespace Microsoft.Activities.Presentation.Xaml
77 using System ;
88 using System . Activities ;
99 using System . Activities . Debugger ;
10+ using System . Activities . DynamicUpdate ;
1011 using System . Activities . Presentation . View ;
1112 using System . Activities . Presentation . ViewState ;
1213 using System . Collections . Generic ;
@@ -30,6 +31,18 @@ internal static class ViewStateXamlHelper
3031 XamlDebuggerXmlReader . EndColumnName . MemberName
3132 } ;
3233
34+ // These are used to discover that we have found a DynamicUpdateInfo.OriginalDefintion or OriginalActivityBuilder
35+ // attached property member. We have "hardcoded" the *MemberName" here because DynamicUpdateInfo has the
36+ // AttachableMemberIdentifier properties marked as private. But the DynamicUpdateInfo class itself is public,
37+ // as are the Get and Set methods.
38+ static readonly string DynamicUpdateOriginalDefinitionMemberName = "OriginalDefinition" ;
39+ static readonly MethodInfo GetOriginalDefinition = typeof ( DynamicUpdateInfo ) . GetMethod ( "GetOriginalDefinition" ) ;
40+ static readonly MethodInfo SetOriginalDefinition = typeof ( DynamicUpdateInfo ) . GetMethod ( "SetOriginalDefinition" ) ;
41+
42+ static readonly string DynamicUpdateOriginalActivityBuilderMemberName = "OriginalActivityBuilder" ;
43+ static readonly MethodInfo GetOriginalActivityBuilder = typeof ( DynamicUpdateInfo ) . GetMethod ( "GetOriginalActivityBuilder" ) ;
44+ static readonly MethodInfo SetOriginalActivityBuilder = typeof ( DynamicUpdateInfo ) . GetMethod ( "SetOriginalActivityBuilder" ) ;
45+
3346 // This method collects view state attached properties and generates a Xaml node stream
3447 // with all view state information appearing within the ViewStateManager node.
3548 // It is called when workflow definition is being serialized to string.
@@ -279,6 +292,22 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe
279292 // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream.
280293 XamlMember idRefMember = new XamlMember ( IdRef , GetIdRef , SetIdRef , inputReader . SchemaContext ) ;
281294
295+ // These are used to ignore the IdRef members that are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder attached property.
296+ // We need to ignore these because if we don't, the IdRef values for the objects in the actual workflow defintion will be ignored because of the
297+ // duplicate IdRef value. This causes problems with activity designers that depend on the ViewStateManager data to correctly display the workflow
298+ // on the WorkflowDesigner canvas.
299+ XamlMember originalDefinitionMember = new XamlMember ( DynamicUpdateOriginalDefinitionMemberName , GetOriginalDefinition , SetOriginalDefinition , inputReader . SchemaContext ) ;
300+ XamlMember originalActivityBuilderMember = new XamlMember ( DynamicUpdateOriginalActivityBuilderMemberName , GetOriginalActivityBuilder , SetOriginalActivityBuilder , inputReader . SchemaContext ) ;
301+
302+ // insideOriginalDefintion gets set to true when we find a "StartMember" node for either of the above two attached properties.
303+ // originalDefintionMemberCount gets incremented if we find any "StartMember" and insideOriginalDefinition is true.
304+ // originalDefintionMemberCount gets decremented if we find any "EndMember" and insideOriginalDefintion is true.
305+ // insideOriginalDefintion gets set to false when we find an "EndMember" and originalDefinitionMemberCount gets decremented to 0.
306+ // If insideOriginalDefintion is true when we find an "IdRef" member, we do NOT add that IdRef to the idRefsSeen HashSet to avoid
307+ // duplicates being defined by the IdRefs inside of the OriginalDefinition attached properties.
308+ bool insideOriginalDefinition = false ;
309+ int originalDefinitionMemberCount = 0 ;
310+
282311 // Dictionary containing Ids and corresponding viewstate related
283312 // attached property nodes. Populated by StripViewStateElement method.
284313 Dictionary < string , XamlNodeList > viewStateInfo = null ;
@@ -323,9 +352,21 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe
323352 break ;
324353
325354 case XamlNodeType . StartMember :
355+ // If we find a StartMember for DynamicUpdateInfo.OriginalDefinition or OriginalActivityBuilder, remember that we are
356+ // inside one of those. We don't want to "remember" IdRef values in the idRefsSeen HashSet while inside these attached properties.
357+ if ( workflowDefinition . Member . Equals ( originalDefinitionMember ) || workflowDefinition . Member . Equals ( originalActivityBuilderMember ) )
358+ {
359+ insideOriginalDefinition = true ;
360+ }
361+
362+ if ( insideOriginalDefinition )
363+ {
364+ originalDefinitionMemberCount ++ ;
365+ }
366+
326367 // Track when the reader enters IdRef. Skip writing the start
327368 // node to the output nodelist until we check for duplicates.
328- if ( workflowDefinition . Member . Equals ( idRefMember ) )
369+ else if ( workflowDefinition . Member . Equals ( idRefMember ) )
329370 {
330371 inIdRefMember = true ;
331372 skipWritingWorkflowDefinition = true ;
@@ -341,38 +382,43 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe
341382 case XamlNodeType . Value :
342383 if ( inIdRefMember )
343384 {
344- string idRef = workflowDefinition . Value as string ;
345- if ( ! string . IsNullOrWhiteSpace ( idRef ) )
385+ // We don't want to deal with the IdRef if we are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder
386+ // attached property.
387+ if ( ! insideOriginalDefinition )
346388 {
347- // If IdRef value is a duplicate then do not associate it with
348- // the stack frame (top of stack == activity node with IdRef member on it).
349- if ( idRefsSeen . Contains ( idRef ) )
350- {
351- stack . Peek ( ) . IdRef = null ;
352- }
353- // If the IdRef value is unique then associate it with the
354- // stack frame and also write its value into the output nodestream.
355- else
389+ string idRef = workflowDefinition . Value as string ;
390+ if ( ! string . IsNullOrWhiteSpace ( idRef ) )
356391 {
357- stack . Peek ( ) . IdRef = idRef ;
358- idManager . UpdateMap ( idRef ) ;
359- idRefsSeen . Add ( idRef ) ;
360-
361- if ( shouldPassLineInfo )
392+ // If IdRef value is a duplicate then do not associate it with
393+ // the stack frame (top of stack == activity node with IdRef member on it).
394+ if ( idRefsSeen . Contains ( idRef ) )
362395 {
363- lineInfoComsumer . SetLineInfo ( idRefLineNumber , idRefLinePosition ) ;
396+ stack . Peek ( ) . IdRef = null ;
364397 }
398+ // If the IdRef value is unique then associate it with the
399+ // stack frame and also write its value into the output nodestream.
400+ else
401+ {
402+ stack . Peek ( ) . IdRef = idRef ;
403+ idManager . UpdateMap ( idRef ) ;
404+ idRefsSeen . Add ( idRef ) ;
365405
366- mergedNodeWriter . WriteStartMember ( idRefMember ) ;
406+ if ( shouldPassLineInfo )
407+ {
408+ lineInfoComsumer . SetLineInfo ( idRefLineNumber , idRefLinePosition ) ;
409+ }
367410
368- if ( shouldPassLineInfo )
369- {
370- lineInfoComsumer . SetLineInfo ( lineInfo . LineNumber , lineInfo . LinePosition ) ;
371- }
411+ mergedNodeWriter . WriteStartMember ( idRefMember ) ;
372412
373- mergedNodeWriter . WriteValue ( idRef ) ;
413+ if ( shouldPassLineInfo )
414+ {
415+ lineInfoComsumer . SetLineInfo ( lineInfo . LineNumber , lineInfo . LinePosition ) ;
416+ }
374417
375- shouldWriteIdRefEndMember = true ;
418+ mergedNodeWriter . WriteValue ( idRef ) ;
419+
420+ shouldWriteIdRefEndMember = true ;
421+ }
376422 }
377423 }
378424 // Don't need to write IdRef value into the output
@@ -382,9 +428,21 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe
382428 break ;
383429
384430 case XamlNodeType . EndMember :
431+ // If we are inside an OriginalDefinition/OriginalActivityBuilder attached property,
432+ // decrement the count and if it goes to zero, set insideOriginalDefintion to false
433+ // because we just encountered the EndMember for it.
434+ if ( insideOriginalDefinition )
435+ {
436+ originalDefinitionMemberCount -- ;
437+ if ( originalDefinitionMemberCount == 0 )
438+ {
439+ insideOriginalDefinition = false ;
440+ }
441+ }
442+
385443 // Exit IdRef node. Skip writing the EndMember node, we would have done
386444 // it as part of reading the IdRef value.
387- if ( inIdRefMember )
445+ if ( inIdRefMember && ! insideOriginalDefinition )
388446 {
389447 inIdRefMember = false ;
390448 skipWritingWorkflowDefinition = true ;
@@ -401,6 +459,7 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe
401459 mergedNodeWriter . WriteEndMember ( ) ;
402460 }
403461 }
462+
404463 break ;
405464
406465 case XamlNodeType . EndObject :
@@ -592,6 +651,7 @@ static XamlReader StripViewStateElement(XamlReader inputReader, out Dictionary<s
592651 viewStateSourceLocationMap = null ;
593652 XamlNodeList strippedNodeList = new XamlNodeList ( inputReader . SchemaContext ) ;
594653 XamlMember viewStateManager = new XamlMember ( ViewStateManager , GetViewStateManager , SetViewStateManager , inputReader . SchemaContext ) ;
654+
595655 using ( XamlWriter strippedWriter = strippedNodeList . Writer )
596656 {
597657 IXamlLineInfo lineInfo = inputReader as IXamlLineInfo ;
@@ -618,7 +678,7 @@ static XamlReader StripViewStateElement(XamlReader inputReader, out Dictionary<s
618678
619679 return strippedNodeList . GetReader ( ) ;
620680 }
621-
681+
622682 // This method reads ViewStateManager nodes from the xaml nodestream and outputs that in the
623683 // viewStateInfo dictionary. The input reader is positioned on the ViewStateManagerNode in the nodestream.
624684 static void ReadViewStateInfo ( XamlReader inputReader , out Dictionary < string , XamlNodeList > viewStateInfo , out Dictionary < string , SourceLocation > viewStateSourceLocationMap )
@@ -700,7 +760,11 @@ static void ReadViewState(XamlType viewStateType, XamlReader xamlReader, out str
700760 }
701761 }
702762 }
703- else if ( globalMemberLevel == 1 && ! IsAttachablePropertyForConvert ( xamlReader ) )
763+ // The xamlReader.ReadSubtree and subsequent while loop to get the Id member
764+ // has moved the xamlReader forward to the next member. We need to check to see
765+ // if it is an Attached Property that we care about. If it isn't then we need to
766+ // skip it and not put it in the resulting XamlNodeList.
767+ if ( globalMemberLevel == 1 && ! IsAttachablePropertyForConvert ( xamlReader ) )
704768 {
705769 skippingUnexpectedAttachedProperty = true ;
706770 }
0 commit comments