-
Notifications
You must be signed in to change notification settings - Fork 3
/
WorkflowDesignerHost.xaml.cs
498 lines (406 loc) · 20.2 KB
/
WorkflowDesignerHost.xaml.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Activities.Presentation;
using System.Activities.Presentation.Debug;
using System.Activities.Core.Presentation;
using System.Activities;
using System.Activities.Debugger;
using System.Activities.Presentation.Services;
using System.Activities.Tracking;
using System.Windows.Threading;
using System.Threading;
using System.Windows;
using System.Activities.XamlIntegration;
using System.Activities.Presentation.Model;
using System.Activities.Presentation.View;
using System.Windows.Input;
using System.Activities.Presentation.Toolbox;
using System.Activities.Statements;
using System.Reflection;
using System.Linq;
using System.ServiceModel.Activities;
using System.ServiceModel.Activities.Presentation.Factories;
using WindowsWorkflowVisualDebugger.Activity;
namespace Business.WorkflowDebugger
{
/// <summary>
/// Interaction logic for WorkflowDesignerHost.xaml
/// </summary>
public partial class WorkflowDesignerHost : UserControl
{
public string WorkflowFilePath = "Workflow.xaml";
public WorkflowDesigner WorkflowDesigner { get; set; }
public IDesignerDebugView DebuggerService { get; set; }
TextBox tx;
Dictionary<int, SourceLocation> textLineToSourceLocationMap;
// SourceLocation class is used to identify specific location in a target source code file
Dictionary<object, SourceLocation> designerSourceLocationMapping = new Dictionary<object, SourceLocation>();
Dictionary<object, SourceLocation> wfElementToSourceLocationMap = null;
AutoResetEvent resumeRuntimeFromHost = null;
List<SourceLocation> breakpointList = new List<SourceLocation>();
public WorkflowDesignerHost()
{
InitializeComponent();
this.RegisterMetadata();
this.AddWorkflowDesigner();
this.AddToolBox();
this.AddPropertyInspector();
this.AddTrackingTextbox();
}
private void RegisterMetadata()
{
(new DesignerMetadata()).Register();
}
public void AddWorkflowDesigner()
{
this.WorkflowDesigner = new WorkflowDesigner();
this.WorkflowDesigner.Context.Services.GetService<DesignerConfigurationService>().AutoConnectEnabled = true;
this.WorkflowDesigner.Context.Services.GetService<DesignerConfigurationService>().PanModeEnabled = true;
this.WorkflowDesigner.Context.Services.GetService<DesignerConfigurationService>().MultipleItemsDragDropEnabled = true;
this.WorkflowDesigner.Context.Services.GetService<DesignerConfigurationService>().LoadingFromUntrustedSourceEnabled = true;
this.WorkflowDesigner.Context.Services.GetService<DesignerConfigurationService>().TargetFrameworkName = new System.Runtime.Versioning.FrameworkName(".NETFramework", new Version(4, 5));
this.DebuggerService = this.WorkflowDesigner.DebugManagerView;
this.WorkflowDesigner.Load(WorkflowFilePath);
this.workflowDesignerPanel.Content = this.WorkflowDesigner.View;
//Updating the mapping between Model item and Source Location as soon as you load the designer so that BP setting can re-use that information from the DesignerSourceLocationMapping.
wfElementToSourceLocationMap = UpdateSourceLocationMappingInDebuggerService();
txtFilename.Text = WorkflowFilePath;
}
private void AddToolBox()
{
ToolboxControl tc = GetToolboxControl();
this.toolboxPanel.Content = tc;
}
private ToolboxControl GetToolboxControl()
{
ToolboxControl toolboxControl = new ToolboxControl();
//var cat = new ToolboxCategory("Standard Activities");
//var assemblies = new List<Assembly>();
//assemblies.Add(typeof(Send).Assembly);
//assemblies.Add(typeof(Delay).Assembly);
//assemblies.Add(typeof(ReceiveAndSendReplyFactory).Assembly);
//var query = from asm in assemblies
// from type in asm.GetTypes()
// where type.IsPublic &&
// !type.IsNested &&
// !type.IsAbstract &&
// !type.ContainsGenericParameters &&
// (typeof(Activity).IsAssignableFrom(type) ||
// typeof(IActivityTemplateFactory).IsAssignableFrom(type))
// orderby type.Name
// select new ToolboxItemWrapper(type);
//query.ToList().ForEach(ti => cat.Add(ti));
//toolboxControl.Categories.Add(cat);
toolboxControl.Categories.Add(new ToolboxCategory("Basic Activities")
{
new ToolboxItemWrapper(typeof(Sequence)),
new ToolboxItemWrapper(typeof(WriteLine)),
new ToolboxItemWrapper(typeof(Assign)),
new ToolboxItemWrapper(typeof(InvokeWebService))
});
toolboxControl.Categories.Add(new ToolboxCategory("Control Flow Activities")
{
new ToolboxItemWrapper(typeof(Flowchart)),
new ToolboxItemWrapper(typeof(FlowSwitch<>)),
new ToolboxItemWrapper(typeof(FlowDecision)),
new ToolboxItemWrapper(typeof(Parallel)),
new ToolboxItemWrapper(typeof(TransactionScope)),
new ToolboxItemWrapper(typeof(While)),
new ToolboxItemWrapper(typeof(DoWhile)),
new ToolboxItemWrapper(typeof(ForEach<>)),
new ToolboxItemWrapper(typeof(ParallelForEach<>)),
new ToolboxItemWrapper(typeof(TryCatch)),
new ToolboxItemWrapper(typeof(Rethrow)),
new ToolboxItemWrapper(typeof(Delay)),
new ToolboxItemWrapper(typeof(If)),
new ToolboxItemWrapper(typeof(Throw))
});
toolboxControl.Categories.Add(new ToolboxCategory("Collection Activities")
{
new ToolboxItemWrapper(typeof(AddToCollection<>)),
new ToolboxItemWrapper(typeof(ClearCollection<>)),
new ToolboxItemWrapper(typeof(RemoveFromCollection<>)),
new ToolboxItemWrapper(typeof(ExistsInCollection<>))
});
toolboxControl.Categories.Add(new ToolboxCategory("Error Handling Activities")
{
new ToolboxItemWrapper(typeof(TransactionScope)),
new ToolboxItemWrapper(typeof(TryCatch)),
new ToolboxItemWrapper(typeof(Rethrow)),
new ToolboxItemWrapper(typeof(Throw))
});
return toolboxControl;
}
private void AddPropertyInspector()
{
if (this.WorkflowDesigner == null)
return;
this.WorkflowPropertyPanel.Content = this.WorkflowDesigner.PropertyInspectorView;
}
//Run the Workflow with the tracking participant
public void RunWorkflow()
{
WorkflowDesigner.Save(WorkflowFilePath);
WorkflowDesigner.Flush();
AddWorkflowDesigner();
WorkflowInvoker instance = new WorkflowInvoker(GetRuntimeExecutionRoot());
resumeRuntimeFromHost = new AutoResetEvent(false);
Dictionary<string, Activity> activityIdToWfElementMap = BuildActivityIdToWfElementMap(wfElementToSourceLocationMap);
#region Set up the Custom Tracking
const String all = "*";
SimulatorTrackingParticipant simTracker = new SimulatorTrackingParticipant()
{
TrackingProfile = new TrackingProfile()
{
Name = "CustomTrackingProfile",
Queries =
{
new CustomTrackingQuery()
{
Name = all,
ActivityName = all
},
new WorkflowInstanceQuery()
{
// Limit workflow instance tracking records for started and completed workflow states
States = { WorkflowInstanceStates.Started, WorkflowInstanceStates.Completed, WorkflowInstanceStates.Idle },
},
new ActivityStateQuery()
{
// Subscribe for track records from all activities for all states
ActivityName = all,
States = { all },
// Extract workflow variables and arguments as a part of the activity tracking record
// VariableName = "*" allows for extraction of all variables in the scope
// of the activity
Variables =
{
{ all }
}
}
}
}
};
simTracker.ActivityIdToWorkflowElementMap = activityIdToWfElementMap;
#endregion
//As the tracking events are received
simTracker.TrackingRecordReceived += (trackingParticpant, trackingEventArgs) =>
{
if (trackingEventArgs.Activity != null)
{
System.Diagnostics.Debug.WriteLine(
String.Format("<+=+=+=+> Activity Tracking Record Received for ActivityId: {0}, record: {1} ",
trackingEventArgs.Activity.Id,
trackingEventArgs.Record
)
);
ShowDebug(wfElementToSourceLocationMap[trackingEventArgs.Activity]);
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, (Action)(() =>
{
//Textbox Updates
tx.AppendText(trackingEventArgs.Activity.Id + " " + trackingEventArgs.Activity.DisplayName + " " + ((ActivityStateRecord)trackingEventArgs.Record).State + "\n");
tx.AppendText("Instance ID: " + ((ActivityStateRecord)trackingEventArgs.Record).InstanceId + "\n");
tx.AppendText("Activity: " + ((ActivityStateRecord)trackingEventArgs.Record).Activity.Name + "\n");
tx.AppendText("Level: " + ((ActivityStateRecord)trackingEventArgs.Record).Level + "\n");
tx.AppendText("Time: " + ((ActivityStateRecord)trackingEventArgs.Record).EventTime + "\n");
tx.AppendText("******************\n");
// textLineToSourceLocationMap.Add(i, wfElementToSourceLocationMap[trackingEventArgs.Activity]);
// i = i + 2;
//Add a sleep so that the debug adornments are visible to the user
System.Threading.Thread.Sleep(500);
}));
}
};
instance.Extensions.Add(simTracker);
ThreadPool.QueueUserWorkItem(new WaitCallback((context) =>
{
//Start the Runtime
instance.Invoke();// new TimeSpan(1,0,0));
//This is to remove the final debug adornment
this.Dispatcher.Invoke(DispatcherPriority.Render
, (Action)(() =>
{
this.WorkflowDesigner.DebugManagerView.CurrentLocation = new SourceLocation(WorkflowFilePath, 1,1,1,10);
}));
}));
}
//Show the Debug Adornment
void ShowDebug(SourceLocation srcLoc)
{
this.Dispatcher.Invoke(DispatcherPriority.Render
, (Action)(() =>
{
this.WorkflowDesigner.DebugManagerView.CurrentLocation = srcLoc;
}));
//Check if this is where any Breakpoint is set
bool isBreakpointHit = false;
foreach (SourceLocation src in breakpointList)
{
if (src.StartLine == srcLoc.StartLine && src.EndLine == srcLoc.EndLine)
{
isBreakpointHit = true;
}
}
if (isBreakpointHit == true)
{
resumeRuntimeFromHost.WaitOne();
}
}
//Provide Debug Adornment on the Activity being executed
void textBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
//string text = this.tx.Text;
//int index = 0;
//int lineClicked = 0;
//while (index < text.Length)
//{
// if (text[index] == '\n')
// lineClicked++;
// if (this.tx.SelectionStart <= index)
// break;
// index++;
//}
//this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
//{
// try
// {
// //Tell Debug Service that the Line Clicked is _______
// this.WorkflowDesigner.DebugManagerView.CurrentLocation = textLineToSourceLocationMap[lineClicked];
// }
// catch (Exception)
// {
// //If the user clicks other than on the tracking records themselves.
// this.WorkflowDesigner.DebugManagerView.CurrentLocation = new SourceLocation("Workflow.xaml", 1, 1, 1, 10);
// }
//}));
}
private Dictionary<string, Activity> BuildActivityIdToWfElementMap(Dictionary<object, SourceLocation> wfElementToSourceLocationMap)
{
Dictionary<string, Activity> map = new Dictionary<string, Activity>();
Activity wfElement;
foreach (object instance in wfElementToSourceLocationMap.Keys)
{
wfElement = instance as Activity;
if (wfElement != null)
{
map.Add(wfElement.Id, wfElement);
}
}
return map;
}
Dictionary<object, SourceLocation> UpdateSourceLocationMappingInDebuggerService()
{
object rootInstance = GetRootInstance();
Dictionary<object, SourceLocation> sourceLocationMapping = new Dictionary<object, SourceLocation>();
if (rootInstance != null)
{
Activity documentRootElement = GetRootWorkflowElement(rootInstance);
SourceLocationProvider.CollectMapping(GetRootRuntimeWorkflowElement(), documentRootElement, sourceLocationMapping,
this.WorkflowDesigner.Context.Items.GetValue<WorkflowFileItem>().LoadedFile);
//Collect the mapping between the Model Item tree and its underlying source location
SourceLocationProvider.CollectMapping(documentRootElement, documentRootElement, designerSourceLocationMapping,
this.WorkflowDesigner.Context.Items.GetValue<WorkflowFileItem>().LoadedFile);
}
// Notify the DebuggerService of the new sourceLocationMapping.
// When rootInstance == null, it'll just reset the mapping.
//DebuggerService debuggerService = debuggerService as DebuggerService;
if (this.DebuggerService != null)
{
((DebuggerService)this.DebuggerService).UpdateSourceLocations(designerSourceLocationMapping);
}
return sourceLocationMapping;
}
# region Helper Methods
object GetRootInstance()
{
ModelService modelService = this.WorkflowDesigner.Context.Services.GetService<ModelService>();
if (modelService != null)
{
return modelService.Root.GetCurrentValue();
}
else
{
return null;
}
}
// Get root WorkflowElement. Currently only handle when the object is ActivitySchemaType or WorkflowElement.
// May return null if it does not know how to get the root activity.
Activity GetRootWorkflowElement(object rootModelObject)
{
System.Diagnostics.Debug.Assert(rootModelObject != null, "Cannot pass null as rootModelObject");
Activity rootWorkflowElement;
IDebuggableWorkflowTree debuggableWorkflowTree = rootModelObject as IDebuggableWorkflowTree;
if (debuggableWorkflowTree != null)
{
rootWorkflowElement = debuggableWorkflowTree.GetWorkflowRoot();
}
else // Loose xaml case.
{
rootWorkflowElement = rootModelObject as Activity;
}
return rootWorkflowElement;
}
Activity GetRuntimeExecutionRoot()
{
Activity root = ActivityXamlServices.Load(WorkflowFilePath);
WorkflowInspectionServices.CacheMetadata(root);
return root;
}
Activity GetRootRuntimeWorkflowElement()
{
Activity root = ActivityXamlServices.Load(WorkflowFilePath);
WorkflowInspectionServices.CacheMetadata(root);
IEnumerator<Activity> enumerator1 = WorkflowInspectionServices.GetActivities(root).GetEnumerator();
//Get the first child of the x:class
enumerator1.MoveNext();
root = enumerator1.Current;
return root;
}
void AddTrackingTextbox()
{
tx = new TextBox();
Grid.SetRow(tx, 1);
this.TrackingRecord.Children.Add(tx);
//For Tracking Records displayed and to check which activity those records corresponds to.
this.tx.SelectionChanged += new RoutedEventHandler(textBox1_SelectionChanged);
textLineToSourceLocationMap = new Dictionary<int, SourceLocation>();
}
#endregion
//Re-hosted debugging - F5/F9 behavior
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.F9)
{
ModelItem mi = this.WorkflowDesigner.Context.Items.GetValue<Selection>().PrimarySelection;
Activity a = mi.GetCurrentValue() as Activity;
if (a != null)
{
SourceLocation srcLoc = designerSourceLocationMapping[a];
if (!breakpointList.Contains(srcLoc))
{
this.WorkflowDesigner.Context.Services.GetService<IDesignerDebugView>().UpdateBreakpoint(srcLoc, BreakpointTypes.Bounded | BreakpointTypes.Enabled);
breakpointList.Add(srcLoc);
}
else
{
this.WorkflowDesigner.Context.Services.GetService<IDesignerDebugView>().UpdateBreakpoint(srcLoc, BreakpointTypes.None);
breakpointList.Remove(srcLoc);
}
}
}
else if (e.Key == Key.F5)
{
resumeRuntimeFromHost.Set();
}
}
private void TabItem_GotFocus_RefreshXamlBox(object sender, RoutedEventArgs e)
{
if (this.WorkflowDesigner.Text != null)
{
this.WorkflowDesigner.Flush(); //save the current state of the workflow to the Test() property
xamlTextBox.Text = this.WorkflowDesigner.Text;
}
}
}
}