// This method converts view state information stored within the ViewStateManager node back as // attached properties on corresponding activity nodes. // It is called when workflow definition is being deserialized from a string. // inputReader - Nodestream that may have all view state information in the ViewStateManager node at the end of workflow definition. // The reader is positioned at the begining of the workflow definition. // idManager - This component issues running sequence numbers for IdRef. // viewStateManager - (output) ViewStateManager object instance deserialized from the workflow definition. // Result - Node stream positioned at the begining of the workflow definition with view state related information // appearing as attached properties on activities. The ViewStateManager nodes are removed from the stream. // Implementation logic: // 1. Scan the input nodestream for ViewStateManager node. // 2. If ViewStateManager node is found, store Id and corresponding attached property nodes // in viewStateInfo dictionary. Otherwise return early. // 3. Walk activity nodes in the workflow definition and apply viewstate related attached properties (from // viewStateInfo dictionary) to each node. // 4. If multiple activities have same IdRef values then corresponding viewstate related attached properties // (from viewStateInfo dictionary) are applied to the first of those activities. The other activities with duplicate // IdRef values do not get view state information. public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputReader, ViewStateIdManager idManager, out Dictionary<string, SourceLocation> viewStateSourceLocationMap) { int idRefLineNumber = 0; int idRefLinePosition = 0; bool shouldWriteIdRefEndMember = false; XamlReader retVal = null; // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream. XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); // Dictionary containing Ids and corresponding viewstate related // attached property nodes. Populated by StripViewStateElement method. Dictionary<string, XamlNodeList> viewStateInfo = null; XamlReader workflowDefinition = StripViewStateElement(inputReader, out viewStateInfo, out viewStateSourceLocationMap); // This is used to keep track of duplicate IdRefs in the workflow definition. HashSet<string> idRefsSeen = new HashSet<string>(); // If the inputReader did not have a ViewStateManager node (4.0 format) // return early. if (viewStateInfo == null) { retVal = workflowDefinition; } else { // Stack to track StartObject/GetObject and EndObject nodes. Stack<Frame> stack = new Stack<Frame>(); // Output node list. XamlNodeList mergedNodeList = new XamlNodeList(workflowDefinition.SchemaContext); bool inIdRefMember = false; using (XamlWriter mergedNodeWriter = mergedNodeList.Writer) { IXamlLineInfo lineInfo = workflowDefinition as IXamlLineInfo; IXamlLineInfoConsumer lineInfoComsumer = mergedNodeWriter as IXamlLineInfoConsumer; bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (workflowDefinition.Read()) { bool skipWritingWorkflowDefinition = false; switch (workflowDefinition.NodeType) { case XamlNodeType.StartObject: stack.Push(new Frame { Type = workflowDefinition.Type }); break; case XamlNodeType.GetObject: stack.Push(new Frame { Type = null }); break; case XamlNodeType.StartMember: // Track when the reader enters IdRef. Skip writing the start // node to the output nodelist until we check for duplicates. if (workflowDefinition.Member.Equals(idRefMember)) { inIdRefMember = true; skipWritingWorkflowDefinition = true; if (shouldPassLineInfo) { idRefLineNumber = lineInfo.LineNumber; idRefLinePosition = lineInfo.LinePosition; } } break; case XamlNodeType.Value: if (inIdRefMember) { string idRef = workflowDefinition.Value as string; if (!string.IsNullOrWhiteSpace(idRef)) { // If IdRef value is a duplicate then do not associate it with // the stack frame (top of stack == activity node with IdRef member on it). if (idRefsSeen.Contains(idRef)) { stack.Peek().IdRef = null; } // If the IdRef value is unique then associate it with the // stack frame and also write its value into the output nodestream. else { stack.Peek().IdRef = idRef; idManager.UpdateMap(idRef); idRefsSeen.Add(idRef); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition); } mergedNodeWriter.WriteStartMember(idRefMember); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteValue(idRef); shouldWriteIdRefEndMember = true; } } // Don't need to write IdRef value into the output // nodestream. If the value was valid, it would have been written above. skipWritingWorkflowDefinition = true; } break; case XamlNodeType.EndMember: // Exit IdRef node. Skip writing the EndMember node, we would have done // it as part of reading the IdRef value. if (inIdRefMember) { inIdRefMember = false; skipWritingWorkflowDefinition = true; if (shouldWriteIdRefEndMember) { shouldWriteIdRefEndMember = false; if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteEndMember(); } } break; case XamlNodeType.EndObject: Frame frameObject = stack.Pop(); // Before we exit the end of an object, check if it had IdRef // associated with it. If it did, look-up viewStateInfo for viewstate // related attached property nodes and add them to the output nodelist. if (!string.IsNullOrWhiteSpace(frameObject.IdRef)) { XamlNodeList viewStateNodeList; if (viewStateInfo.TryGetValue(frameObject.IdRef, out viewStateNodeList)) { XamlReader viewStateReader = viewStateNodeList.GetReader(); IXamlLineInfo viewStateLineInfo = viewStateReader as IXamlLineInfo; bool viewStateShouldPassLineInfo = viewStateLineInfo != null && viewStateLineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (viewStateReader.Read()) { if (viewStateShouldPassLineInfo) { lineInfoComsumer.SetLineInfo(viewStateLineInfo.LineNumber, viewStateLineInfo.LinePosition); } mergedNodeWriter.WriteNode(viewStateReader); } } } break; } if (!skipWritingWorkflowDefinition) { if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteNode(workflowDefinition); } } } retVal = mergedNodeList.GetReader(); } return retVal; }
// This method collects view state attached properties and generates a Xaml node stream // with all view state information appearing within the ViewStateManager node. // It is called when workflow definition is being serialized to string. // inputReader - Nodestream with view state information as attached properties on the activity nodes. // The reader is positioned at the begining of the workflow definition. // idManager - This component issues running sequence numbers for IdRef. // Result - Node stream positioned at the begining of the workflow definition with a // ViewStateManager node containing all view state information. // Implementation logic: // 1. Scan the input nodestream Objects for attached properties that need to be converted (VirtualizedContainerService.HintSize and WorkflowViewStateService.ViewState). // 2. If the Object had a IdRef value then use it otherwise generate a new value. // 3. Store idRef value and corresponding viewstate related attached property nodes (from step 1) // in the viewStateInfo dictionary. // 4. Use the viewStateInfo dictionary to generate ViewStateManager node which is then inserted // into the end of output nodestream. public static XamlReader ConvertAttachedPropertiesToViewState(XamlObjectReader inputReader, ViewStateIdManager idManager) { // Stack to track StartObject/GetObject and EndObject nodes. Stack<Frame> stack = new Stack<Frame>(); XamlMember viewStateManager = new XamlMember(ViewStateManager, GetViewStateManager, SetViewStateManager, inputReader.SchemaContext); XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); // Xaml member corresponding to x:Class property of the workflow definition. Used to find x:Class value in the node stream. XamlMember activityBuilderName = new XamlMember(typeof(ActivityBuilder).GetProperty("Name"), inputReader.SchemaContext); string activityBuilderTypeName = typeof(ActivityBuilder).Name; // Dictionary to keep track of IdRefs and corresponding viewstate related // attached property nodes. Dictionary<string, XamlNodeList> viewStateInfo = new Dictionary<string, XamlNodeList>(); // Output node list XamlNodeList workflowDefinition = new XamlNodeList(inputReader.SchemaContext); using (XamlWriter workflowDefinitionWriter = workflowDefinition.Writer) { bool design2010NamespaceFound = false; bool inIdRefMember = false; bool inxClassMember = false; bool skipWritingWorkflowDefinition = false; bool skipReadingWorkflowDefinition = false; string xClassName = null; while (skipReadingWorkflowDefinition || inputReader.Read()) { skipWritingWorkflowDefinition = false; skipReadingWorkflowDefinition = false; switch (inputReader.NodeType) { case XamlNodeType.NamespaceDeclaration: if (inputReader.Namespace.Namespace.Equals(NameSpaces.Design2010, StringComparison.Ordinal)) { design2010NamespaceFound = true; } break; case XamlNodeType.StartObject: // Save the Xaml type and clr object on the stack frame. These are used later to generate // IdRef values and attaching the same to the clr object. stack.Push(new Frame() { Type = inputReader.Type, InstanceObject = inputReader.Instance }); // If the design2010 namespace was not found add the namespace node // before the start object is written out. if (!design2010NamespaceFound) { workflowDefinitionWriter.WriteNamespace(new NamespaceDeclaration(NameSpaces.Design2010, NameSpaces.Design2010Prefix)); design2010NamespaceFound = true; } break; case XamlNodeType.GetObject: // Push an empty frame to balance the Pop operation when the EndObject node // is encountered. stack.Push(new Frame() { Type = null }); break; case XamlNodeType.StartMember: // Track when we enter IdRef member so that we can save its value. if (inputReader.Member.Equals(idRefMember)) { inIdRefMember = true; } // Track when we enter x:Class member so that we can save its value. else if (inputReader.Member.Equals(activityBuilderName)) { inxClassMember = true; } // Start of VirtualizedContainerService.HintSize or WorkflowViewStateService.ViewState property. else if (IsAttachablePropertyForConvert(inputReader)) { // The top of stack here corresponds to the activity on which // the above properties are attached. if (stack.Peek().AttachedPropertyNodes == null) { stack.Peek().AttachedPropertyNodes = new XamlNodeList(inputReader.SchemaContext); } // Write the attached property's xaml nodes into the stack. XamlReader subTreeReader = inputReader.ReadSubtree(); XamlWriter attachedPropertyWriter = stack.Peek().AttachedPropertyNodes.Writer; while (subTreeReader.Read()) { attachedPropertyWriter.WriteNode(subTreeReader); } // The subtree reader loop put us at the begining of the next node in the input stream. // So skip reading/writing it out just yet. skipReadingWorkflowDefinition = true; skipWritingWorkflowDefinition = true; } break; case XamlNodeType.Value: // Read and save IdRef/x:Class member values. // Also update idManager to keep track of prefixes and ids seen. if (inIdRefMember) { string idRef = inputReader.Value as string; stack.Peek().IdRef = idRef; idManager.UpdateMap(idRef); } else if (inxClassMember) { xClassName = inputReader.Value as string; idManager.UpdateMap(xClassName); } break; case XamlNodeType.EndMember: // Exit IdRef/x:Class member state. if (inIdRefMember) { inIdRefMember = false; } else if (inxClassMember) { inxClassMember = false; } break; case XamlNodeType.EndObject: // Remove an item from the stack because we encountered the end of an object definition. Frame frameObject = stack.Pop(); // If the object had (viewstate related) attached properties we need to save them // into the viewStateInfo dictionary. if (frameObject.AttachedPropertyNodes != null) { frameObject.AttachedPropertyNodes.Writer.Close(); // If the object didn't have IdRef, generate a new one. if (string.IsNullOrWhiteSpace(frameObject.IdRef)) { // Use the object type name (or x:Class value) to generate a new id. if (frameObject.Type != null) { string prefix = frameObject.Type.Name; if (frameObject.Type.UnderlyingType != null) { prefix = frameObject.Type.UnderlyingType.Name; } if (string.CompareOrdinal(prefix, activityBuilderTypeName) == 0 && !string.IsNullOrWhiteSpace(xClassName)) { frameObject.IdRef = idManager.GetNewId(xClassName); } else { frameObject.IdRef = idManager.GetNewId(prefix); } } else //Fallback to generating a guid value. { frameObject.IdRef = Guid.NewGuid().ToString(); } // Since we didn't see a IdRef on this object, insert the generated // viewstate id into the output Xaml node-stream. workflowDefinitionWriter.WriteStartMember(idRefMember); workflowDefinitionWriter.WriteValue(frameObject.IdRef); workflowDefinitionWriter.WriteEndMember(); // Save the generated idRef on the corresponding clr object as well. if (frameObject.InstanceObject != null) { WorkflowViewState.SetIdRef(frameObject.InstanceObject, frameObject.IdRef); } } viewStateInfo[frameObject.IdRef] = frameObject.AttachedPropertyNodes; } // We're at the end of input nodestream and have collected data in viewStateInfo // so we need to create and insert the ViewStateManager nodes into the output nodestream. if (stack.Count == 0 && viewStateInfo.Count > 0) { XamlNodeList viewStateManagerNodeList = CreateViewStateManagerNodeList(viewStateInfo, inputReader.SchemaContext); XamlReader viewStateManagerNodeReader = viewStateManagerNodeList.GetReader(); // Insert the ViewStateManager nodes into the output node stream. workflowDefinitionWriter.WriteStartMember(viewStateManager); while (viewStateManagerNodeReader.Read()) { workflowDefinitionWriter.WriteNode(viewStateManagerNodeReader); } workflowDefinitionWriter.WriteEndMember(); // viewStateManager } break; } if (!skipWritingWorkflowDefinition) { workflowDefinitionWriter.WriteNode(inputReader); } } } return workflowDefinition.GetReader(); }
public WorkflowDesigner() { // create our perf trace provider first this.perfEventProvider = new DesignerPerfEventProvider(); this.idManager = new ViewStateIdManager(); this.context = new EditingContext(); this.ModelSearchService = new ModelSearchServiceImpl(this); this.context.Items.SetValue(new ReadOnlyState { IsReadOnly = false }); this.view = new Grid(); this.view.Focusable = false; //add the resource dictionary to application resource so every component could reference it if (Application.Current == null) { //create an application if it doesn't exist, make sure it will not shutdown after windows being shut down Application app = new Application(); app.ShutdownMode = ShutdownMode.OnExplicitShutdown; } Fx.Assert(Application.Current != null, "Application and resources must be there"); Application.Current.Resources.MergedDictionaries.Add(WorkflowDesignerColors.FontAndColorResources); Application.Current.Resources.MergedDictionaries.Add(WorkflowDesignerIcons.IconResourceDictionary); AttachedPropertiesService propertiesService = new AttachedPropertiesService(); this.context.Services.Publish(typeof(AttachedPropertiesService), propertiesService); undoEngine = new UndoEngine(context); this.context.Services.Publish(typeof(UndoEngine), undoEngine); undoEngine.UndoCompleted += new EventHandler<UndoUnitEventArgs>(OnUndoCompleted); this.context.Services.Publish<ValidationService>(this.ValidationService); this.context.Services.Publish<ObjectReferenceService>(this.ObjectReferenceService); this.context.Services.Publish<DesignerPerfEventProvider>(this.perfEventProvider); this.context.Services.Publish<FeatureManager>(new FeatureManager(this.context)); this.context.Services.Publish<DesignerConfigurationService>(new DesignerConfigurationService()); this.context.Services.Subscribe<ICommandService>((s) => { const string addinTypeName = "Microsoft.VisualStudio.Activities.AddIn.WorkflowDesignerAddIn"; if (s != null && s.GetType().FullName.Equals(addinTypeName)) { DesignerConfigurationService service = this.context.Services.GetService<DesignerConfigurationService>(); if (service != null) { service.WorkflowDesignerHostId = WorkflowDesignerHostId.Dev10; } } }); this.context.Services.Subscribe<IVSSqmService>((service) => { const string serviceTypeName = "Microsoft.VisualStudio.Activities.AddIn.VSSqmService"; if (service != null && service.GetType().FullName.Equals(serviceTypeName)) { DesignerConfigurationService configurationService = this.context.Services.GetService<DesignerConfigurationService>(); if (configurationService != null) { configurationService.WorkflowDesignerHostId = WorkflowDesignerHostId.Dev11; } } }); this.Context.Items.Subscribe<ErrorItem>(delegate(ErrorItem errorItem) { ErrorView errorView = new ErrorView(); errorView.Message = errorItem.Message; errorView.Details = errorItem.Details; errorView.Context = this.Context; // Clear views this.view.Children.Clear(); this.view.Children.Add(errorView); if (this.outlineView != null) { this.outlineView.Children.Clear(); } } ); this.context.Items.Subscribe<ReadOnlyState>(new SubscribeContextCallback<ReadOnlyState>(OnReadonlyStateChanged)); this.context.Services.Subscribe<IXamlLoadErrorService>(s => this.xamlLoadErrorService = s); this.PreviewLoad += NamespaceSettingsHandler.PreviewLoadRoot; this.view.Loaded += (s, e) => { //when view is loaded, check if user did provide his own WindowHelperService - if not, provide a default one if (!this.context.Services.Contains<WindowHelperService>()) { IntPtr hWND = IntPtr.Zero; Window ownerWindow = Window.GetWindow(this.view); if (null != ownerWindow) { WindowInteropHelper helper = new WindowInteropHelper(ownerWindow); hWND = helper.Handle; } this.Context.Services.Publish<WindowHelperService>(new WindowHelperService(hWND)); } WindowHelperService whs = this.context.Services.GetService<WindowHelperService>(); whs.View = this.view; //check if workflow command extension item is available - if not, provide default one if (!this.context.Items.Contains<WorkflowCommandExtensionItem>()) { WorkflowCommandExtensionItem item = new WorkflowCommandExtensionItem(new DefaultCommandExtensionCallback()); this.context.Items.SetValue(item); } ComponentDispatcher.EnterThreadModal += new EventHandler(ComponentDispatcher_EnterThreadModal); ComponentDispatcher.LeaveThreadModal += new EventHandler(ComponentDispatcher_LeaveThreadModal); }; this.view.Unloaded += (s, e) => { ComponentDispatcher.EnterThreadModal -= new EventHandler(ComponentDispatcher_EnterThreadModal); ComponentDispatcher.LeaveThreadModal -= new EventHandler(ComponentDispatcher_LeaveThreadModal); }; this.view.IsKeyboardFocusWithinChanged += (s, e) => { // The ModelTreeManager is null when there is an active ErrorItem. // We have nothing to write to text in this case. if (this.modelTreeManager != null && (bool)e.NewValue == false) { if ((FocusManager.GetFocusedElement(this.view) as TextBox) != null) { FocusManager.SetFocusedElement(this.view, null); this.NotifyModelChanged(); } } }; }
// This method collects view state attached properties and generates a Xaml node stream // with all view state information appearing within the ViewStateManager node. // It is called when workflow definition is being serialized to string. // inputReader - Nodestream with view state information as attached properties on the activity nodes. // The reader is positioned at the begining of the workflow definition. // idManager - This component issues running sequence numbers for IdRef. // Result - Node stream positioned at the begining of the workflow definition with a // ViewStateManager node containing all view state information. // Implementation logic: // 1. Scan the input nodestream Objects for attached properties that need to be converted (VirtualizedContainerService.HintSize and WorkflowViewStateService.ViewState). // 2. If the Object had a IdRef value then use it otherwise generate a new value. // 3. Store idRef value and corresponding viewstate related attached property nodes (from step 1) // in the viewStateInfo dictionary. // 4. Use the viewStateInfo dictionary to generate ViewStateManager node which is then inserted // into the end of output nodestream. public static XamlReader ConvertAttachedPropertiesToViewState(XamlObjectReader inputReader, ViewStateIdManager idManager) { // Stack to track StartObject/GetObject and EndObject nodes. Stack <Frame> stack = new Stack <Frame>(); XamlMember viewStateManager = new XamlMember(ViewStateManager, GetViewStateManager, SetViewStateManager, inputReader.SchemaContext); XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); // Xaml member corresponding to x:Class property of the workflow definition. Used to find x:Class value in the node stream. XamlMember activityBuilderName = new XamlMember(typeof(ActivityBuilder).GetProperty("Name"), inputReader.SchemaContext); string activityBuilderTypeName = typeof(ActivityBuilder).Name; // Dictionary to keep track of IdRefs and corresponding viewstate related // attached property nodes. Dictionary <string, XamlNodeList> viewStateInfo = new Dictionary <string, XamlNodeList>(); // Output node list XamlNodeList workflowDefinition = new XamlNodeList(inputReader.SchemaContext); using (XamlWriter workflowDefinitionWriter = workflowDefinition.Writer) { bool design2010NamespaceFound = false; bool inIdRefMember = false; bool inxClassMember = false; bool skipWritingWorkflowDefinition = false; bool skipReadingWorkflowDefinition = false; string xClassName = null; while (skipReadingWorkflowDefinition || inputReader.Read()) { skipWritingWorkflowDefinition = false; skipReadingWorkflowDefinition = false; switch (inputReader.NodeType) { case XamlNodeType.NamespaceDeclaration: if (inputReader.Namespace.Namespace.Equals(NameSpaces.Design2010, StringComparison.Ordinal)) { design2010NamespaceFound = true; } break; case XamlNodeType.StartObject: // Save the Xaml type and clr object on the stack frame. These are used later to generate // IdRef values and attaching the same to the clr object. stack.Push(new Frame() { Type = inputReader.Type, InstanceObject = inputReader.Instance }); // If the design2010 namespace was not found add the namespace node // before the start object is written out. if (!design2010NamespaceFound) { workflowDefinitionWriter.WriteNamespace(new NamespaceDeclaration(NameSpaces.Design2010, NameSpaces.Design2010Prefix)); design2010NamespaceFound = true; } break; case XamlNodeType.GetObject: // Push an empty frame to balance the Pop operation when the EndObject node // is encountered. stack.Push(new Frame() { Type = null }); break; case XamlNodeType.StartMember: // Track when we enter IdRef member so that we can save its value. if (inputReader.Member.Equals(idRefMember)) { inIdRefMember = true; } // Track when we enter x:Class member so that we can save its value. else if (inputReader.Member.Equals(activityBuilderName)) { inxClassMember = true; } // Start of VirtualizedContainerService.HintSize or WorkflowViewStateService.ViewState property. else if (IsAttachablePropertyForConvert(inputReader)) { // The top of stack here corresponds to the activity on which // the above properties are attached. if (stack.Peek().AttachedPropertyNodes == null) { stack.Peek().AttachedPropertyNodes = new XamlNodeList(inputReader.SchemaContext); } // Write the attached property's xaml nodes into the stack. XamlReader subTreeReader = inputReader.ReadSubtree(); XamlWriter attachedPropertyWriter = stack.Peek().AttachedPropertyNodes.Writer; while (subTreeReader.Read()) { attachedPropertyWriter.WriteNode(subTreeReader); } // The subtree reader loop put us at the begining of the next node in the input stream. // So skip reading/writing it out just yet. skipReadingWorkflowDefinition = true; skipWritingWorkflowDefinition = true; } break; case XamlNodeType.Value: // Read and save IdRef/x:Class member values. // Also update idManager to keep track of prefixes and ids seen. if (inIdRefMember) { string idRef = inputReader.Value as string; stack.Peek().IdRef = idRef; idManager.UpdateMap(idRef); } else if (inxClassMember) { xClassName = inputReader.Value as string; idManager.UpdateMap(xClassName); } break; case XamlNodeType.EndMember: // Exit IdRef/x:Class member state. if (inIdRefMember) { inIdRefMember = false; } else if (inxClassMember) { inxClassMember = false; } break; case XamlNodeType.EndObject: // Remove an item from the stack because we encountered the end of an object definition. Frame frameObject = stack.Pop(); // If the object had (viewstate related) attached properties we need to save them // into the viewStateInfo dictionary. if (frameObject.AttachedPropertyNodes != null) { frameObject.AttachedPropertyNodes.Writer.Close(); // If the object didn't have IdRef, generate a new one. if (string.IsNullOrWhiteSpace(frameObject.IdRef)) { // Use the object type name (or x:Class value) to generate a new id. if (frameObject.Type != null) { string prefix = frameObject.Type.Name; if (frameObject.Type.UnderlyingType != null) { prefix = frameObject.Type.UnderlyingType.Name; } if (string.CompareOrdinal(prefix, activityBuilderTypeName) == 0 && !string.IsNullOrWhiteSpace(xClassName)) { frameObject.IdRef = idManager.GetNewId(xClassName); } else { frameObject.IdRef = idManager.GetNewId(prefix); } } else //Fallback to generating a guid value. { frameObject.IdRef = Guid.NewGuid().ToString(); } // Since we didn't see a IdRef on this object, insert the generated // viewstate id into the output Xaml node-stream. workflowDefinitionWriter.WriteStartMember(idRefMember); workflowDefinitionWriter.WriteValue(frameObject.IdRef); workflowDefinitionWriter.WriteEndMember(); // Save the generated idRef on the corresponding clr object as well. if (frameObject.InstanceObject != null) { WorkflowViewState.SetIdRef(frameObject.InstanceObject, frameObject.IdRef); } } viewStateInfo[frameObject.IdRef] = frameObject.AttachedPropertyNodes; } // We're at the end of input nodestream and have collected data in viewStateInfo // so we need to create and insert the ViewStateManager nodes into the output nodestream. if (stack.Count == 0 && viewStateInfo.Count > 0) { XamlNodeList viewStateManagerNodeList = CreateViewStateManagerNodeList(viewStateInfo, inputReader.SchemaContext); XamlReader viewStateManagerNodeReader = viewStateManagerNodeList.GetReader(); // Insert the ViewStateManager nodes into the output node stream. workflowDefinitionWriter.WriteStartMember(viewStateManager); while (viewStateManagerNodeReader.Read()) { workflowDefinitionWriter.WriteNode(viewStateManagerNodeReader); } workflowDefinitionWriter.WriteEndMember(); // viewStateManager } break; } if (!skipWritingWorkflowDefinition) { workflowDefinitionWriter.WriteNode(inputReader); } } } return(workflowDefinition.GetReader()); }
// This method converts view state information stored within the ViewStateManager node back as // attached properties on corresponding activity nodes. // It is called when workflow definition is being deserialized from a string. // inputReader - Nodestream that may have all view state information in the ViewStateManager node at the end of workflow definition. // The reader is positioned at the begining of the workflow definition. // idManager - This component issues running sequence numbers for IdRef. // viewStateManager - (output) ViewStateManager object instance deserialized from the workflow definition. // Result - Node stream positioned at the begining of the workflow definition with view state related information // appearing as attached properties on activities. The ViewStateManager nodes are removed from the stream. // Implementation logic: // 1. Scan the input nodestream for ViewStateManager node. // 2. If ViewStateManager node is found, store Id and corresponding attached property nodes // in viewStateInfo dictionary. Otherwise return early. // 3. Walk activity nodes in the workflow definition and apply viewstate related attached properties (from // viewStateInfo dictionary) to each node. // 4. If multiple activities have same IdRef values then corresponding viewstate related attached properties // (from viewStateInfo dictionary) are applied to the first of those activities. The other activities with duplicate // IdRef values do not get view state information. public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputReader, ViewStateIdManager idManager, out Dictionary <string, SourceLocation> viewStateSourceLocationMap) { int idRefLineNumber = 0; int idRefLinePosition = 0; bool shouldWriteIdRefEndMember = false; XamlReader retVal = null; // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream. XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); // These are used to ignore the IdRef members that are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder attached property. // 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 // duplicate IdRef value. This causes problems with activity designers that depend on the ViewStateManager data to correctly display the workflow // on the WorkflowDesigner canvas. XamlMember originalDefinitionMember = new XamlMember(DynamicUpdateOriginalDefinitionMemberName, GetOriginalDefinition, SetOriginalDefinition, inputReader.SchemaContext); XamlMember originalActivityBuilderMember = new XamlMember(DynamicUpdateOriginalActivityBuilderMemberName, GetOriginalActivityBuilder, SetOriginalActivityBuilder, inputReader.SchemaContext); // insideOriginalDefintion gets set to true when we find a "StartMember" node for either of the above two attached properties. // originalDefintionMemberCount gets incremented if we find any "StartMember" and insideOriginalDefinition is true. // originalDefintionMemberCount gets decremented if we find any "EndMember" and insideOriginalDefintion is true. // insideOriginalDefintion gets set to false when we find an "EndMember" and originalDefinitionMemberCount gets decremented to 0. // If insideOriginalDefintion is true when we find an "IdRef" member, we do NOT add that IdRef to the idRefsSeen HashSet to avoid // duplicates being defined by the IdRefs inside of the OriginalDefinition attached properties. bool insideOriginalDefinition = false; int originalDefinitionMemberCount = 0; // Dictionary containing Ids and corresponding viewstate related // attached property nodes. Populated by StripViewStateElement method. Dictionary <string, XamlNodeList> viewStateInfo = null; XamlReader workflowDefinition = StripViewStateElement(inputReader, out viewStateInfo, out viewStateSourceLocationMap); // This is used to keep track of duplicate IdRefs in the workflow definition. HashSet <string> idRefsSeen = new HashSet <string>(); // If the inputReader did not have a ViewStateManager node (4.0 format) // return early. if (viewStateInfo == null) { retVal = workflowDefinition; } else { // Stack to track StartObject/GetObject and EndObject nodes. Stack <Frame> stack = new Stack <Frame>(); // Output node list. XamlNodeList mergedNodeList = new XamlNodeList(workflowDefinition.SchemaContext); bool inIdRefMember = false; using (XamlWriter mergedNodeWriter = mergedNodeList.Writer) { IXamlLineInfo lineInfo = workflowDefinition as IXamlLineInfo; IXamlLineInfoConsumer lineInfoComsumer = mergedNodeWriter as IXamlLineInfoConsumer; bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (workflowDefinition.Read()) { bool skipWritingWorkflowDefinition = false; switch (workflowDefinition.NodeType) { case XamlNodeType.StartObject: stack.Push(new Frame { Type = workflowDefinition.Type }); break; case XamlNodeType.GetObject: stack.Push(new Frame { Type = null }); break; case XamlNodeType.StartMember: // If we find a StartMember for DynamicUpdateInfo.OriginalDefinition or OriginalActivityBuilder, remember that we are // inside one of those. We don't want to "remember" IdRef values in the idRefsSeen HashSet while inside these attached properties. if (workflowDefinition.Member.Equals(originalDefinitionMember) || workflowDefinition.Member.Equals(originalActivityBuilderMember)) { insideOriginalDefinition = true; } if (insideOriginalDefinition) { originalDefinitionMemberCount++; } // Track when the reader enters IdRef. Skip writing the start // node to the output nodelist until we check for duplicates. else if (workflowDefinition.Member.Equals(idRefMember)) { inIdRefMember = true; skipWritingWorkflowDefinition = true; if (shouldPassLineInfo) { idRefLineNumber = lineInfo.LineNumber; idRefLinePosition = lineInfo.LinePosition; } } break; case XamlNodeType.Value: if (inIdRefMember) { // We don't want to deal with the IdRef if we are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder // attached property. if (!insideOriginalDefinition) { string idRef = workflowDefinition.Value as string; if (!string.IsNullOrWhiteSpace(idRef)) { // If IdRef value is a duplicate then do not associate it with // the stack frame (top of stack == activity node with IdRef member on it). if (idRefsSeen.Contains(idRef)) { stack.Peek().IdRef = null; } // If the IdRef value is unique then associate it with the // stack frame and also write its value into the output nodestream. else { stack.Peek().IdRef = idRef; idManager.UpdateMap(idRef); idRefsSeen.Add(idRef); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition); } mergedNodeWriter.WriteStartMember(idRefMember); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteValue(idRef); shouldWriteIdRefEndMember = true; } } } // Don't need to write IdRef value into the output // nodestream. If the value was valid, it would have been written above. skipWritingWorkflowDefinition = true; } break; case XamlNodeType.EndMember: // If we are inside an OriginalDefinition/OriginalActivityBuilder attached property, // decrement the count and if it goes to zero, set insideOriginalDefintion to false // because we just encountered the EndMember for it. if (insideOriginalDefinition) { originalDefinitionMemberCount--; if (originalDefinitionMemberCount == 0) { insideOriginalDefinition = false; } } // Exit IdRef node. Skip writing the EndMember node, we would have done // it as part of reading the IdRef value. if (inIdRefMember && !insideOriginalDefinition) { inIdRefMember = false; skipWritingWorkflowDefinition = true; if (shouldWriteIdRefEndMember) { shouldWriteIdRefEndMember = false; if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteEndMember(); } } break; case XamlNodeType.EndObject: Frame frameObject = stack.Pop(); // Before we exit the end of an object, check if it had IdRef // associated with it. If it did, look-up viewStateInfo for viewstate // related attached property nodes and add them to the output nodelist. if (!string.IsNullOrWhiteSpace(frameObject.IdRef)) { XamlNodeList viewStateNodeList; if (viewStateInfo.TryGetValue(frameObject.IdRef, out viewStateNodeList)) { XamlReader viewStateReader = viewStateNodeList.GetReader(); IXamlLineInfo viewStateLineInfo = viewStateReader as IXamlLineInfo; bool viewStateShouldPassLineInfo = viewStateLineInfo != null && viewStateLineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (viewStateReader.Read()) { if (viewStateShouldPassLineInfo) { lineInfoComsumer.SetLineInfo(viewStateLineInfo.LineNumber, viewStateLineInfo.LinePosition); } mergedNodeWriter.WriteNode(viewStateReader); } } } break; } if (!skipWritingWorkflowDefinition) { if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteNode(workflowDefinition); } } } retVal = mergedNodeList.GetReader(); } return(retVal); }
// This method converts view state information stored within the ViewStateManager node back as // attached properties on corresponding activity nodes. // It is called when workflow definition is being deserialized from a string. // inputReader - Nodestream that may have all view state information in the ViewStateManager node at the end of workflow definition. // The reader is positioned at the begining of the workflow definition. // idManager - This component issues running sequence numbers for IdRef. // viewStateManager - (output) ViewStateManager object instance deserialized from the workflow definition. // Result - Node stream positioned at the begining of the workflow definition with view state related information // appearing as attached properties on activities. The ViewStateManager nodes are removed from the stream. // Implementation logic: // 1. Scan the input nodestream for ViewStateManager node. // 2. If ViewStateManager node is found, store Id and corresponding attached property nodes // in viewStateInfo dictionary. Otherwise return early. // 3. Walk activity nodes in the workflow definition and apply viewstate related attached properties (from // viewStateInfo dictionary) to each node. // 4. If multiple activities have same IdRef values then corresponding viewstate related attached properties // (from viewStateInfo dictionary) are applied to the first of those activities. The other activities with duplicate // IdRef values do not get view state information. public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputReader, ViewStateIdManager idManager, out Dictionary <string, SourceLocation> viewStateSourceLocationMap) { int idRefLineNumber = 0; int idRefLinePosition = 0; bool shouldWriteIdRefEndMember = false; XamlReader retVal = null; // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream. XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); // Dictionary containing Ids and corresponding viewstate related // attached property nodes. Populated by StripViewStateElement method. Dictionary <string, XamlNodeList> viewStateInfo = null; XamlReader workflowDefinition = StripViewStateElement(inputReader, out viewStateInfo, out viewStateSourceLocationMap); // This is used to keep track of duplicate IdRefs in the workflow definition. HashSet <string> idRefsSeen = new HashSet <string>(); // If the inputReader did not have a ViewStateManager node (4.0 format) // return early. if (viewStateInfo == null) { retVal = workflowDefinition; } else { // Stack to track StartObject/GetObject and EndObject nodes. Stack <Frame> stack = new Stack <Frame>(); // Output node list. XamlNodeList mergedNodeList = new XamlNodeList(workflowDefinition.SchemaContext); bool inIdRefMember = false; using (XamlWriter mergedNodeWriter = mergedNodeList.Writer) { IXamlLineInfo lineInfo = workflowDefinition as IXamlLineInfo; IXamlLineInfoConsumer lineInfoComsumer = mergedNodeWriter as IXamlLineInfoConsumer; bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (workflowDefinition.Read()) { bool skipWritingWorkflowDefinition = false; switch (workflowDefinition.NodeType) { case XamlNodeType.StartObject: stack.Push(new Frame { Type = workflowDefinition.Type }); break; case XamlNodeType.GetObject: stack.Push(new Frame { Type = null }); break; case XamlNodeType.StartMember: // Track when the reader enters IdRef. Skip writing the start // node to the output nodelist until we check for duplicates. if (workflowDefinition.Member.Equals(idRefMember)) { inIdRefMember = true; skipWritingWorkflowDefinition = true; if (shouldPassLineInfo) { idRefLineNumber = lineInfo.LineNumber; idRefLinePosition = lineInfo.LinePosition; } } break; case XamlNodeType.Value: if (inIdRefMember) { string idRef = workflowDefinition.Value as string; if (!string.IsNullOrWhiteSpace(idRef)) { // If IdRef value is a duplicate then do not associate it with // the stack frame (top of stack == activity node with IdRef member on it). if (idRefsSeen.Contains(idRef)) { stack.Peek().IdRef = null; } // If the IdRef value is unique then associate it with the // stack frame and also write its value into the output nodestream. else { stack.Peek().IdRef = idRef; idManager.UpdateMap(idRef); idRefsSeen.Add(idRef); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition); } mergedNodeWriter.WriteStartMember(idRefMember); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteValue(idRef); shouldWriteIdRefEndMember = true; } } // Don't need to write IdRef value into the output // nodestream. If the value was valid, it would have been written above. skipWritingWorkflowDefinition = true; } break; case XamlNodeType.EndMember: // Exit IdRef node. Skip writing the EndMember node, we would have done // it as part of reading the IdRef value. if (inIdRefMember) { inIdRefMember = false; skipWritingWorkflowDefinition = true; if (shouldWriteIdRefEndMember) { shouldWriteIdRefEndMember = false; if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteEndMember(); } } break; case XamlNodeType.EndObject: Frame frameObject = stack.Pop(); // Before we exit the end of an object, check if it had IdRef // associated with it. If it did, look-up viewStateInfo for viewstate // related attached property nodes and add them to the output nodelist. if (!string.IsNullOrWhiteSpace(frameObject.IdRef)) { XamlNodeList viewStateNodeList; if (viewStateInfo.TryGetValue(frameObject.IdRef, out viewStateNodeList)) { XamlReader viewStateReader = viewStateNodeList.GetReader(); IXamlLineInfo viewStateLineInfo = viewStateReader as IXamlLineInfo; bool viewStateShouldPassLineInfo = viewStateLineInfo != null && viewStateLineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (viewStateReader.Read()) { if (viewStateShouldPassLineInfo) { lineInfoComsumer.SetLineInfo(viewStateLineInfo.LineNumber, viewStateLineInfo.LinePosition); } mergedNodeWriter.WriteNode(viewStateReader); } } } break; } if (!skipWritingWorkflowDefinition) { if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteNode(workflowDefinition); } } } retVal = mergedNodeList.GetReader(); } return(retVal); }
// This method converts view state information stored within the ViewStateManager node back as // attached properties on corresponding activity nodes. // It is called when workflow definition is being deserialized from a string. // inputReader - Nodestream that may have all view state information in the ViewStateManager node at the end of workflow definition. // The reader is positioned at the begining of the workflow definition. // idManager - This component issues running sequence numbers for IdRef. // viewStateManager - (output) ViewStateManager object instance deserialized from the workflow definition. // Result - Node stream positioned at the begining of the workflow definition with view state related information // appearing as attached properties on activities. The ViewStateManager nodes are removed from the stream. // Implementation logic: // 1. Scan the input nodestream for ViewStateManager node. // 2. If ViewStateManager node is found, store Id and corresponding attached property nodes // in viewStateInfo dictionary. Otherwise return early. // 3. Walk activity nodes in the workflow definition and apply viewstate related attached properties (from // viewStateInfo dictionary) to each node. // 4. If multiple activities have same IdRef values then corresponding viewstate related attached properties // (from viewStateInfo dictionary) are applied to the first of those activities. The other activities with duplicate // IdRef values do not get view state information. public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputReader, ViewStateIdManager idManager, out Dictionary<string, SourceLocation> viewStateSourceLocationMap) { int idRefLineNumber = 0; int idRefLinePosition = 0; bool shouldWriteIdRefEndMember = false; XamlReader retVal = null; // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream. XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); // These are used to ignore the IdRef members that are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder attached property. // 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 // duplicate IdRef value. This causes problems with activity designers that depend on the ViewStateManager data to correctly display the workflow // on the WorkflowDesigner canvas. XamlMember originalDefinitionMember = new XamlMember(DynamicUpdateOriginalDefinitionMemberName, GetOriginalDefinition, SetOriginalDefinition, inputReader.SchemaContext); XamlMember originalActivityBuilderMember = new XamlMember(DynamicUpdateOriginalActivityBuilderMemberName, GetOriginalActivityBuilder, SetOriginalActivityBuilder, inputReader.SchemaContext); // insideOriginalDefintion gets set to true when we find a "StartMember" node for either of the above two attached properties. // originalDefintionMemberCount gets incremented if we find any "StartMember" and insideOriginalDefinition is true. // originalDefintionMemberCount gets decremented if we find any "EndMember" and insideOriginalDefintion is true. // insideOriginalDefintion gets set to false when we find an "EndMember" and originalDefinitionMemberCount gets decremented to 0. // If insideOriginalDefintion is true when we find an "IdRef" member, we do NOT add that IdRef to the idRefsSeen HashSet to avoid // duplicates being defined by the IdRefs inside of the OriginalDefinition attached properties. bool insideOriginalDefinition = false; int originalDefinitionMemberCount = 0; // Dictionary containing Ids and corresponding viewstate related // attached property nodes. Populated by StripViewStateElement method. Dictionary<string, XamlNodeList> viewStateInfo = null; XamlReader workflowDefinition = StripViewStateElement(inputReader, out viewStateInfo, out viewStateSourceLocationMap); // This is used to keep track of duplicate IdRefs in the workflow definition. HashSet<string> idRefsSeen = new HashSet<string>(); // If the inputReader did not have a ViewStateManager node (4.0 format) // return early. if (viewStateInfo == null) { retVal = workflowDefinition; } else { // Stack to track StartObject/GetObject and EndObject nodes. Stack<Frame> stack = new Stack<Frame>(); // Output node list. XamlNodeList mergedNodeList = new XamlNodeList(workflowDefinition.SchemaContext); bool inIdRefMember = false; using (XamlWriter mergedNodeWriter = mergedNodeList.Writer) { IXamlLineInfo lineInfo = workflowDefinition as IXamlLineInfo; IXamlLineInfoConsumer lineInfoComsumer = mergedNodeWriter as IXamlLineInfoConsumer; bool shouldPassLineInfo = lineInfo != null && lineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (workflowDefinition.Read()) { bool skipWritingWorkflowDefinition = false; switch (workflowDefinition.NodeType) { case XamlNodeType.StartObject: stack.Push(new Frame { Type = workflowDefinition.Type }); break; case XamlNodeType.GetObject: stack.Push(new Frame { Type = null }); break; case XamlNodeType.StartMember: // If we find a StartMember for DynamicUpdateInfo.OriginalDefinition or OriginalActivityBuilder, remember that we are // inside one of those. We don't want to "remember" IdRef values in the idRefsSeen HashSet while inside these attached properties. if (workflowDefinition.Member.Equals(originalDefinitionMember) || workflowDefinition.Member.Equals(originalActivityBuilderMember)) { insideOriginalDefinition = true; } if (insideOriginalDefinition) { originalDefinitionMemberCount++; } // Track when the reader enters IdRef. Skip writing the start // node to the output nodelist until we check for duplicates. else if (workflowDefinition.Member.Equals(idRefMember)) { inIdRefMember = true; skipWritingWorkflowDefinition = true; if (shouldPassLineInfo) { idRefLineNumber = lineInfo.LineNumber; idRefLinePosition = lineInfo.LinePosition; } } break; case XamlNodeType.Value: if (inIdRefMember) { // We don't want to deal with the IdRef if we are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder // attached property. if (!insideOriginalDefinition) { string idRef = workflowDefinition.Value as string; if (!string.IsNullOrWhiteSpace(idRef)) { // If IdRef value is a duplicate then do not associate it with // the stack frame (top of stack == activity node with IdRef member on it). if (idRefsSeen.Contains(idRef)) { stack.Peek().IdRef = null; } // If the IdRef value is unique then associate it with the // stack frame and also write its value into the output nodestream. else { stack.Peek().IdRef = idRef; idManager.UpdateMap(idRef); idRefsSeen.Add(idRef); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition); } mergedNodeWriter.WriteStartMember(idRefMember); if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteValue(idRef); shouldWriteIdRefEndMember = true; } } } // Don't need to write IdRef value into the output // nodestream. If the value was valid, it would have been written above. skipWritingWorkflowDefinition = true; } break; case XamlNodeType.EndMember: // If we are inside an OriginalDefinition/OriginalActivityBuilder attached property, // decrement the count and if it goes to zero, set insideOriginalDefintion to false // because we just encountered the EndMember for it. if (insideOriginalDefinition) { originalDefinitionMemberCount--; if (originalDefinitionMemberCount == 0) { insideOriginalDefinition = false; } } // Exit IdRef node. Skip writing the EndMember node, we would have done // it as part of reading the IdRef value. if (inIdRefMember && !insideOriginalDefinition) { inIdRefMember = false; skipWritingWorkflowDefinition = true; if (shouldWriteIdRefEndMember) { shouldWriteIdRefEndMember = false; if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteEndMember(); } } break; case XamlNodeType.EndObject: Frame frameObject = stack.Pop(); // Before we exit the end of an object, check if it had IdRef // associated with it. If it did, look-up viewStateInfo for viewstate // related attached property nodes and add them to the output nodelist. if (!string.IsNullOrWhiteSpace(frameObject.IdRef)) { XamlNodeList viewStateNodeList; if (viewStateInfo.TryGetValue(frameObject.IdRef, out viewStateNodeList)) { XamlReader viewStateReader = viewStateNodeList.GetReader(); IXamlLineInfo viewStateLineInfo = viewStateReader as IXamlLineInfo; bool viewStateShouldPassLineInfo = viewStateLineInfo != null && viewStateLineInfo.HasLineInfo && lineInfoComsumer != null && lineInfoComsumer.ShouldProvideLineInfo; while (viewStateReader.Read()) { if (viewStateShouldPassLineInfo) { lineInfoComsumer.SetLineInfo(viewStateLineInfo.LineNumber, viewStateLineInfo.LinePosition); } mergedNodeWriter.WriteNode(viewStateReader); } } } break; } if (!skipWritingWorkflowDefinition) { if (shouldPassLineInfo) { lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } mergedNodeWriter.WriteNode(workflowDefinition); } } } retVal = mergedNodeList.GetReader(); } return retVal; }