1 namespace System.Workflow.ComponentModel.Design
8 using System.Resources;
9 using System.Reflection;
10 using System.Diagnostics;
11 using System.Collections;
12 using System.Windows.Forms;
13 using System.ComponentModel;
14 using System.Drawing.Design;
15 using System.Drawing.Imaging;
16 using System.Drawing.Printing;
17 using System.Drawing.Drawing2D;
18 using System.Workflow.Interop;
19 using System.Collections.Generic;
20 using System.Windows.Forms.Design;
21 using System.Security.Permissions;
22 using System.ComponentModel.Design;
23 using System.Runtime.InteropServices;
24 using System.Collections.ObjectModel;
25 using System.Diagnostics.CodeAnalysis;
26 /// What did I change in this file
27 /// 1. Eliminated the layout manager and introduced classes for WorkflowLayout and PrintPreviewLayout
28 /// 2. Eliminated the event syncing of PageSetupData change. We call performlayout on the current designer service whenever the pagesetupdata changes
30 /// Designer Features:
31 /// Selection on click and thru drag rectangle
32 /// Reconfigurable background
34 /// Ensure Visible functionality
39 /// Small memory footprint
40 /// Ability to move around objects using drag drop
41 /// Ability to drop the objects using drag drop
47 /// USE THIS FOR PERFORMANCE TEST: Debug.WriteLine("******Root drawing: " + Convert.ToString((DateTime.Now.Ticks - ticks) / 10000) + "ms");
49 /// Here are some details about the coordinate system,
51 /// Screen CoOrdinate System: Starts at 0,0 of the screen
52 /// Client CoOrdinate System: Starts at 0,0 of the control
53 /// Logical CoOrdinate System: The workflowview supports zooming and scroll, we want to hide this
54 /// complexity from the activity writter and hence whenever we get a coordinate we translate it based
55 /// scroll position, zoom level and layout. This helps us to sheild the activity designers from complexity
56 /// of zooming, scaling and layouting. The designer writters deal with one coordinate system which is unscaled and
62 [ActivityDesignerTheme(typeof(AmbientTheme), Xml = WorkflowView.ThemeXml)]
63 [Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
64 public class WorkflowView : UserControl, IServiceProvider, IMessageFilter
66 #region Theme Initializer XML
67 internal const string ThemeXml =
68 "<AmbientTheme xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/workflow\"" +
69 " ApplyTo=\"System.Workflow.ComponentModel.Design.WorkflowView\"" +
70 " ShowConfigErrors=\"True\"" +
71 " DrawShadow=\"False\"" +
72 " DrawGrayscale=\"False\"" +
73 " DropIndicatorColor=\"0xFF006400\"" +
74 " SelectionForeColor=\"0xFF0000FF\"" +
75 " SelectionPatternColor=\"0xFF606060\"" +
76 " ForeColor=\"0xFF808080\"" +
77 " BackColor=\"0xFFFFFFFF\"" +
78 " ShowGrid=\"False\"" +
79 " GridColor=\"0xFFC0C0C0\"" +
80 " TextQuality=\"Aliased\"" +
81 " DrawRounded=\"True\"" +
82 " ShowDesignerBorder=\"True\"" +
86 #region Members Variables
87 [Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
88 private enum TabButtonIds { MultiPage = 1, Zoom, Pan }
91 private IServiceProvider serviceProvider = null;
93 private ActivityDesigner rootDesigner = null;
96 private float zoomLevel = 1.0f;
97 private int shadowDepth = WorkflowTheme.CurrentTheme.AmbientTheme.ShadowDepth;
100 private List<WorkflowDesignerMessageFilter> stockMessageFilters = new List<WorkflowDesignerMessageFilter>();
101 private List<WorkflowDesignerMessageFilter> customMessageFilters = new List<WorkflowDesignerMessageFilter>();
105 private Bitmap viewPortBitmap = null;
108 private WorkflowToolTip workflowToolTip = null;
110 private CommandSet commandSet = null;
111 private DynamicAction fitAllAction = null;
114 private int prePreviewZoom = 100;
115 private Point prePreviewScroll = Point.Empty;
116 private WorkflowPrintDocument printDocument = null;
119 private WorkflowLayout activeLayout = null;
120 private WorkflowLayout defaultLayout = null;
122 //One time callable delegates
123 private EventHandler layoutEventHandler = null;
124 private EventHandler ensureVisibleEventHandler = null;
126 private Stack<HitTestInfo> messageHitTestContexts = new Stack<HitTestInfo>();
128 private HScrollBar hScrollBar;
129 private VScrollBar vScrollBar;
131 private TabControl toolContainer;
132 private EventHandler idleEventListeners;
133 private EventHandler idleEventHandler;
135 private bool dragDropInProgress;
139 public event EventHandler ZoomChanged;
140 public event EventHandler RootDesignerChanged;
143 #region Constructor and Dispose
144 public WorkflowView()
145 : this(new DesignSurface())
149 public WorkflowView(IServiceProvider serviceProvider)
151 Debug.Assert(serviceProvider != null);
152 if (serviceProvider == null)
153 throw new ArgumentNullException("serviceProvider");
160 SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint | ControlStyles.Selectable | ControlStyles.EnableNotifyMessage, true);
162 this.serviceProvider = serviceProvider;
164 //*****Promote the services which are accessed from other components
165 IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer;
166 if (serviceContainer != null)
168 //Remove any existing designer service if there is any
169 serviceContainer.RemoveService(typeof(WorkflowView));
170 serviceContainer.AddService(typeof(WorkflowView), this);
173 //set the UI Service to be used by themes
174 IUIService uiService = this.serviceProvider.GetService(typeof(IUIService)) as IUIService;
175 if (uiService != null)
176 WorkflowTheme.UIService = uiService;
178 //Make sure that we add scrollbars
179 EnsureScrollBars(new HScrollBar(), new VScrollBar());
181 //Initialize the tooltip shown
182 this.workflowToolTip = new WorkflowToolTip(this);
184 //Sync the global theme change event, which is fired by the theme infrastructure for theme change
185 WorkflowTheme.ThemeChanged += new EventHandler(OnThemeChange);
187 //Create the core message filters
188 PopulateMessageFilters(true);
190 //Set the root designer, note that the dynamic action is dependent on the DynamicActionMessageFilter pushed
191 //when the root is set.
192 RootDesigner = ActivityDesigner.GetSafeRootDesigner(this);
193 this.fitAllAction = CreateDynamicAction();
195 //If the active layout is still null then we will set the default layout as active layout
196 if (this.activeLayout == null || this.defaultLayout == null)
197 ActiveLayout = DefaultLayout = new WorkflowRootLayout(this.serviceProvider);
199 //Create the local command set and update all the commands once
200 IMenuCommandService menuCommandService = GetService(typeof(IMenuCommandService)) as IMenuCommandService;
201 if (menuCommandService != null)
203 this.commandSet = new CommandSet(this);
204 this.commandSet.UpdatePanCommands(true);
207 //Subscribe to selection change
208 ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService;
209 if (selectionService != null)
210 selectionService.SelectionChanged += new EventHandler(OnSelectionChanged);
212 //In case of non VS case we need to pumpin the Keyboard messages, the user control sets
213 //focus to the child controls by default which is a problem so we need to trap the
214 //messages by adding application level message filter, in case of VS this is not required and
215 //the message filter is never called.
216 Application.AddMessageFilter(this);
218 //We make sure that during the construction we dont do perform layouts on idle event
222 protected override void Dispose(bool disposing)
224 //Remove the proffered services
231 Application.RemoveMessageFilter(this);
233 if (this.layoutEventHandler != null)
235 Idle -= this.layoutEventHandler;
236 this.layoutEventHandler = null;
239 if (this.ensureVisibleEventHandler != null)
241 Idle -= this.ensureVisibleEventHandler;
242 this.ensureVisibleEventHandler = null;
245 if (this.idleEventHandler != null)
247 this.idleEventListeners = null;
249 Form host = TopLevelControl as Form;
250 if (!Application.MessageLoop || (host != null && host.Modal))
251 WorkflowTimer.Default.Unsubscribe(this.idleEventHandler);
253 Application.Idle -= this.idleEventHandler;
254 this.idleEventHandler = null;
257 ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService;
258 if (selectionService != null)
259 selectionService.SelectionChanged -= new EventHandler(OnSelectionChanged);
261 //Unsubscribe the theme change
262 WorkflowTheme.ThemeChanged -= new EventHandler(OnThemeChange);
264 //Remove the dynamic action
265 if (this.fitAllAction != null)
267 this.fitAllAction.Dispose();
268 this.fitAllAction = null;
271 if (this.workflowToolTip != null)
273 ((IDisposable)this.workflowToolTip).Dispose();
274 this.workflowToolTip = null;
277 DisposeMessageFilters(false);
278 DisposeMessageFilters(true);
280 //Dispose the layouts
281 this.activeLayout = null;
282 if (this.defaultLayout != null)
284 this.defaultLayout.Dispose();
285 this.defaultLayout = null;
288 //Destroy other resources
289 if (this.viewPortBitmap != null)
291 this.viewPortBitmap.Dispose();
292 this.viewPortBitmap = null;
295 if (this.commandSet != null)
297 this.commandSet.Dispose();
298 this.commandSet = null;
301 HScrollBar.ValueChanged -= new EventHandler(OnScroll);
302 VScrollBar.ValueChanged -= new EventHandler(OnScroll);
304 if (this.toolContainer != null)
306 Controls.Remove(this.toolContainer);
307 this.toolContainer.TabStrip.Tabs.Clear();
308 this.toolContainer.Dispose();
309 this.toolContainer = null;
312 IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer;
313 if (serviceContainer != null)
315 serviceContainer.RemoveService(typeof(WorkflowView));
324 base.Dispose(disposing);
328 #region Public Properties
333 return Convert.ToInt32(this.zoomLevel * 100);
341 if (value < AmbientTheme.MinZoom || value > AmbientTheme.MaxZoom)
342 throw new NotSupportedException(DR.GetString(DR.ZoomLevelException2, AmbientTheme.MinZoom, AmbientTheme.MaxZoom));
344 ScrollBar hScrollBar = HScrollBar;
345 ScrollBar vScrollBar = VScrollBar;
347 if (hScrollBar != null && vScrollBar != null)
349 PointF oldRelativeCenter = Point.Empty;
350 Point oldCenter = new Point(ScrollPosition.X, ScrollPosition.Y);
351 oldRelativeCenter = new PointF((float)oldCenter.X / (float)hScrollBar.Maximum, (float)oldCenter.Y / (float)vScrollBar.Maximum);
353 //recalculate the zoom and scroll range
354 this.zoomLevel = (float)value / 100.0f;
357 //center the view again
358 Point newCenter = new Point((int)((float)hScrollBar.Maximum * oldRelativeCenter.X), (int)((float)vScrollBar.Maximum * oldRelativeCenter.Y));
359 ScrollPosition = new Point(newCenter.X, newCenter.Y);
361 if (this.rootDesigner != null)
362 this.rootDesigner.Location = this.activeLayout.RootDesignerAlignment;
364 InvalidateClientRectangle(Rectangle.Empty);
368 this.activeLayout.Update(null, WorkflowLayout.LayoutUpdateReason.ZoomChanged);
370 //force command refresh
371 //this is to workarond VS not refreshing Zoom drop down when doing area zoom-in
372 IUIService uis = GetService(typeof(IUIService)) as IUIService;
376 //We need to update the zoom commands when the zoom is updated
377 if (this.commandSet != null)
378 this.commandSet.UpdateZoomCommands(true);
385 public ActivityDesigner RootDesigner
389 return this.rootDesigner;
394 if (this.rootDesigner == value)
397 DisposeMessageFilters(false);
399 this.rootDesigner = value;
401 if (this.rootDesigner != null)
403 PopulateMessageFilters(false);
404 ActiveLayout = DefaultLayout = this.rootDesigner.SupportedLayout;
407 OnRootDesignerChanged();
409 base.PerformLayout();
413 public int ShadowDepth
417 return this.shadowDepth;
422 if (value < AmbientTheme.MinShadowDepth || value > AmbientTheme.MaxShadowDepth)
423 throw new NotSupportedException(DR.GetString(DR.ShadowDepthException, AmbientTheme.MinShadowDepth, AmbientTheme.MaxShadowDepth));
425 if (this.shadowDepth == value)
428 this.shadowDepth = value;
429 InvalidateClientRectangle(Rectangle.Empty);
433 public Rectangle ViewPortRectangle
437 return new Rectangle(ScrollPosition, ViewPortSize);
441 public Size ViewPortSize
445 Size viewPortSize = ClientSize;
446 if (HScrollBar.Visible)
447 viewPortSize.Height = Math.Max(0, viewPortSize.Height - HScrollBar.Height);
448 if (VScrollBar.Visible)
449 viewPortSize.Width = Math.Max(0, viewPortSize.Width - VScrollBar.Width);
454 public Point ScrollPosition
458 return new Point(HScrollBar.Value, VScrollBar.Value);
463 ScrollBar hScrollBar = HScrollBar;
464 if (hScrollBar != null)
466 value.X = Math.Min(value.X, hScrollBar.Maximum - hScrollBar.LargeChange + 1);
467 value.X = Math.Max(value.X, hScrollBar.Minimum);
468 hScrollBar.Value = value.X;
471 ScrollBar vScrollBar = VScrollBar;
472 if (vScrollBar != null)
474 value.Y = Math.Min(value.Y, vScrollBar.Maximum - vScrollBar.LargeChange + 1);
475 value.Y = Math.Max(value.Y, vScrollBar.Minimum);
476 vScrollBar.Value = value.Y;
481 public bool PrintPreviewMode
485 return (this.activeLayout == ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout);
490 if (PrintPreviewMode == value)
493 if (value && PrinterSettings.InstalledPrinters.Count == 0)
495 DesignerHelpers.ShowError(this, DR.GetString(DR.ThereIsNoPrinterInstalledErrorMessage));
499 ActiveLayout = (value) ? ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout : DefaultLayout;
501 if (this.commandSet != null)
502 this.commandSet.UpdatePageLayoutCommands(true);
504 if (PrintPreviewMode)
506 this.prePreviewZoom = Zoom;
507 this.prePreviewScroll = ScrollPosition;
512 Zoom = this.prePreviewZoom;
513 ScrollPosition = this.prePreviewScroll;
518 public PrintDocument PrintDocument
522 if (this.printDocument == null)
523 this.printDocument = new WorkflowPrintDocument(this);
525 return this.printDocument;
529 public event EventHandler Idle
533 //Add the listener to our list
534 this.idleEventListeners += value;
536 if (this.idleEventHandler == null)
538 this.idleEventHandler = new EventHandler(OnWorkflowIdle);
540 Form host = TopLevelControl as Form;
541 if (!Application.MessageLoop || (host != null && host.Modal))
542 WorkflowTimer.Default.Subscribe(100, this.idleEventHandler);
544 Application.Idle += this.idleEventHandler;
550 this.idleEventListeners -= value;
552 if (this.idleEventHandler != null && this.idleEventListeners == null)
554 Form host = TopLevelControl as Form;
555 if (host != null && host.Modal)
556 WorkflowTimer.Default.Unsubscribe(this.idleEventHandler);
558 Application.Idle -= this.idleEventHandler;
560 this.idleEventHandler = null;
565 public HScrollBar HScrollBar
569 return this.hScrollBar;
573 public VScrollBar VScrollBar
577 return this.vScrollBar;
581 public bool EnableFitToScreen
585 return (this.fitAllAction != null);
590 if (EnableFitToScreen == value)
595 if (this.fitAllAction == null)
596 this.fitAllAction = CreateDynamicAction();
600 if (this.fitAllAction != null)
602 this.fitAllAction.Dispose();
603 this.fitAllAction = null;
607 InvalidateClientRectangle(Rectangle.Empty);
612 #region Protected Properties
615 #region Private Properties
616 internal bool DragDropInProgress
620 return this.dragDropInProgress;
624 internal bool ShowToolContainer
628 return (this.toolContainer != null);
633 if (ShowToolContainer == value)
642 this.toolContainer = new TabControl(DockStyle.Right, AnchorAlignment.Far);
643 Controls.Add(this.toolContainer);
644 EnsureScrollBars(this.hScrollBar, this.toolContainer.ScrollBar as VScrollBar);
646 string[,] tabButtonInfo = new string[/*Caption Resource ID*/, /*Bitmap Resource ID*/] { { "MultipageLayoutCaption", "MultipageLayout" }, { "ZoomCaption", "Zoom" }, { "PanCaption", "AutoPan" } };
647 for (int i = 0; i < tabButtonInfo.GetLength(0); i++)
649 Bitmap tabImage = DR.GetImage(tabButtonInfo[i, 1]) as Bitmap;
650 string buttonCaption = DR.GetString(tabButtonInfo[i, 0]);
651 this.toolContainer.TabStrip.Tabs.Add(new ItemInfo(i + 1, tabImage, buttonCaption));
654 this.toolContainer.TabStrip.TabChange += new SelectionChangeEventHandler<TabSelectionChangeEventArgs>(OnTabChange);
655 if (this.commandSet != null)
657 this.commandSet.UpdatePageLayoutCommands(true);
658 this.commandSet.UpdateZoomCommands(true);
659 this.commandSet.UpdatePanCommands(true);
664 this.toolContainer.TabStrip.TabChange -= new SelectionChangeEventHandler<TabSelectionChangeEventArgs>(OnTabChange);
665 this.toolContainer.TabStrip.Tabs.Clear();
667 Controls.Remove(this.toolContainer);
668 this.toolContainer.Dispose();
669 this.toolContainer = null;
671 EnsureScrollBars(this.hScrollBar, new VScrollBar());
681 internal HitTestInfo MessageHitTestContext
685 return this.messageHitTestContexts.Peek();
689 internal WorkflowLayout ActiveLayout
693 return this.activeLayout;
698 Debug.Assert(value != null);
700 throw new ArgumentNullException("Layout cannot be null!");
702 Cursor cursor = Cursor.Current;
705 Cursor.Current = Cursors.WaitCursor;
707 this.activeLayout = value;
708 if (this.activeLayout != ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout)
709 DefaultLayout = this.activeLayout;
711 base.PerformLayout();
712 if (this.commandSet != null)
713 this.commandSet.UpdatePageLayoutCommands(true);
717 Cursor.Current = cursor;
722 private WorkflowLayout DefaultLayout
726 if (this.defaultLayout == null)
727 this.defaultLayout = new WorkflowRootLayout(this);
728 return this.defaultLayout;
734 throw new ArgumentNullException(DR.GetString(DR.Error_WorkflowLayoutNull));
736 if (this.defaultLayout == value)
739 if (this.defaultLayout != null)
740 this.defaultLayout.Dispose();
742 this.defaultLayout = value;
746 private float ScaleZoomFactor
750 return (this.zoomLevel * this.activeLayout.Scaling);
755 #region Public Methods
756 public void AddDesignerMessageFilter(WorkflowDesignerMessageFilter designerMessageFilter)
758 if (designerMessageFilter == null)
759 throw new ArgumentNullException("designerMessageFilter");
764 this.customMessageFilters.Insert(0, designerMessageFilter);
765 designerMessageFilter.SetParentView(this);
768 public void RemoveDesignerMessageFilter(WorkflowDesignerMessageFilter designerMessageFilter)
770 if (designerMessageFilter == null)
771 throw new ArgumentNullException("designerMessageFilter");
773 if (this.customMessageFilters.Contains(designerMessageFilter))
778 this.customMessageFilters.Remove(designerMessageFilter);
779 ((IDisposable)designerMessageFilter).Dispose();
783 public void ShowInPlaceToolTip(string toolTipText, Rectangle toolTipRectangle)
785 if (toolTipText == null)
786 throw new ArgumentNullException("toolTipText");
788 if (toolTipRectangle.IsEmpty)
789 throw new ArgumentException(SR.GetString(SR.Error_EmptyToolTipRectangle));
791 this.workflowToolTip.SetText(toolTipText, toolTipRectangle);
794 public void ShowInfoTip(string text)
797 throw new ArgumentNullException("text");
799 this.workflowToolTip.SetText(String.Empty, text);
802 public void ShowInfoTip(string title, string text)
805 throw new ArgumentNullException("title");
808 throw new ArgumentNullException("text");
810 this.workflowToolTip.SetText(title, text);
813 public void EnsureVisible(object selectableObject)
815 if (selectableObject == null)
816 throw new ArgumentNullException("selectableObject");
818 // make sure that all the parents are expanded
819 Activity activity = selectableObject as Activity;
820 while (activity != null)
822 ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(activity);
823 CompositeActivityDesigner parentDesigner = activityDesigner.ParentDesigner;
824 if (parentDesigner != null)
826 if (activityDesigner != null)
827 parentDesigner.EnsureVisibleContainedDesigner(activityDesigner);
828 activity = parentDesigner.Activity;
836 //this is to handle the case when we call ensure visible of a scope which currently has
837 //activity from the secondary flow selected. instead we should always switch to the main flow
838 activity = selectableObject as Activity;
839 if (activity != null)
841 CompositeActivityDesigner compositeDesigner = ActivityDesigner.GetDesigner(activity) as CompositeActivityDesigner;
842 if (compositeDesigner != null)
843 compositeDesigner.EnsureVisibleContainedDesigner(compositeDesigner);
846 PerformLayout(false);
848 if (this.ensureVisibleEventHandler == null)
850 this.ensureVisibleEventHandler = new EventHandler(OnEnsureVisible);
851 Idle += this.ensureVisibleEventHandler;
855 public void PerformLayout(bool immediateUpdate)
859 if (this.layoutEventHandler != null)
861 Idle -= this.layoutEventHandler;
862 this.layoutEventHandler = null;
864 base.PerformLayout(); //invalidate rectangle really cares for the this.layoutEventHandler being null
866 else if (this.layoutEventHandler == null)
868 this.layoutEventHandler = new EventHandler(OnPerformLayout);
869 Idle += this.layoutEventHandler;
873 public void SaveViewState(Stream viewState)
875 if (viewState == null)
876 throw new ArgumentNullException("viewState");
878 IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));
879 if (designerHost == null)
880 throw new Exception(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName));
882 BinaryWriter writer = new BinaryWriter(viewState);
884 // write workflow properties
885 writer.Write(this.PrintPreviewMode);
886 writer.Write(this.Zoom);
889 DesignerHelpers.SerializeDesignerStates(designerHost, writer);
891 // write scroll position
892 writer.Write(this.ScrollPosition.X);
893 writer.Write(this.ScrollPosition.Y);
896 public void LoadViewState(Stream viewState)
898 if (viewState == null)
899 throw new ArgumentNullException("viewState");
901 bool outdated = false;
902 Point scrollPosition = new Point(0, 0);
904 IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));
905 if (designerHost == null)
906 throw new Exception(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName));
908 viewState.Position = 0;
910 BinaryReader reader = new BinaryReader(viewState);
912 // read workflow properties
913 this.PrintPreviewMode = reader.ReadBoolean();
914 this.Zoom = reader.ReadInt32();
918 outdated = DesignerHelpers.DeserializeDesignerStates(designerHost, reader);
920 // we will apply the scrolling only if if there is perfect match
921 // between the components in the workflow and the persisted data.
922 // It might be different if files were updated outside of VS, or if
926 scrollPosition.X = reader.ReadInt32();
927 scrollPosition.Y = reader.ReadInt32();
932 // flush the layout to apply the new settings, this will set the scrollers extents
933 base.PerformLayout();
934 this.ScrollPosition = scrollPosition;
939 /// Changes zoom level on the design surface such that the entire workflow is displayed in the view
941 public void FitToScreenSize()
943 if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height)
945 int newZoom = (int)(100.0f / ActiveLayout.Scaling * Math.Min((float)ViewPortSize.Width / (float)ActiveLayout.Extent.Width, (float)ViewPortSize.Height / (float)ActiveLayout.Extent.Height));
946 Zoom = Math.Min(Math.Max(newZoom, AmbientTheme.MinZoom), AmbientTheme.MaxZoom);
951 /// Sets the zoom level to 100% so that the workflow size is restored to actial workflow size
953 public void FitToWorkflowSize()
960 /// Saves workflow as image to a file based on the format specified
962 /// <param name="imageFile">Path to file where to save the image</param>
963 /// <param name="imageFormat">Format in which to save the image</param>
964 public void SaveWorkflowImage(string imageFile, ImageFormat imageFormat)
966 if (imageFile == null)
967 throw new ArgumentNullException("imageFile");
969 if (imageFormat == null)
970 throw new ArgumentNullException("imageFormat");
972 Bitmap workflowBitmap = TakeWorkflowSnapShot();
973 if (workflowBitmap != null)
975 workflowBitmap.Save(imageFile, imageFormat);
976 workflowBitmap.Dispose();
981 /// Saves workflow as image to a stream based on encoding specified
983 /// <param name="stream">Stream where to save the workflow</param>
984 /// <param name="imageFormat">Format in which to save the image</param>
985 public void SaveWorkflowImage(Stream stream, ImageFormat imageFormat)
988 throw new ArgumentNullException("stream");
990 if (imageFormat == null)
991 throw new ArgumentNullException("imageFormat");
993 Bitmap workflowBitmap = TakeWorkflowSnapShot();
994 if (workflowBitmap != null)
996 workflowBitmap.Save(stream, imageFormat);
997 workflowBitmap.Dispose();
1002 /// Stores the workflow image to clipboard.
1004 public void SaveWorkflowImageToClipboard()
1006 Bitmap workflowBitmap = TakeWorkflowSnapShot();
1007 if (workflowBitmap != null)
1009 Clipboard.SetDataObject(workflowBitmap, true);
1010 workflowBitmap.Dispose();
1015 #region Protected Methods
1017 #region Overridden Methods handling UI events
1019 protected override void OnPaint(PaintEventArgs e)
1023 //We set the highest quality interpolation so that we do not loose the image quality
1024 GraphicsContainer graphicsState = e.Graphics.BeginContainer();
1025 e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
1026 e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
1027 e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
1029 bool takeWorkflowSnapShot = (this.viewPortBitmap == null || this.viewPortBitmap.Size != ViewPortSize);
1030 if (takeWorkflowSnapShot)
1032 if (this.viewPortBitmap != null)
1033 this.viewPortBitmap.Dispose();
1034 this.viewPortBitmap = new Bitmap(Math.Max(1, ViewPortSize.Width), Math.Max(1, ViewPortSize.Height), e.Graphics);
1037 //Create viewport information and take the workflow snapshot before passing on the information to the active layout
1038 ViewPortData viewPortData = new ViewPortData();
1039 viewPortData.LogicalViewPort = ClientRectangleToLogical(new Rectangle(Point.Empty, ViewPortSize));
1040 viewPortData.MemoryBitmap = this.viewPortBitmap;
1041 viewPortData.Scaling = new SizeF(ScaleZoomFactor, ScaleZoomFactor);
1042 viewPortData.Translation = ScrollPosition;
1043 viewPortData.ShadowDepth = new Size(this.shadowDepth, this.shadowDepth);
1044 viewPortData.ViewPortSize = ViewPortSize;
1046 //capture the workflow onto in-memory bitmap
1047 if (this.layoutEventHandler == null || takeWorkflowSnapShot)
1048 WorkflowView.TakeWorkflowSnapShot(this, viewPortData);
1050 //copy workflow from the bitmap onto corresponding pages on the screen
1053 this.activeLayout.OnPaintWorkflow(e, viewPortData);
1055 catch (Exception ex)
1057 //If a layout throws an exception then we will not draw the layout
1059 Debug.WriteLine(ex);
1062 //If any of the message filters throws an exception we continue to draw
1063 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty))
1065 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1069 if (((IWorkflowDesignerMessageSink)filter).OnPaintWorkflowAdornments(e, ViewPortRectangle))
1072 catch (Exception ex)
1074 //Ignore the filter throwing the exception and continue to function
1075 Debug.WriteLine(ex);
1080 e.Graphics.EndContainer(graphicsState);
1082 e.Graphics.FillRectangle(SystemBrushes.Control, new Rectangle(Width - SystemInformation.VerticalScrollBarWidth, Height - SystemInformation.HorizontalScrollBarHeight, SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight));
1085 protected virtual void OnZoomChanged()
1087 if (this.ZoomChanged != null)
1088 this.ZoomChanged(this, EventArgs.Empty);
1091 protected virtual void OnRootDesignerChanged()
1093 if (this.RootDesignerChanged != null)
1094 this.RootDesignerChanged(this, EventArgs.Empty);
1098 #region Mouse Events
1099 protected override void OnMouseDown(MouseEventArgs e)
1101 base.OnMouseDown(e);
1103 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1105 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1107 if (((IWorkflowDesignerMessageSink)filter).OnMouseDown(e))
1113 protected override void OnMouseMove(MouseEventArgs e)
1115 base.OnMouseMove(e);
1117 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1119 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1121 if (((IWorkflowDesignerMessageSink)filter).OnMouseMove(e))
1127 protected override void OnMouseUp(MouseEventArgs e)
1131 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1133 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1135 if (((IWorkflowDesignerMessageSink)filter).OnMouseUp(e))
1141 protected override void OnMouseDoubleClick(MouseEventArgs e)
1143 base.OnMouseDoubleClick(e);
1145 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1147 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1149 if (((IWorkflowDesignerMessageSink)filter).OnMouseDoubleClick(e))
1155 protected override void OnMouseEnter(EventArgs e)
1157 base.OnMouseEnter(e);
1159 Point clientPoint = PointToClient(Control.MousePosition);
1160 MouseEventArgs eventArgs = new MouseEventArgs(Control.MouseButtons, 1, clientPoint.X, clientPoint.Y, 0);
1162 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, eventArgs))
1164 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1166 if (((IWorkflowDesignerMessageSink)filter).OnMouseEnter(eventArgs))
1172 protected override void OnMouseHover(EventArgs e)
1174 base.OnMouseHover(e);
1176 Point clientPoint = PointToClient(Control.MousePosition);
1177 MouseEventArgs eventArgs = new MouseEventArgs(Control.MouseButtons, 1, clientPoint.X, clientPoint.Y, 0);
1179 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, eventArgs))
1181 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1183 if (((IWorkflowDesignerMessageSink)filter).OnMouseHover(eventArgs))
1189 protected override void OnMouseLeave(EventArgs e)
1191 base.OnMouseLeave(e);
1193 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty))
1195 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1197 if (((IWorkflowDesignerMessageSink)filter).OnMouseLeave())
1203 protected override void OnMouseCaptureChanged(EventArgs e)
1205 base.OnMouseCaptureChanged(e);
1207 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty))
1209 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1211 if (((IWorkflowDesignerMessageSink)filter).OnMouseCaptureChanged())
1217 protected override void OnMouseWheel(MouseEventArgs e)
1219 base.OnMouseWheel(e);
1221 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1223 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1225 if (((IWorkflowDesignerMessageSink)filter).OnMouseWheel(e))
1232 #region Keyboard Events
1233 protected override void OnKeyDown(KeyEventArgs e)
1235 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1237 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1239 if (((IWorkflowDesignerMessageSink)filter).OnKeyDown(e))
1248 protected override void OnKeyUp(KeyEventArgs e)
1250 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1252 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1254 if (((IWorkflowDesignerMessageSink)filter).OnKeyUp(e))
1264 #region Layouting Events
1265 protected override void OnLayout(LayoutEventArgs levent)
1267 base.OnLayout(levent);
1269 ScrollBar hScrollBar = HScrollBar;
1270 ScrollBar vScrollBar = VScrollBar;
1272 if (Controls.Contains(hScrollBar))
1273 hScrollBar.Bounds = new Rectangle(0, Math.Max(0, Height - SystemInformation.HorizontalScrollBarHeight), Math.Max(Width - ((vScrollBar.Visible) ? SystemInformation.VerticalScrollBarWidth : 0), 0), SystemInformation.HorizontalScrollBarHeight);
1275 if (Controls.Contains(vScrollBar))
1276 vScrollBar.Bounds = new Rectangle(Math.Max(0, Width - SystemInformation.VerticalScrollBarWidth), 0, SystemInformation.VerticalScrollBarWidth, Math.Max(Height - ((hScrollBar.Visible) ? SystemInformation.HorizontalScrollBarHeight : 0), 0));
1278 if (this.toolContainer != null)
1280 this.toolContainer.Location = new Point(Width - this.toolContainer.Width, 0);
1281 this.toolContainer.Height = Height - ((hScrollBar.Visible) ? hScrollBar.Height : 0);
1284 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, levent))
1286 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1287 ((IWorkflowDesignerMessageSink)filter).OnLayout(levent);
1290 //Layout the designers
1291 using (Graphics graphics = CreateGraphics())
1293 this.activeLayout.Update(graphics, WorkflowLayout.LayoutUpdateReason.LayoutChanged);
1295 if (this.rootDesigner != null)
1296 this.rootDesigner.Location = this.activeLayout.RootDesignerAlignment;
1299 //Update the scroll range and redraw
1300 UpdateScrollRange();
1301 InvalidateClientRectangle(Rectangle.Empty);
1305 #region DragDrop Events
1306 protected override void OnDragEnter(DragEventArgs dragEventArgs)
1308 base.OnDragEnter(dragEventArgs);
1310 this.dragDropInProgress = true;
1312 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs))
1314 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1316 if (((IWorkflowDesignerMessageSink)filter).OnDragEnter(dragEventArgs))
1322 protected override void OnDragOver(DragEventArgs dragEventArgs)
1324 base.OnDragOver(dragEventArgs);
1326 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs))
1328 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1330 if (((IWorkflowDesignerMessageSink)filter).OnDragOver(dragEventArgs))
1336 protected override void OnDragLeave(EventArgs e)
1338 base.OnDragLeave(e);
1340 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty))
1342 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1344 if (((IWorkflowDesignerMessageSink)filter).OnDragLeave())
1349 this.dragDropInProgress = false;
1352 protected override void OnDragDrop(DragEventArgs dragEventArgs)
1354 base.OnDragDrop(dragEventArgs);
1356 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs))
1358 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1360 if (((IWorkflowDesignerMessageSink)filter).OnDragDrop(dragEventArgs))
1365 this.dragDropInProgress = false;
1368 protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent)
1370 base.OnGiveFeedback(gfbevent);
1372 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, gfbevent))
1374 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1376 if (((IWorkflowDesignerMessageSink)filter).OnGiveFeedback(gfbevent))
1382 protected override void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent)
1384 base.OnQueryContinueDrag(qcdevent);
1386 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, qcdevent))
1388 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1390 if (((IWorkflowDesignerMessageSink)filter).OnQueryContinueDrag(qcdevent))
1397 #region General Events
1398 //Handle context menus here reason being it can come from mouse r button click
1399 //or shift+F10, or there might be other keys too
1400 //We need to handle the WndProc and not the OnNotifyMessage because we need to set
1401 //the m.Result to handled (IntPtr.Zero) and dont let the base class see the message at all
1402 //see WinOE #787 "The keyboard "key" to launch the context menu launches the menu at 0,0"
1403 [UIPermission(SecurityAction.Assert, Window = UIPermissionWindow.AllWindows)]
1404 [SuppressMessage("Microsoft.Security", "CA2106", Justification = "This is SecurityCritical, therefore not callable from partial trust code.")]
1405 protected override void WndProc(ref Message m)
1407 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty))
1409 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1411 if (((IWorkflowDesignerMessageSink)filter).ProcessMessage(m))
1415 const int WM_CONTEXTMENU = 0x007B;
1416 if (m.Msg == WM_CONTEXTMENU)
1418 int LParam = (int)m.LParam;
1419 Point location = (LParam != -1) ? new Point(LParam) : Control.MousePosition;
1421 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1423 if (((IWorkflowDesignerMessageSink)filter).OnShowContextMenu(location))
1427 //mark the message handled
1428 m.Result = IntPtr.Zero;
1429 //dont pass the message to the base but return immediatly
1434 if (this.workflowToolTip != null && m.Msg == NativeMethods.WM_NOTIFY)
1435 this.workflowToolTip.RelayParentNotify(ref m);
1439 if (m.Result == IntPtr.Zero)
1440 base.WndProc(ref m);
1444 if (e != CheckoutException.Canceled)
1445 DesignerHelpers.ShowError(this, e);
1449 protected override void OnControlAdded(ControlEventArgs e)
1451 if (e.Control != VScrollBar && e.Control != HScrollBar && e.Control != this.toolContainer)
1452 throw new InvalidOperationException(SR.GetString(SR.Error_InsertingChildControls));
1455 protected override AccessibleObject CreateAccessibilityInstance()
1457 return new WorkflowViewAccessibleObject(this);
1465 #region Private Methods
1466 private void OnWorkflowIdle(object sender, EventArgs e)
1468 if (this.idleEventListeners != null)
1469 this.idleEventListeners(this, e);
1472 private void UpdateLayout()
1474 if (this.layoutEventHandler != null)
1476 PerformLayout(true);
1477 InvalidateClientRectangle(Rectangle.Empty);
1481 internal void OnCommandKey(KeyEventArgs e)
1487 private void OnSelectionChanged(object sender, EventArgs e)
1489 if (this.commandSet != null)
1490 this.commandSet.UpdateCommandSet();
1492 //Make sure that the ensure visible also works when the component is selected
1493 //from property browser dropdown
1494 //Make sure that when there is a selection change using the property browser
1495 //drop down we make sure that the designer associated with component selected by the user in the dropdown
1497 //To enable this functionality please note that selection change is not a good event as it will get
1498 //fired in multiple cases, instead we should add a event in extended ui service which will do this and move
1499 //the following code in the event handler of that event
1502 if (RootDesigner != null && RootDesigner.Activity != null)
1504 ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService;
1505 if (selectionService != null && selectionService.GetComponentSelected(RootDesigner.Activity))
1507 IHelpService helpService = GetService(typeof(IHelpService)) as IHelpService;
1508 if (helpService != null)
1509 helpService.AddContextAttribute("Keyword", RootDesigner.Activity.GetType().FullName, HelpKeywordType.F1Keyword);
1514 private void OnPerformLayout(object sender, EventArgs e)
1516 if (this.layoutEventHandler != null)
1518 Idle -= this.layoutEventHandler;
1519 this.layoutEventHandler = null;
1521 base.PerformLayout();
1525 //Gets the snapshot of the entire workflow
1526 private Bitmap TakeWorkflowSnapShot()
1528 Bitmap bitmap = null;
1529 ActivityDesigner rootDesigner = RootDesigner;
1530 if (rootDesigner != null)
1532 using (Graphics graphics = CreateGraphics())
1534 ViewPortData viewPortData = new ViewPortData();
1535 viewPortData.LogicalViewPort = new Rectangle(Point.Empty, new Size(rootDesigner.Bounds.Width + 2 * DefaultWorkflowLayout.Separator.Width, rootDesigner.Bounds.Height + 2 * DefaultWorkflowLayout.Separator.Height));
1536 viewPortData.MemoryBitmap = new Bitmap(viewPortData.LogicalViewPort.Width, viewPortData.LogicalViewPort.Height, graphics);
1537 viewPortData.Scaling = new SizeF(1, 1);
1538 viewPortData.Translation = Point.Empty;
1539 viewPortData.ShadowDepth = new Size(0, 0);
1540 viewPortData.ViewPortSize = viewPortData.LogicalViewPort.Size;
1541 TakeWorkflowSnapShot(this, viewPortData);
1542 bitmap = viewPortData.MemoryBitmap;
1549 //This function will give snapshot of what is drawn on the screen at any point of time
1550 //It will scale and translate the designers and drawing based on the viewport data
1551 //We need this function in OnPaint and taking snapshot of magnifier bitmap
1552 //At the end of this function; the ViewPortData.MemoryBitmap will contain the bitmap of the
1553 //workflow to be drawn as per layout
1554 internal static void TakeWorkflowSnapShot(WorkflowView workflowView, ViewPortData viewPortData)
1556 //Get the drawing canvas
1557 Bitmap memoryBitmap = viewPortData.MemoryBitmap;
1558 Debug.Assert(memoryBitmap != null);
1560 using (Graphics viewPortGraphics = Graphics.FromImage(memoryBitmap))
1562 //We set the highest quality interpolation so that we do not loose the image quality
1563 viewPortGraphics.SmoothingMode = SmoothingMode.HighQuality;
1564 viewPortGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
1566 using (PaintEventArgs eventArgs = new PaintEventArgs(viewPortGraphics, viewPortData.LogicalViewPort))
1568 workflowView.ActiveLayout.OnPaint(eventArgs, viewPortData);
1571 //Create the scaling matrix
1572 Matrix transformationMatrix = new Matrix();
1573 transformationMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend);
1575 //When we draw on the viewport we draw in scaled and translated.
1576 //So that we minimize the calls to DrawImage
1577 //Make sure that we scale down the logical view port origin in order to take care of scaling factor
1578 //Before we select the transform factor we make sure that logicalviewport origin is scaled down
1579 Point[] logicalViewPortOrigin = new Point[] { viewPortData.LogicalViewPort.Location };
1580 transformationMatrix.TransformPoints(logicalViewPortOrigin);
1582 //For performance improvement and to eliminate one extra DrawImage...we draw the designers on the viewport
1583 //bitmap with visual depth consideration
1584 transformationMatrix.Translate(-logicalViewPortOrigin[0].X + viewPortData.ShadowDepth.Width, -logicalViewPortOrigin[0].Y + viewPortData.ShadowDepth.Height, MatrixOrder.Append);
1586 //Select the transform into viewport graphics.
1587 //Viewport bitmap has the scaled and translated designers which we then map to
1588 //the actual graphics based on page layout
1589 viewPortGraphics.Transform = transformationMatrix;
1591 //Draw the designers on bitmap
1592 if (workflowView.RootDesigner != null)
1594 using (Region clipRegion = new Region())
1595 using (GraphicsPath designerPath = ActivityDesignerPaint.GetDesignerPath(workflowView.RootDesigner, false))
1597 Region oldRegion = viewPortGraphics.Clip;
1599 //First draw the grid and rectangle with the designer clip region
1600 clipRegion.MakeEmpty();
1601 clipRegion.Union(designerPath);
1602 viewPortGraphics.Clip = clipRegion;
1603 AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
1604 viewPortGraphics.FillRectangle(ambientTheme.BackgroundBrush, workflowView.RootDesigner.Bounds);
1605 if (ambientTheme.ShowGrid)
1606 ActivityDesignerPaint.DrawGrid(viewPortGraphics, workflowView.RootDesigner.Bounds);
1607 viewPortGraphics.Clip = oldRegion;
1609 //Then draw the root with clip region extended
1612 using (PaintEventArgs paintEventArgs = new PaintEventArgs(viewPortGraphics, viewPortData.LogicalViewPort))
1614 ((IWorkflowDesignerMessageSink)workflowView.RootDesigner).OnPaint(paintEventArgs, viewPortData.LogicalViewPort);
1619 //Eat the exception thrown in draw
1625 //Draw all the filters
1628 using (PaintEventArgs paintArgs = new PaintEventArgs(viewPortGraphics, workflowView.RootDesigner.Bounds))
1630 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(workflowView, EventArgs.Empty))
1632 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1636 if (((IWorkflowDesignerMessageSink)filter).OnPaint(paintArgs, viewPortData.LogicalViewPort))
1647 viewPortGraphics.Transform = new Matrix();
1649 //Now that we have a bitmap which is bit offseted based visual depth we need to take copy of it
1650 //This is done so as to avoid expensive DrawImage call, what I am assuming here is that time it
1651 //will take to create a new bitmap from an existing one is less expensive in terms of speed than space
1652 //As you just need to copy bitmap bits in memory than to perform expesive Image Drawing operation
1653 if (!viewPortData.ShadowDepth.IsEmpty)
1655 Bitmap temporaryBitmap = new Bitmap(memoryBitmap);
1657 //THEMETODO: WE JUST NEED TO GRAYSCALE THIS, RATHER THAN DRAWING A SHADOW
1658 //Now that we have taken a copy we will draw over the existing bitmap so that we can make it as shadow bitmap
1659 using (Brush shadowDepthBrush = new SolidBrush(Color.FromArgb(220, Color.White)))
1660 viewPortGraphics.FillRectangle(shadowDepthBrush, new Rectangle(Point.Empty, new Size(memoryBitmap.Size.Width - viewPortData.ShadowDepth.Width - 1, memoryBitmap.Size.Height - viewPortData.ShadowDepth.Height - 1)));
1662 //Now make sure that we draw the image from the temporary bitmap with white color set as transparent
1663 //so that we achive the 3D effect
1664 //Make sure that we take into consideration the transparency key
1665 ImageAttributes transparentColorKey = new ImageAttributes();
1666 transparentColorKey.SetColorKey(viewPortData.TransparentColor, viewPortData.TransparentColor, ColorAdjustType.Default);
1667 transparentColorKey.SetColorKey(viewPortData.TransparentColor, viewPortData.TransparentColor, ColorAdjustType.Bitmap);
1668 viewPortGraphics.DrawImage(temporaryBitmap, new Rectangle(-viewPortData.ShadowDepth.Width, -viewPortData.ShadowDepth.Height, memoryBitmap.Width, memoryBitmap.Height), 0, 0, memoryBitmap.Width, memoryBitmap.Height, GraphicsUnit.Pixel, transparentColorKey);
1670 //Now dispose the temporary bitmap
1671 temporaryBitmap.Dispose();
1676 internal void OnThemeChange(object sender, EventArgs e)
1678 ShadowDepth = WorkflowTheme.CurrentTheme.AmbientTheme.ShadowDepth;
1680 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty))
1682 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1686 ((IWorkflowDesignerMessageSink)filter).OnThemeChange();
1688 catch (Exception ex)
1690 Debug.WriteLine(ex);
1695 base.PerformLayout();
1698 private void OnEnsureVisible(object sender, EventArgs e)
1700 if (this.ensureVisibleEventHandler != null)
1702 Idle -= this.ensureVisibleEventHandler;
1703 this.ensureVisibleEventHandler = null;
1706 ISelectionService selectionService = (ISelectionService)GetService(typeof(ISelectionService));
1707 if (selectionService != null && selectionService.SelectionCount > 0)
1709 //We do not want to regenerate a layout event in ensure visible
1710 ArrayList selectedComponents = new ArrayList(selectionService.GetSelectedComponents());
1711 for (int i = selectedComponents.Count - 1; i >= 0; i--)
1713 Rectangle rectangleToMakeVisible = Rectangle.Empty;
1714 if (selectedComponents[i] is Activity)
1716 ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(selectedComponents[i] as Activity);
1717 if (activityDesigner != null)
1719 rectangleToMakeVisible = activityDesigner.Bounds;
1720 rectangleToMakeVisible.Inflate(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize);
1721 rectangleToMakeVisible.Inflate(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize);
1724 else if (selectedComponents[i] is HitTestInfo)
1726 rectangleToMakeVisible = ((HitTestInfo)selectedComponents[i]).Bounds;
1729 if (!rectangleToMakeVisible.IsEmpty)
1730 EnsureVisible(rectangleToMakeVisible);
1735 private void EnsureVisible(Rectangle rect)
1737 Rectangle clientRectangle = ClientRectangleToLogical(new Rectangle(Point.Empty, ViewPortSize));
1739 if (!clientRectangle.Contains(rect.Location) || !clientRectangle.Contains(new Point(rect.Right, rect.Bottom)))
1741 Size scrollDelta = new Size();
1742 if (!clientRectangle.Contains(new Point(rect.Left, clientRectangle.Top)) || !clientRectangle.Contains(new Point(rect.Right, clientRectangle.Top)))
1744 if (rect.Width > clientRectangle.Width)
1745 scrollDelta.Width = (rect.Left + rect.Width / 2) - (clientRectangle.Left + clientRectangle.Width / 2);
1746 else if (rect.Left < clientRectangle.Left)
1747 scrollDelta.Width = (rect.Left - clientRectangle.Left);
1749 scrollDelta.Width = (rect.Right - clientRectangle.Right);
1752 if (!clientRectangle.Contains(new Point(clientRectangle.Left, rect.Top)) || !clientRectangle.Contains(new Point(clientRectangle.Left, rect.Bottom)))
1754 if ((rect.Top < clientRectangle.Top) || (rect.Height > clientRectangle.Height))
1755 scrollDelta.Height = (rect.Top - clientRectangle.Top);
1757 scrollDelta.Height = rect.Bottom - clientRectangle.Bottom;
1760 scrollDelta = LogicalSizeToClient(scrollDelta);
1761 Point scrollPosition = ScrollPosition;
1762 ScrollPosition = new Point(scrollPosition.X + scrollDelta.Width, scrollPosition.Y + scrollDelta.Height);
1766 private void OnScroll(object sender, EventArgs e)
1768 //Lets speedup the scrolling logic
1769 InvalidateClientRectangle(Rectangle.Empty);
1771 ScrollBar scrollBar = sender as ScrollBar;
1772 if (scrollBar != null)
1774 using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e))
1776 foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters)
1780 ((IWorkflowDesignerMessageSink)filter).OnScroll(scrollBar, scrollBar.Value);
1782 catch (Exception ex)
1784 Debug.WriteLine(ex);
1791 private void UpdateScrollRange()
1793 if (ViewPortSize.Width < 0 || ViewPortSize.Height < 0)
1796 Size currentSize = ViewPortSize;
1797 Size maximumScrollSize = LogicalSizeToClient(this.activeLayout.Extent);
1798 Size largeChangeSize = new Size(Math.Min(maximumScrollSize.Width, currentSize.Width), Math.Min(maximumScrollSize.Height, currentSize.Height));
1800 if (hScrollBar.Maximum != maximumScrollSize.Width)
1801 hScrollBar.Maximum = maximumScrollSize.Width;
1802 if (vScrollBar.Maximum != maximumScrollSize.Height)
1803 vScrollBar.Maximum = maximumScrollSize.Height;
1805 if (hScrollBar.LargeChange != largeChangeSize.Width)
1807 hScrollBar.SmallChange = largeChangeSize.Width / 15;
1808 hScrollBar.LargeChange = largeChangeSize.Width + 1;
1810 if (vScrollBar.LargeChange != largeChangeSize.Height)
1812 vScrollBar.SmallChange = largeChangeSize.Height / 15;
1813 vScrollBar.LargeChange = largeChangeSize.Height + 1;
1816 int xMaxScrollPos = maximumScrollSize.Width - hScrollBar.LargeChange;
1817 xMaxScrollPos = (xMaxScrollPos < 0) ? 0 : xMaxScrollPos;
1818 if (hScrollBar.Value > xMaxScrollPos)
1819 hScrollBar.Value = xMaxScrollPos;
1821 int yMaxScrollPos = maximumScrollSize.Height - vScrollBar.LargeChange;
1822 yMaxScrollPos = (yMaxScrollPos < 0) ? 0 : yMaxScrollPos;
1823 if (vScrollBar.Value > yMaxScrollPos)
1824 vScrollBar.Value = yMaxScrollPos;
1826 RefreshDynamicAction();
1828 bool hScrollBarVisible = hScrollBar.Visible;
1829 if (Controls.Contains(hScrollBar))
1830 hScrollBar.Visible = (hScrollBar.Maximum > currentSize.Width);
1832 bool vScrollBarVisible = vScrollBar.Visible;
1833 if (Controls.Contains(vScrollBar))
1834 vScrollBar.Visible = (vScrollBar.Maximum > currentSize.Height);
1836 if (hScrollBarVisible != hScrollBar.Visible || vScrollBar.Visible != vScrollBarVisible)
1838 base.PerformLayout();
1843 private DynamicAction CreateDynamicAction()
1845 DynamicAction fitAllAction = new DynamicAction();
1846 fitAllAction.ButtonSize = DynamicAction.ButtonSizes.Large;
1847 fitAllAction.DockAlignment = DesignerContentAlignment.BottomRight;
1848 fitAllAction.DockMargin = new Size(5, 5);
1850 ActionButton fitallButton = new ActionButton(new Image[] { DR.GetImage(DR.FitToScreen) as Bitmap });
1851 fitallButton.StateChanged += new EventHandler(OnFitToScreen);
1852 fitAllAction.Buttons.Add(fitallButton);
1854 return fitAllAction;
1857 private void RefreshDynamicAction()
1859 DynamicActionMessageFilter dynamicActionFilter = GetService(typeof(DynamicActionMessageFilter)) as DynamicActionMessageFilter;
1860 if (dynamicActionFilter == null || this.fitAllAction == null)
1863 if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height)
1865 //This means we need to show the zoomin icon
1866 this.fitAllAction.Buttons[0].Description = DR.GetString(DR.FitToScreenDescription);
1867 this.fitAllAction.Buttons[0].StateImages = new Bitmap[] { DR.GetImage(DR.FitToScreen) as Bitmap };
1868 dynamicActionFilter.AddAction(this.fitAllAction);
1870 else if (Zoom != 100)
1872 //We need to show zoomout icon
1873 this.fitAllAction.Buttons[0].Description = DR.GetString(DR.FitToWorkflowDescription);
1874 this.fitAllAction.Buttons[0].StateImages = new Bitmap[] { DR.GetImage(DR.FitToWorkflow) as Bitmap };
1875 dynamicActionFilter.AddAction(this.fitAllAction);
1879 //In neither case we remove the action
1880 dynamicActionFilter.RemoveAction(this.fitAllAction);
1881 this.fitAllAction.Buttons[0].State = ActionButton.States.Normal;
1885 private void OnFitToScreen(object sender, EventArgs e)
1887 ActionButton fitallButton = sender as ActionButton;
1888 if (fitallButton == null || fitallButton.State != ActionButton.States.Pressed)
1891 if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height)
1893 else if (Zoom != 100)
1894 FitToWorkflowSize();
1897 private void OnTabChange(object sender, TabSelectionChangeEventArgs e)
1899 if (e.CurrentItem.Identifier == (int)TabButtonIds.MultiPage ||
1900 e.CurrentItem.Identifier == (int)TabButtonIds.Zoom ||
1901 e.CurrentItem.Identifier == (int)TabButtonIds.Pan)
1903 Rectangle buttonRect = e.SelectedTabBounds;
1904 CommandID menuID = null;
1906 if (e.CurrentItem.Identifier == (int)TabButtonIds.MultiPage)
1907 menuID = WorkflowMenuCommands.PageLayoutMenu;
1908 else if (e.CurrentItem.Identifier == (int)TabButtonIds.Zoom)
1909 menuID = WorkflowMenuCommands.ZoomMenu;
1911 menuID = WorkflowMenuCommands.PanMenu;
1913 IMenuCommandService menuCommandService = (IMenuCommandService)GetService(typeof(IMenuCommandService));
1914 if (menuCommandService != null)
1915 menuCommandService.ShowContextMenu(menuID, buttonRect.Right, buttonRect.Top);
1919 private void EnsureScrollBars(HScrollBar newHorizScrollBar, VScrollBar newVertScrollBar)
1925 if (this.hScrollBar != newHorizScrollBar)
1927 if (this.hScrollBar != null)
1929 this.hScrollBar.ValueChanged -= new EventHandler(OnScroll);
1930 if (Controls.Contains(this.hScrollBar))
1931 Controls.Remove(this.hScrollBar);
1934 this.hScrollBar = newHorizScrollBar;
1935 if (this.hScrollBar.Parent == null)
1937 this.hScrollBar.TabStop = false;
1938 Controls.Add(this.hScrollBar);
1942 if (this.vScrollBar != newVertScrollBar)
1944 if (this.vScrollBar != null)
1946 this.vScrollBar.ValueChanged -= new EventHandler(OnScroll);
1947 if (Controls.Contains(this.vScrollBar))
1948 Controls.Remove(this.vScrollBar);
1951 this.vScrollBar = newVertScrollBar;
1952 if (this.vScrollBar.Parent == null)
1954 this.vScrollBar.TabStop = false;
1955 Controls.Add(this.vScrollBar);
1959 this.hScrollBar.ValueChanged += new EventHandler(OnScroll);
1960 this.vScrollBar.ValueChanged += new EventHandler(OnScroll);
1968 private void PopulateMessageFilters(bool stockFilters)
1970 IList<WorkflowDesignerMessageFilter> filters = (stockFilters) ? this.stockMessageFilters : this.customMessageFilters;
1971 Debug.Assert(filters.Count == 0);
1975 filters.Add(new GlyphManager());
1976 filters.Add(new WindowManager());
1980 Debug.Assert(this.rootDesigner != null);
1985 IList customFilters = ((IWorkflowRootDesigner)this.rootDesigner).MessageFilters;
1986 foreach (WorkflowDesignerMessageFilter filter in customFilters)
1987 filters.Add(filter);
1990 foreach (WorkflowDesignerMessageFilter filter in filters)
1991 filter.SetParentView(this);
1994 private void DisposeMessageFilters(bool stockFilters)
1996 List<WorkflowDesignerMessageFilter> filters = (stockFilters) ? this.stockMessageFilters : this.customMessageFilters;
1998 //We dispose all the message filters, this is done by copying because some of the
1999 //message filters might remove other dependent messagefilters
2000 ArrayList clonedFilterList = new ArrayList(filters.ToArray());
2001 foreach (WorkflowDesignerMessageFilter filter in clonedFilterList)
2002 ((IDisposable)filter).Dispose();
2007 #region Coordinate Transformation Functions
2008 public void InvalidateClientRectangle(Rectangle clientRectangle)
2010 if (this.layoutEventHandler == null)
2012 if (!clientRectangle.IsEmpty)
2014 //Inflate the invalidated rectangle. When zoom factor is less than 1; there is a loss of precision
2015 clientRectangle.Inflate(1, 1);
2016 base.Invalidate(clientRectangle);
2025 public void InvalidateLogicalRectangle(Rectangle logicalRectangle)
2027 InvalidateClientRectangle(LogicalRectangleToClient(logicalRectangle));
2030 public Point LogicalPointToScreen(Point logicalPoint)
2032 return PointToScreen(LogicalPointToClient(logicalPoint));
2035 public Point ScreenPointToLogical(Point screenPoint)
2037 return ClientPointToLogical(PointToClient(screenPoint));
2040 public Point LogicalPointToClient(Point logicalPoint)
2042 return LogicalPointToClient(logicalPoint, true);
2045 public Point ClientPointToLogical(Point clientPoint)
2047 return ClientPointToLogical(clientPoint, true);
2050 public Size LogicalSizeToClient(Size logicalSize)
2052 Point[] points = new Point[] { new Point(logicalSize) };
2055 Matrix scalingMatrix = new Matrix();
2056 scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor);
2057 scalingMatrix.TransformPoints(points);
2058 return new Size(points[0]);
2061 public Size ClientSizeToLogical(Size clientSize)
2063 //Scale the size, size scaling does not require translate
2064 Point[] points = new Point[] { new Point(clientSize) };
2065 Matrix scalingMatrix = new Matrix();
2066 scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor);
2067 scalingMatrix.Invert();
2068 scalingMatrix.TransformPoints(points);
2069 scalingMatrix.Invert();
2070 return new Size(points[0]);
2073 public Rectangle LogicalRectangleToClient(Rectangle rectangle)
2075 Debug.Assert(this.activeLayout != null, "active layout should not be null");
2076 Rectangle clientViewPort = (this.activeLayout != null) ? this.activeLayout.MapOutRectangleFromLayout(rectangle) : rectangle;
2080 return new Rectangle(LogicalPointToClient(clientViewPort.Location, false), LogicalSizeToClient(clientViewPort.Size));
2083 public Rectangle ClientRectangleToLogical(Rectangle rectangle)
2085 //We translate the client viewport to logical view port.
2086 //To do this we first get the view port rectangle scale it down
2087 //then translate it to area of page we would be viewing
2088 Rectangle scaledLogicalViewPort = new Rectangle(ClientPointToLogical(rectangle.Location, false), ClientSizeToLogical(rectangle.Size));
2089 return this.activeLayout.MapInRectangleToLayout(scaledLogicalViewPort);
2092 internal bool IsClientPointInActiveLayout(Point clientPoint)
2094 Point logicalPoint = ClientPointToLogical(clientPoint, false);
2095 return this.activeLayout.IsCoOrdInLayout(logicalPoint);
2099 * Client scale is when we transform the coordinate based on zoom and translate it based on scrolling position and layout
2100 * Logical scale is when we transform the coordinate and map it to a flat coordinate system which goes from 0,0 to m,n
2101 * We also consider the ActiveLayout to transform the coordinates.
2103 private Point LogicalPointToClient(Point point, bool mapToLayout)
2106 point = this.activeLayout.MapOutCoOrdFromLayout(point);
2109 Matrix scalingMatrix = new Matrix();
2110 scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor);
2111 Point[] points = new Point[] { point };
2112 scalingMatrix.TransformPoints(points);
2114 //Translate the point
2115 Matrix translateMatrix = new Matrix();
2116 translateMatrix.Translate(-ScrollPosition.X, -ScrollPosition.Y);
2117 translateMatrix.TransformPoints(points);
2121 private Point ClientPointToLogical(Point point, bool mapToLayout)
2123 Point[] points = new Point[] { point };
2125 //Translate the point
2126 Matrix translateMatrix = new Matrix();
2127 translateMatrix.Translate(ScrollPosition.X, ScrollPosition.Y);
2128 translateMatrix.TransformPoints(points);
2130 //Scale down the point
2131 Matrix scalingMatrix = new Matrix();
2132 scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor);
2133 scalingMatrix.Invert();
2134 scalingMatrix.TransformPoints(points);
2135 scalingMatrix.Invert();
2139 return this.activeLayout.MapInCoOrdToLayout(points[0]);
2143 #region IServiceProvider Implemetation
2144 object IServiceProvider.GetService(Type serviceType)
2146 return GetService(serviceType);
2149 protected override object GetService(Type serviceType)
2151 object retVal = null;
2153 if (serviceType == typeof(CommandID))
2154 retVal = new CommandID(new Guid("5f1c3c8d-60f1-4b98-b85b-8679f97e8eac"), 0);
2156 retVal = this.serviceProvider.GetService(serviceType);
2162 #region Class WorkflowMessageDispatchData
2163 private sealed class WorkflowMessageDispatchData : IDisposable
2165 private WorkflowView workflowView;
2166 private HitTestInfo messageContext = null;
2168 public WorkflowMessageDispatchData(WorkflowView workflowView, EventArgs e)
2170 this.workflowView = workflowView;
2172 if (this.workflowView.RootDesigner != null && this.workflowView.stockMessageFilters.Count > 0)
2174 Point clientPoint = Point.Empty;
2175 if (e is MouseEventArgs || e is DragEventArgs)
2177 if (e is MouseEventArgs)
2179 clientPoint = new Point(((MouseEventArgs)e).X, ((MouseEventArgs)e).Y);
2181 else if (e is DragEventArgs)
2183 clientPoint = this.workflowView.PointToClient(new Point(((DragEventArgs)e).X, ((DragEventArgs)e).Y));
2184 this.workflowView.UpdateLayout();
2187 Point logicalPoint = this.workflowView.ClientPointToLogical(clientPoint);
2188 HitTestInfo hitTestInfo = this.workflowView.RootDesigner.HitTest(logicalPoint);
2189 this.messageContext = (hitTestInfo != null) ? hitTestInfo : HitTestInfo.Nowhere;
2190 this.workflowView.messageHitTestContexts.Push(this.messageContext);
2195 void IDisposable.Dispose()
2197 if (this.workflowView != null && this.messageContext != null)
2199 HitTestInfo hittestInfo = this.workflowView.messageHitTestContexts.Pop();
2200 if (hittestInfo != this.messageContext)
2201 Debug.Assert(false, "WorkflowView poped wrong message context");
2205 public ReadOnlyCollection<WorkflowDesignerMessageFilter> Filters
2209 //We recreate a new list everytime as in some of the messages dispatched, we there can
2210 //be additional filters which might be added
2211 List<WorkflowDesignerMessageFilter> mergedFilterList = new List<WorkflowDesignerMessageFilter>();
2212 mergedFilterList.AddRange(this.workflowView.customMessageFilters);
2213 mergedFilterList.AddRange(this.workflowView.stockMessageFilters);
2214 return mergedFilterList.AsReadOnly();
2220 #region IMessageFilter Implementation
2221 bool IMessageFilter.PreFilterMessage(ref Message m)
2223 bool handled = false;
2224 if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN ||
2225 m.Msg == NativeMethods.WM_KEYUP || m.Msg == NativeMethods.WM_SYSKEYUP)
2227 Control control = Control.FromHandle(m.HWnd);
2228 if (control != null && (control == this || Controls.Contains(control)))
2230 KeyEventArgs eventArgs = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys);
2231 if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN)
2232 OnKeyDown(eventArgs);
2236 handled = eventArgs.Handled;
2245 #region Class WorkflowViewAccessibleObject
2246 [Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
2247 public class WorkflowViewAccessibleObject : Control.ControlAccessibleObject
2249 private WorkflowView workflowView;
2251 public WorkflowViewAccessibleObject(WorkflowView workflowView)
2252 : base(workflowView)
2254 if (workflowView == null)
2255 throw new ArgumentNullException("workflowView");
2256 this.workflowView = workflowView;
2259 public override Rectangle Bounds
2263 return new Rectangle(this.workflowView.PointToScreen(Point.Empty), this.workflowView.ViewPortSize);
2267 public override string DefaultAction
2271 return DR.GetString(DR.AccessibleAction);
2275 public override string Description
2279 return DR.GetString(DR.WorkflowViewAccessibleDescription);
2283 public override string Help
2287 return DR.GetString(DR.WorkflowViewAccessibleHelp);
2291 public override string Name
2295 return DR.GetString(DR.WorkflowViewAccessibleName);
2303 public override AccessibleRole Role
2307 return AccessibleRole.Diagram;
2311 public override AccessibleObject GetChild(int index)
2313 return (this.workflowView.RootDesigner != null && index == 0) ? this.workflowView.RootDesigner.AccessibilityObject : base.GetChild(index);
2316 public override int GetChildCount()
2318 return (this.workflowView.RootDesigner != null) ? 1 : -1;
2321 public override AccessibleObject Navigate(AccessibleNavigation navdir)
2323 if (navdir == AccessibleNavigation.FirstChild || navdir == AccessibleNavigation.LastChild)
2326 return base.Navigate(navdir);
2331 #region WorkflowTimer
2332 internal sealed class WorkflowTimer : IDisposable
2334 private static WorkflowTimer workflowTimer;
2336 private const int TimerInterval = 50;
2337 private Timer timer = null;
2338 private List<ElapsedEventUnit> elapsedEvents = new List<ElapsedEventUnit>();
2340 internal static WorkflowTimer Default
2344 if (WorkflowTimer.workflowTimer == null)
2345 WorkflowTimer.workflowTimer = new WorkflowTimer();
2346 return WorkflowTimer.workflowTimer;
2350 private WorkflowTimer()
2352 this.timer = new Timer();
2353 this.timer.Interval = WorkflowTimer.TimerInterval;
2354 this.timer.Tick += new EventHandler(OnTimer);
2363 public void Dispose()
2366 GC.SuppressFinalize(this);
2369 private void Dispose(bool disposing)
2371 if (this.timer != null)
2373 if (this.timer.Enabled)
2376 this.timer.Dispose();
2381 internal void Subscribe(int elapsedInterval, EventHandler elapsedEventHandler)
2383 this.elapsedEvents.Add(new ElapsedEventUnit(elapsedInterval / WorkflowTimer.TimerInterval, elapsedEventHandler));
2384 if (!this.timer.Enabled)
2388 internal void Unsubscribe(EventHandler elapsedEventHandler)
2390 List<ElapsedEventUnit> removableElapsedEvents = new List<ElapsedEventUnit>();
2391 foreach (ElapsedEventUnit elapsedEvent in this.elapsedEvents)
2393 if (elapsedEvent.elapsedEventHandler == elapsedEventHandler)
2394 removableElapsedEvents.Add(elapsedEvent);
2397 foreach (ElapsedEventUnit elapsedEvent in removableElapsedEvents)
2398 this.elapsedEvents.Remove(elapsedEvent);
2400 if (this.elapsedEvents.Count == 0 && this.timer.Enabled)
2404 private void OnTimer(object sender, EventArgs e)
2406 List<ElapsedEventUnit> clonedList = new List<ElapsedEventUnit>(this.elapsedEvents);
2407 foreach (ElapsedEventUnit elapsedEvent in clonedList)
2409 elapsedEvent.elapsedTime += 1;
2410 if (elapsedEvent.elapsedInterval <= elapsedEvent.elapsedTime)
2412 elapsedEvent.elapsedTime = 0;
2413 elapsedEvent.elapsedEventHandler(this, EventArgs.Empty);
2418 private sealed class ElapsedEventUnit
2420 internal EventHandler elapsedEventHandler;
2421 internal int elapsedInterval;
2422 internal int elapsedTime;
2424 internal ElapsedEventUnit(int interval, EventHandler eventHandler)
2426 this.elapsedInterval = interval;
2427 this.elapsedEventHandler = eventHandler;