Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WF / Common / AuthoringOM / Design / WorkflowView.cs / 1305376 / WorkflowView.cs
namespace System.Workflow.ComponentModel.Design { using System; using System.IO; using System.Data; using System.Drawing; using System.Security; using System.Resources; using System.Reflection; using System.Diagnostics; using System.Collections; using System.Windows.Forms; using System.ComponentModel; using System.Drawing.Design; using System.Drawing.Imaging; using System.Drawing.Printing; using System.Drawing.Drawing2D; using System.Workflow.Interop; using System.Collections.Generic; using System.Windows.Forms.Design; using System.Security.Permissions; using System.ComponentModel.Design; using System.Runtime.InteropServices; using System.Collections.ObjectModel; ///What did I change in this file ///1. Eliminated the layout manager and introduced classes for WorkflowLayout and PrintPreviewLayout ///2. Eliminated the event syncing of PageSetupData change. We call performlayout on the current designer service whenever the pagesetupdata changes /// Designer Features: /// Selection on click and thru drag rectangle /// Reconfigurable background /// Scrolling /// Ensure Visible functionality /// Accessibility /// Zoom /// Rehostable /// Extensible /// Small memory footprint /// Ability to move around objects using drag drop /// Ability to drop the objects using drag drop /// Printing support /// Theme support /// Magnifier /// AutoScroll /// AutoExpand /// USE THIS FOR PERFORMANCE TEST: Debug.WriteLine("******Root drawing: " + Convert.ToString((DateTime.Now.Ticks - ticks) / 10000) + "ms"); /// /// Here are some details about the coordinate system, /// /// Screen CoOrdinate System: Starts at 0,0 of the screen /// Client CoOrdinate System: Starts at 0,0 of the control /// Logical CoOrdinate System: The workflowview supports zooming and scroll, we want to hide this /// complexity from the activity writter and hence whenever we get a coordinate we translate it based /// scroll position, zoom level and layout. This helps us to sheild the activity designers from complexity /// of zooming, scaling and layouting. The designer writters deal with one coordinate system which is unscaled and /// starts at 0,0 /// /// [ToolboxItem(false)] [ActivityDesignerTheme(typeof(AmbientTheme), Xml = WorkflowView.ThemeXml)] public class WorkflowView : UserControl, IServiceProvider, IMessageFilter { #region Theme Initializer XML internal const string ThemeXml = ""; #endregion #region Members Variables private enum TabButtonIds { MultiPage = 1, Zoom, Pan} //Designer Hookup private IServiceProvider serviceProvider = null; private ActivityDesigner rootDesigner = null; //Zoom private float zoomLevel = 1.0f; private int shadowDepth = WorkflowTheme.CurrentTheme.AmbientTheme.ShadowDepth; //MessageFilters private List stockMessageFilters = new List (); private List customMessageFilters = new List (); // private Bitmap viewPortBitmap = null; //Misc. private WorkflowToolTip workflowToolTip = null; private CommandSet commandSet = null; private DynamicAction fitAllAction = null; //print private int prePreviewZoom = 100; private Point prePreviewScroll = Point.Empty; private WorkflowPrintDocument printDocument = null; //Active layout private WorkflowLayout activeLayout = null; private WorkflowLayout defaultLayout = null; //One time callable delegates private EventHandler layoutEventHandler = null; private EventHandler ensureVisibleEventHandler = null; private Stack messageHitTestContexts = new Stack (); private HScrollBar hScrollBar; private VScrollBar vScrollBar; private TabControl toolContainer; private EventHandler idleEventListeners; private EventHandler idleEventHandler; private bool dragDropInProgress; #endregion #region Events public event EventHandler ZoomChanged; public event EventHandler RootDesignerChanged; #endregion #region Constructor and Dispose public WorkflowView() : this(new DesignSurface()) { } public WorkflowView(IServiceProvider serviceProvider) { Debug.Assert(serviceProvider != null); if (serviceProvider == null) throw new ArgumentNullException("serviceProvider"); SuspendLayout(); AllowDrop = true; AutoScroll = false; HScroll = false; VScroll = false; SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint | ControlStyles.Selectable | ControlStyles.EnableNotifyMessage, true); this.serviceProvider = serviceProvider; //*****Promote the services which are accessed from other components IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer; if (serviceContainer != null) { //Remove any existing designer service if there is any serviceContainer.RemoveService(typeof(WorkflowView)); serviceContainer.AddService(typeof(WorkflowView), this); } //set the UI Service to be used by themes IUIService uiService = this.serviceProvider.GetService(typeof(IUIService)) as IUIService; if(uiService != null) WorkflowTheme.UIService = uiService; //Make sure that we add scrollbars EnsureScrollBars(new HScrollBar(), new VScrollBar()); //Initialize the tooltip shown this.workflowToolTip = new WorkflowToolTip(this); //[....] the global theme change event, which is fired by the theme infrastructure for theme change WorkflowTheme.ThemeChanged += new EventHandler(OnThemeChange); //Create the core message filters PopulateMessageFilters(true); //Set the root designer, note that the dynamic action is dependent on the DynamicActionMessageFilter pushed //when the root is set. RootDesigner = ActivityDesigner.GetSafeRootDesigner(this); this.fitAllAction = CreateDynamicAction(); //If the active layout is still null then we will set the default layout as active layout if (this.activeLayout == null || this.defaultLayout == null) ActiveLayout = DefaultLayout = new WorkflowRootLayout(this.serviceProvider); //Create the local command set and update all the commands once IMenuCommandService menuCommandService = GetService(typeof(IMenuCommandService)) as IMenuCommandService; if (menuCommandService != null) { this.commandSet = new CommandSet(this); this.commandSet.UpdatePanCommands(true); } //Subscribe to selection change ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService; if (selectionService != null) selectionService.SelectionChanged += new EventHandler(OnSelectionChanged); //In case of non VS case we need to pumpin the Keyboard messages, the user control sets //focus to the child controls by default which is a problem so we need to trap the //messages by adding application level message filter, in case of VS this is not required and //the message filter is never called. Application.AddMessageFilter(this); //We make sure that during the construction we dont do perform layouts on idle event ResumeLayout(true); } protected override void Dispose(bool disposing) { //Remove the proffered services if (disposing) { try { SuspendLayout(); Application.RemoveMessageFilter(this); if (this.layoutEventHandler != null) { Idle -= this.layoutEventHandler; this.layoutEventHandler = null; } if (this.ensureVisibleEventHandler != null) { Idle -= this.ensureVisibleEventHandler; this.ensureVisibleEventHandler = null; } if (this.idleEventHandler != null) { this.idleEventListeners = null; Form host = TopLevelControl as Form; if (!Application.MessageLoop || (host != null && host.Modal)) WorkflowTimer.Default.Unsubscribe(this.idleEventHandler); else Application.Idle -= this.idleEventHandler; this.idleEventHandler = null; } ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService; if (selectionService != null) selectionService.SelectionChanged -= new EventHandler(OnSelectionChanged); //Unsubscribe the theme change WorkflowTheme.ThemeChanged -= new EventHandler(OnThemeChange); //Remove the dynamic action if (this.fitAllAction != null) { this.fitAllAction.Dispose(); this.fitAllAction = null; } if (this.workflowToolTip != null) { ((IDisposable)this.workflowToolTip).Dispose(); this.workflowToolTip = null; } DisposeMessageFilters(false); DisposeMessageFilters(true); //Dispose the layouts this.activeLayout = null; if (this.defaultLayout != null) { this.defaultLayout.Dispose(); this.defaultLayout = null; } //Destroy other resources if (this.viewPortBitmap != null) { this.viewPortBitmap.Dispose(); this.viewPortBitmap = null; } if (this.commandSet != null) { this.commandSet.Dispose(); this.commandSet = null; } HScrollBar.ValueChanged -= new EventHandler(OnScroll); VScrollBar.ValueChanged -= new EventHandler(OnScroll); if (this.toolContainer != null) { Controls.Remove(this.toolContainer); this.toolContainer.TabStrip.Tabs.Clear(); this.toolContainer.Dispose(); this.toolContainer = null; } IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer; if (serviceContainer != null) { serviceContainer.RemoveService(typeof(WorkflowView)); } } finally { ResumeLayout(false); } } base.Dispose( disposing ); } #endregion #region Public Properties public int Zoom { get { return Convert.ToInt32(this.zoomLevel * 100); } set { if (Zoom == value) return; if (value < AmbientTheme.MinZoom || value > AmbientTheme.MaxZoom) throw new NotSupportedException(DR.GetString(DR.ZoomLevelException2, AmbientTheme.MinZoom, AmbientTheme.MaxZoom)); ScrollBar hScrollBar = HScrollBar; ScrollBar vScrollBar = VScrollBar; if (hScrollBar != null && vScrollBar != null) { PointF oldRelativeCenter = Point.Empty; Point oldCenter = new Point(ScrollPosition.X, ScrollPosition.Y); oldRelativeCenter = new PointF((float)oldCenter.X / (float)hScrollBar.Maximum, (float)oldCenter.Y / (float)vScrollBar.Maximum); //recalculate the zoom and scroll range this.zoomLevel = (float)value / 100.0f; UpdateScrollRange(); //center the view again Point newCenter = new Point((int)((float)hScrollBar.Maximum * oldRelativeCenter.X), (int)((float)vScrollBar.Maximum * oldRelativeCenter.Y)); ScrollPosition = new Point(newCenter.X, newCenter.Y); if (this.rootDesigner != null) this.rootDesigner.Location = this.activeLayout.RootDesignerAlignment; InvalidateClientRectangle(Rectangle.Empty); // this.activeLayout.Update(null, WorkflowLayout.LayoutUpdateReason.ZoomChanged); //force command refresh //this is to workarond VS not refreshing Zoom drop down when doing area zoom-in IUIService uis = GetService(typeof(IUIService)) as IUIService; if (uis != null) uis.SetUIDirty(); //We need to update the zoom commands when the zoom is updated if (this.commandSet != null) this.commandSet.UpdateZoomCommands(true); OnZoomChanged(); } } } public ActivityDesigner RootDesigner { get { return this.rootDesigner; } set { if (this.rootDesigner == value) return; DisposeMessageFilters(false); this.rootDesigner = value; if (this.rootDesigner != null) { PopulateMessageFilters(false); ActiveLayout = DefaultLayout = this.rootDesigner.SupportedLayout; } OnRootDesignerChanged(); base.PerformLayout(); } } public int ShadowDepth { get { return this.shadowDepth; } set { if (value < AmbientTheme.MinShadowDepth || value > AmbientTheme.MaxShadowDepth) throw new NotSupportedException(DR.GetString(DR.ShadowDepthException, AmbientTheme.MinShadowDepth, AmbientTheme.MaxShadowDepth)); if (this.shadowDepth == value) return; this.shadowDepth = value; InvalidateClientRectangle(Rectangle.Empty); } } public Rectangle ViewPortRectangle { get { return new Rectangle(ScrollPosition, ViewPortSize); } } public Size ViewPortSize { get { Size viewPortSize = ClientSize; if (HScrollBar.Visible) viewPortSize.Height = Math.Max(0, viewPortSize.Height - HScrollBar.Height); if (VScrollBar.Visible) viewPortSize.Width = Math.Max(0, viewPortSize.Width - VScrollBar.Width); return viewPortSize; } } public Point ScrollPosition { get { return new Point(HScrollBar.Value, VScrollBar.Value); } set { ScrollBar hScrollBar = HScrollBar; if (hScrollBar != null) { value.X = Math.Min(value.X, hScrollBar.Maximum - hScrollBar.LargeChange + 1); value.X = Math.Max(value.X, hScrollBar.Minimum); hScrollBar.Value = value.X; } ScrollBar vScrollBar = VScrollBar; if (vScrollBar != null) { value.Y = Math.Min(value.Y, vScrollBar.Maximum - vScrollBar.LargeChange + 1); value.Y = Math.Max(value.Y, vScrollBar.Minimum); vScrollBar.Value = value.Y; } } } public bool PrintPreviewMode { get { return (this.activeLayout == ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout); } set { if (PrintPreviewMode == value) return; if (value && PrinterSettings.InstalledPrinters.Count == 0) { DesignerHelpers.ShowError(this, DR.GetString(DR.ThereIsNoPrinterInstalledErrorMessage)); value = false; } ActiveLayout = (value) ? ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout : DefaultLayout; if (this.commandSet != null) this.commandSet.UpdatePageLayoutCommands(true); if (PrintPreviewMode) { this.prePreviewZoom = Zoom; this.prePreviewScroll = ScrollPosition; Zoom = 40; } else { Zoom = this.prePreviewZoom; ScrollPosition = this.prePreviewScroll; } } } public PrintDocument PrintDocument { get { if (this.printDocument == null) this.printDocument = new WorkflowPrintDocument(this); return this.printDocument; } } public event EventHandler Idle { add { //Add the listener to our list this.idleEventListeners += value; if (this.idleEventHandler == null) { this.idleEventHandler = new EventHandler(OnWorkflowIdle); Form host = TopLevelControl as Form; if (!Application.MessageLoop || (host != null && host.Modal)) WorkflowTimer.Default.Subscribe(100, this.idleEventHandler); else Application.Idle += this.idleEventHandler; } } remove { this.idleEventListeners -= value; if (this.idleEventHandler != null && this.idleEventListeners == null) { Form host = TopLevelControl as Form; if (host != null && host.Modal) WorkflowTimer.Default.Unsubscribe(this.idleEventHandler); else Application.Idle -= this.idleEventHandler; this.idleEventHandler = null; } } } public HScrollBar HScrollBar { get { return this.hScrollBar; } } public VScrollBar VScrollBar { get { return this.vScrollBar; } } public bool EnableFitToScreen { get { return (this.fitAllAction != null); } set { if (EnableFitToScreen == value) return; if (value) { if (this.fitAllAction == null) this.fitAllAction = CreateDynamicAction(); } else { if (this.fitAllAction != null) { this.fitAllAction.Dispose(); this.fitAllAction = null; } } InvalidateClientRectangle(Rectangle.Empty); } } #endregion #region Protected Properties #endregion #region Private Properties internal bool DragDropInProgress { get { return this.dragDropInProgress; } } internal bool ShowToolContainer { get { return (this.toolContainer != null); } set { if (ShowToolContainer == value) return; try { SuspendLayout(); if (value) { this.toolContainer = new TabControl(DockStyle.Right, AnchorAlignment.Far); Controls.Add(this.toolContainer); EnsureScrollBars(this.hScrollBar, this.toolContainer.ScrollBar as VScrollBar); string[,] tabButtonInfo = new string[/*Caption Resource ID*/,/*Bitmap Resource ID*/ ] { { "MultipageLayoutCaption", "MultipageLayout" }, { "ZoomCaption", "Zoom" }, { "PanCaption", "AutoPan" } }; for (int i = 0; i < tabButtonInfo.GetLength(0); i++) { Bitmap tabImage = DR.GetImage(tabButtonInfo[i, 1]) as Bitmap; string buttonCaption = DR.GetString(tabButtonInfo[i, 0]); this.toolContainer.TabStrip.Tabs.Add(new ItemInfo(i + 1, tabImage, buttonCaption)); } this.toolContainer.TabStrip.TabChange += new SelectionChangeEventHandler (OnTabChange); if (this.commandSet != null) { this.commandSet.UpdatePageLayoutCommands(true); this.commandSet.UpdateZoomCommands(true); this.commandSet.UpdatePanCommands(true); } } else { this.toolContainer.TabStrip.TabChange -= new SelectionChangeEventHandler (OnTabChange); this.toolContainer.TabStrip.Tabs.Clear(); Controls.Remove(this.toolContainer); this.toolContainer.Dispose(); this.toolContainer = null; EnsureScrollBars(this.hScrollBar, new VScrollBar()); } } finally { ResumeLayout(true); } } } internal HitTestInfo MessageHitTestContext { get { return this.messageHitTestContexts.Peek(); } } internal WorkflowLayout ActiveLayout { get { return this.activeLayout; } set { Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("Layout cannot be null!"); Cursor cursor = Cursor.Current; try { Cursor.Current = Cursors.WaitCursor; this.activeLayout = value; if (this.activeLayout != ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout) DefaultLayout = this.activeLayout; base.PerformLayout(); if (this.commandSet != null) this.commandSet.UpdatePageLayoutCommands(true); } finally { Cursor.Current = cursor; } } } private WorkflowLayout DefaultLayout { get { if (this.defaultLayout == null) this.defaultLayout = new WorkflowRootLayout(this); return this.defaultLayout; } set { if (value == null) throw new ArgumentNullException(DR.GetString(DR.Error_WorkflowLayoutNull)); if (this.defaultLayout == value) return; if (this.defaultLayout != null) this.defaultLayout.Dispose(); this.defaultLayout = value; } } private float ScaleZoomFactor { get { return (this.zoomLevel * this.activeLayout.Scaling); } } #endregion #region Public Methods public void AddDesignerMessageFilter(WorkflowDesignerMessageFilter designerMessageFilter) { if (designerMessageFilter == null) throw new ArgumentNullException("designerMessageFilter"); if (Capture) Capture = false; this.customMessageFilters.Insert(0, designerMessageFilter); designerMessageFilter.SetParentView(this); } public void RemoveDesignerMessageFilter(WorkflowDesignerMessageFilter designerMessageFilter) { if (designerMessageFilter == null) throw new ArgumentNullException("designerMessageFilter"); if (this.customMessageFilters.Contains(designerMessageFilter)) { if (Capture) Capture = false; this.customMessageFilters.Remove(designerMessageFilter); ((IDisposable)designerMessageFilter).Dispose(); } } public void ShowInPlaceToolTip(string toolTipText, Rectangle toolTipRectangle) { if (toolTipText == null) throw new ArgumentNullException("toolTipText"); if (toolTipRectangle.IsEmpty) throw new ArgumentException(SR.GetString(SR.Error_EmptyToolTipRectangle)); this.workflowToolTip.SetText(toolTipText, toolTipRectangle); } public void ShowInfoTip(string text) { if (text == null) throw new ArgumentNullException("text"); this.workflowToolTip.SetText(String.Empty, text); } public void ShowInfoTip(string title, string text) { if (title == null) throw new ArgumentNullException("title"); if (text == null) throw new ArgumentNullException("text"); this.workflowToolTip.SetText(title, text); } public void EnsureVisible(object selectableObject) { if (selectableObject == null) throw new ArgumentNullException("selectableObject"); // make sure that all the parents are expanded Activity activity = selectableObject as Activity; while (activity != null) { ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(activity); CompositeActivityDesigner parentDesigner = activityDesigner.ParentDesigner; if (parentDesigner != null) { if (activityDesigner != null) parentDesigner.EnsureVisibleContainedDesigner(activityDesigner); activity = parentDesigner.Activity; } else { activity = null; } } //this is to handle the case when we call ensure visible of a scope which currently has //activity from the secondary flow selected. instead we should always switch to the main flow activity = selectableObject as Activity; if (activity != null) { CompositeActivityDesigner compositeDesigner = ActivityDesigner.GetDesigner(activity) as CompositeActivityDesigner; if (compositeDesigner != null) compositeDesigner.EnsureVisibleContainedDesigner(compositeDesigner); } PerformLayout(false); if (this.ensureVisibleEventHandler == null) { this.ensureVisibleEventHandler = new EventHandler(OnEnsureVisible); Idle += this.ensureVisibleEventHandler; } } public void PerformLayout(bool immediateUpdate) { if (immediateUpdate) { if (this.layoutEventHandler != null) { Idle -= this.layoutEventHandler; this.layoutEventHandler = null; } base.PerformLayout();//invalidate rectangle really cares for the this.layoutEventHandler being null } else if (this.layoutEventHandler == null) { this.layoutEventHandler = new EventHandler(OnPerformLayout); Idle += this.layoutEventHandler; } } public void SaveViewState(Stream viewState) { if (viewState == null) throw new ArgumentNullException("viewState"); IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); if (designerHost == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName)); BinaryWriter writer = new BinaryWriter(viewState); // write workflow properties writer.Write(this.PrintPreviewMode); writer.Write(this.Zoom); // write components DesignerHelpers.SerializeDesignerStates(designerHost, writer); // write scroll position writer.Write(this.ScrollPosition.X); writer.Write(this.ScrollPosition.Y); } public void LoadViewState(Stream viewState) { if (viewState == null) throw new ArgumentNullException("viewState"); bool outdated = false; Point scrollPosition = new Point(0, 0); IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); if (designerHost == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName)); viewState.Position = 0; BinaryReader reader = new BinaryReader(viewState); // read workflow properties this.PrintPreviewMode = reader.ReadBoolean(); this.Zoom = reader.ReadInt32(); try { // get activities outdated = DesignerHelpers.DeserializeDesignerStates(designerHost, reader); // we will apply the scrolling only if if there is perfect match // between the components in the workflow and the persisted data. // It might be different if files were updated outside of VS, or if // VS crashes. if (!outdated) { scrollPosition.X = reader.ReadInt32(); scrollPosition.Y = reader.ReadInt32(); } } finally { // flush the layout to apply the new settings, this will set the scrollers extents base.PerformLayout(); this.ScrollPosition = scrollPosition; } } /// /// Changes zoom level on the design surface such that the entire workflow is displayed in the view /// public void FitToScreenSize() { if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height) { int newZoom = (int)(100.0f / ActiveLayout.Scaling * Math.Min((float)ViewPortSize.Width / (float)ActiveLayout.Extent.Width, (float)ViewPortSize.Height / (float)ActiveLayout.Extent.Height)); Zoom = Math.Min(Math.Max(newZoom, AmbientTheme.MinZoom), AmbientTheme.MaxZoom); } } ////// Sets the zoom level to 100% so that the workflow size is restored to actial workflow size /// public void FitToWorkflowSize() { if (Zoom != 100) Zoom = 100; } ////// Saves workflow as image to a file based on the format specified /// /// Path to file where to save the image /// Format in which to save the image public void SaveWorkflowImage(string imageFile, ImageFormat imageFormat) { if (imageFile == null) throw new ArgumentNullException("imageFile"); if (imageFormat == null) throw new ArgumentNullException("imageFormat"); Bitmap workflowBitmap = TakeWorkflowSnapShot(); if (workflowBitmap != null) { workflowBitmap.Save(imageFile, imageFormat); workflowBitmap.Dispose(); } } ////// Saves workflow as image to a stream based on encoding specified /// /// Stream where to save the workflow /// Format in which to save the image public void SaveWorkflowImage(Stream stream, ImageFormat imageFormat) { if (stream == null) throw new ArgumentNullException("stream"); if (imageFormat == null) throw new ArgumentNullException("imageFormat"); Bitmap workflowBitmap = TakeWorkflowSnapShot(); if (workflowBitmap != null) { workflowBitmap.Save(stream, imageFormat); workflowBitmap.Dispose(); } } ////// Stores the workflow image to clipboard. /// public void SaveWorkflowImageToClipboard() { Bitmap workflowBitmap = TakeWorkflowSnapShot(); if (workflowBitmap != null) { Clipboard.SetDataObject(workflowBitmap, true); workflowBitmap.Dispose(); } } #endregion #region Protected Methods #region Overridden Methods handling UI events #region Drawing protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //We set the highest quality interpolation so that we do not loose the image quality GraphicsContainer graphicsState = e.Graphics.BeginContainer(); e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; e.Graphics.CompositingQuality = CompositingQuality.HighQuality; bool takeWorkflowSnapShot = (this.viewPortBitmap == null || this.viewPortBitmap.Size != ViewPortSize); if (takeWorkflowSnapShot) { if (this.viewPortBitmap != null) this.viewPortBitmap.Dispose(); this.viewPortBitmap = new Bitmap(Math.Max(1, ViewPortSize.Width), Math.Max(1, ViewPortSize.Height), e.Graphics); } //Create viewport information and take the workflow snapshot before passing on the information to the active layout ViewPortData viewPortData = new ViewPortData(); viewPortData.LogicalViewPort = ClientRectangleToLogical(new Rectangle(Point.Empty, ViewPortSize)); viewPortData.MemoryBitmap = this.viewPortBitmap; viewPortData.Scaling = new SizeF(ScaleZoomFactor, ScaleZoomFactor); viewPortData.Translation = ScrollPosition; viewPortData.ShadowDepth = new Size(this.shadowDepth, this.shadowDepth); viewPortData.ViewPortSize = ViewPortSize; //capture the workflow onto in-memory bitmap if (this.layoutEventHandler == null || takeWorkflowSnapShot) WorkflowView.TakeWorkflowSnapShot(this, viewPortData); //copy workflow from the bitmap onto corresponding pages on the screen try { this.activeLayout.OnPaintWorkflow(e, viewPortData); } catch (Exception ex) { //If a layout throws an exception then we will not draw the layout // Debug.WriteLine(ex); } //If any of the message filters throws an exception we continue to draw using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { if (((IWorkflowDesignerMessageSink)filter).OnPaintWorkflowAdornments(e, ViewPortRectangle)) break; } catch (Exception ex) { //Ignore the filter throwing the exception and continue to function Debug.WriteLine(ex); } } } e.Graphics.EndContainer(graphicsState); e.Graphics.FillRectangle(SystemBrushes.Control, new Rectangle(Width - SystemInformation.VerticalScrollBarWidth, Height - SystemInformation.HorizontalScrollBarHeight, SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight)); } protected virtual void OnZoomChanged() { if (this.ZoomChanged != null) this.ZoomChanged(this, EventArgs.Empty); } protected virtual void OnRootDesignerChanged() { if (this.RootDesignerChanged != null) this.RootDesignerChanged(this, EventArgs.Empty); } #endregion #region Mouse Events protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseDown(e)) break; } } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseMove(e)) break; } } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseUp(e)) break; } } } protected override void OnMouseDoubleClick(MouseEventArgs e) { base.OnMouseDoubleClick(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseDoubleClick(e)) break; } } } protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); Point clientPoint = PointToClient(Control.MousePosition); MouseEventArgs eventArgs = new MouseEventArgs(Control.MouseButtons, 1, clientPoint.X, clientPoint.Y, 0); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, eventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseEnter(eventArgs)) break; } } } protected override void OnMouseHover(EventArgs e) { base.OnMouseHover(e); Point clientPoint = PointToClient(Control.MousePosition); MouseEventArgs eventArgs = new MouseEventArgs(Control.MouseButtons, 1, clientPoint.X, clientPoint.Y, 0); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, eventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseHover(eventArgs)) break; } } } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseLeave()) break; } } } protected override void OnMouseCaptureChanged(EventArgs e) { base.OnMouseCaptureChanged(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseCaptureChanged()) break; } } } protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseWheel(e)) break; } } } #endregion #region Keyboard Events protected override void OnKeyDown(KeyEventArgs e) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnKeyDown(e)) break; } } if (!e.Handled) base.OnKeyDown(e); } protected override void OnKeyUp(KeyEventArgs e) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnKeyUp(e)) break; } } if (!e.Handled) base.OnKeyUp(e); } #endregion #region Layouting Events protected override void OnLayout(LayoutEventArgs levent) { base.OnLayout(levent); ScrollBar hScrollBar = HScrollBar; ScrollBar vScrollBar = VScrollBar; if (Controls.Contains(hScrollBar)) hScrollBar.Bounds = new Rectangle(0, Math.Max(0, Height - SystemInformation.HorizontalScrollBarHeight), Math.Max(Width - ((vScrollBar.Visible) ? SystemInformation.VerticalScrollBarWidth : 0), 0), SystemInformation.HorizontalScrollBarHeight); if (Controls.Contains(vScrollBar)) vScrollBar.Bounds = new Rectangle(Math.Max(0, Width - SystemInformation.VerticalScrollBarWidth), 0, SystemInformation.VerticalScrollBarWidth, Math.Max(Height - ((hScrollBar.Visible) ? SystemInformation.HorizontalScrollBarHeight : 0), 0)); if (this.toolContainer != null) { this.toolContainer.Location = new Point(Width - this.toolContainer.Width, 0); this.toolContainer.Height = Height - ((hScrollBar.Visible) ? hScrollBar.Height :0); } using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, levent)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) ((IWorkflowDesignerMessageSink)filter).OnLayout(levent); } //Layout the designers using (Graphics graphics = CreateGraphics()) { this.activeLayout.Update(graphics, WorkflowLayout.LayoutUpdateReason.LayoutChanged); if (this.rootDesigner != null) this.rootDesigner.Location = this.activeLayout.RootDesignerAlignment; } //Update the scroll range and redraw UpdateScrollRange(); InvalidateClientRectangle(Rectangle.Empty); } #endregion #region DragDrop Events protected override void OnDragEnter(DragEventArgs dragEventArgs) { base.OnDragEnter(dragEventArgs); this.dragDropInProgress = true; using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragEnter(dragEventArgs)) break; } } } protected override void OnDragOver(DragEventArgs dragEventArgs) { base.OnDragOver(dragEventArgs); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragOver(dragEventArgs)) break; } } } protected override void OnDragLeave(EventArgs e) { base.OnDragLeave(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragLeave()) break; } } this.dragDropInProgress = false; } protected override void OnDragDrop(DragEventArgs dragEventArgs) { base.OnDragDrop(dragEventArgs); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragDrop(dragEventArgs)) break; } } this.dragDropInProgress = false; } protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent) { base.OnGiveFeedback(gfbevent); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, gfbevent)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnGiveFeedback(gfbevent)) break; } } } protected override void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent) { base.OnQueryContinueDrag(qcdevent); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, qcdevent)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnQueryContinueDrag(qcdevent)) break; } } } #endregion #region General Events //Handle context menus here reason being it can come from mouse r button click //or shift+F10, or there might be other keys too //We need to handle the WndProc and not the OnNotifyMessage because we need to set //the m.Result to handled (IntPtr.Zero) and dont let the base class see the message at all //see WinOE #787 "The keyboard "key" to launch the context menu launches the menu at 0,0" [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] [UIPermission(SecurityAction.Assert, Window = UIPermissionWindow.AllWindows)] protected override void WndProc(ref Message m) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).ProcessMessage(m)) break; } const int WM_CONTEXTMENU = 0x007B; if (m.Msg == WM_CONTEXTMENU) { int LParam = (int)m.LParam; Point location = (LParam != -1) ? new Point(LParam) : Control.MousePosition; foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnShowContextMenu(location)) break; } //mark the message handled m.Result = IntPtr.Zero; //dont pass the message to the base but return immediatly return; } } if (this.workflowToolTip != null && m.Msg == NativeMethods.WM_NOTIFY) this.workflowToolTip.RelayParentNotify(ref m); try { if (m.Result == IntPtr.Zero) base.WndProc(ref m); } catch (Exception e) { if (e != CheckoutException.Canceled) DesignerHelpers.ShowError(this, e); } } protected override void OnControlAdded(ControlEventArgs e) { if (e.Control != VScrollBar && e.Control != HScrollBar && e.Control != this.toolContainer) throw new InvalidOperationException(SR.GetString(SR.Error_InsertingChildControls)); } protected override AccessibleObject CreateAccessibilityInstance() { return new WorkflowViewAccessibleObject(this); } #endregion #endregion #endregion #region Private Methods private void OnWorkflowIdle(object sender, EventArgs e) { if (this.idleEventListeners != null) this.idleEventListeners(this, e); } private void UpdateLayout() { if (this.layoutEventHandler != null) { PerformLayout(true); InvalidateClientRectangle(Rectangle.Empty); } } internal void OnCommandKey(KeyEventArgs e) { this.OnKeyDown(e); this.OnKeyUp(e); } private void OnSelectionChanged(object sender, EventArgs e) { if (this.commandSet != null) this.commandSet.UpdateCommandSet(); //Make sure that the ensure visible also works when the component is selected //from property browser dropdown //Make sure that when there is a selection change using the property browser //drop down we make sure that the designer associated with component selected by the user in the dropdown //is made visible. //To enable this functionality please note that selection change is not a good event as it will get //fired in multiple cases, instead we should add a event in extended ui service which will do this and move //the following code in the event handler of that event //Ref Bug#3925 if (RootDesigner != null && RootDesigner.Activity != null) { ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService; if (selectionService != null && selectionService.GetComponentSelected(RootDesigner.Activity)) { IHelpService helpService = GetService(typeof(IHelpService)) as IHelpService; if (helpService != null) helpService.AddContextAttribute("Keyword", RootDesigner.Activity.GetType().FullName, HelpKeywordType.F1Keyword); } } } private void OnPerformLayout(object sender, EventArgs e) { if (this.layoutEventHandler != null) { Idle -= this.layoutEventHandler; this.layoutEventHandler = null; base.PerformLayout(); } } //Gets the snapshot of the entire workflow private Bitmap TakeWorkflowSnapShot() { Bitmap bitmap = null; ActivityDesigner rootDesigner = RootDesigner; if (rootDesigner != null) { using (Graphics graphics = CreateGraphics()) { ViewPortData viewPortData = new ViewPortData(); viewPortData.LogicalViewPort = new Rectangle(Point.Empty, new Size(rootDesigner.Bounds.Width + 2 * DefaultWorkflowLayout.Separator.Width, rootDesigner.Bounds.Height + 2 * DefaultWorkflowLayout.Separator.Height)); viewPortData.MemoryBitmap = new Bitmap(viewPortData.LogicalViewPort.Width, viewPortData.LogicalViewPort.Height, graphics); viewPortData.Scaling = new SizeF(1, 1); viewPortData.Translation = Point.Empty; viewPortData.ShadowDepth = new Size(0, 0); viewPortData.ViewPortSize = viewPortData.LogicalViewPort.Size; TakeWorkflowSnapShot(this, viewPortData); bitmap = viewPortData.MemoryBitmap; } } return bitmap; } //This function will give snapshot of what is drawn on the screen at any point of time //It will scale and translate the designers and drawing based on the viewport data //We need this function in OnPaint and taking snapshot of magnifier bitmap //At the end of this function; the ViewPortData.MemoryBitmap will contain the bitmap of the //workflow to be drawn as per layout internal static void TakeWorkflowSnapShot(WorkflowView workflowView, ViewPortData viewPortData) { //Get the drawing canvas Bitmap memoryBitmap = viewPortData.MemoryBitmap; Debug.Assert(memoryBitmap != null); using (Graphics viewPortGraphics = Graphics.FromImage(memoryBitmap)) { //We set the highest quality interpolation so that we do not loose the image quality viewPortGraphics.SmoothingMode = SmoothingMode.HighQuality; viewPortGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic; using (PaintEventArgs eventArgs = new PaintEventArgs(viewPortGraphics, viewPortData.LogicalViewPort)) { workflowView.ActiveLayout.OnPaint(eventArgs, viewPortData); } //Create the scaling matrix Matrix transformationMatrix = new Matrix(); transformationMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend); //When we draw on the viewport we draw in scaled and translated. //So that we minimize the calls to DrawImage //Make sure that we scale down the logical view port origin in order to take care of scaling factor //Before we select the transform factor we make sure that logicalviewport origin is scaled down Point[] logicalViewPortOrigin = new Point[] { viewPortData.LogicalViewPort.Location }; transformationMatrix.TransformPoints(logicalViewPortOrigin); //For performance improvement and to eliminate one extra DrawImage...we draw the designers on the viewport //bitmap with visual depth consideration transformationMatrix.Translate(-logicalViewPortOrigin[0].X + viewPortData.ShadowDepth.Width, -logicalViewPortOrigin[0].Y + viewPortData.ShadowDepth.Height, MatrixOrder.Append); //Select the transform into viewport graphics. //Viewport bitmap has the scaled and translated designers which we then map to //the actual graphics based on page layout viewPortGraphics.Transform = transformationMatrix; //Draw the designers on bitmap if (workflowView.RootDesigner != null) { using (Region clipRegion = new Region()) using (GraphicsPath designerPath = ActivityDesignerPaint.GetDesignerPath(workflowView.RootDesigner, false)) { Region oldRegion = viewPortGraphics.Clip; //First draw the grid and rectangle with the designer clip region clipRegion.MakeEmpty(); clipRegion.Union(designerPath); viewPortGraphics.Clip = clipRegion; AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme; viewPortGraphics.FillRectangle(ambientTheme.BackgroundBrush, workflowView.RootDesigner.Bounds); if (ambientTheme.ShowGrid) ActivityDesignerPaint.DrawGrid(viewPortGraphics, workflowView.RootDesigner.Bounds); viewPortGraphics.Clip = oldRegion; //Then draw the root with clip region extended try { using (PaintEventArgs paintEventArgs = new PaintEventArgs(viewPortGraphics, viewPortData.LogicalViewPort)) { ((IWorkflowDesignerMessageSink)workflowView.RootDesigner).OnPaint(paintEventArgs, viewPortData.LogicalViewPort); } } catch (Exception e) { //Eat the exception thrown in draw Debug.WriteLine(e); } } } //Draw all the filters using (PaintEventArgs paintArgs = new PaintEventArgs(viewPortGraphics, workflowView.RootDesigner.Bounds)) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(workflowView, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { if (((IWorkflowDesignerMessageSink)filter).OnPaint(paintArgs, viewPortData.LogicalViewPort)) break; } catch (Exception e) { Debug.WriteLine(e); } } } } viewPortGraphics.Transform = new Matrix(); //Now that we have a bitmap which is bit offseted based visual depth we need to take copy of it //This is done so as to avoid expensive DrawImage call, what I am assuming here is that time it //will take to create a new bitmap from an existing one is less expensive in terms of speed than space //As you just need to copy bitmap bits in memory than to perform expesive Image Drawing operation if (!viewPortData.ShadowDepth.IsEmpty) { Bitmap temporaryBitmap = new Bitmap(memoryBitmap); //THEMETODO: WE JUST NEED TO GRAYSCALE THIS, RATHER THAN DRAWING A SHADOW //Now that we have taken a copy we will draw over the existing bitmap so that we can make it as shadow bitmap using (Brush shadowDepthBrush = new SolidBrush(Color.FromArgb(220, Color.White))) viewPortGraphics.FillRectangle(shadowDepthBrush, new Rectangle(Point.Empty, new Size(memoryBitmap.Size.Width - viewPortData.ShadowDepth.Width - 1, memoryBitmap.Size.Height - viewPortData.ShadowDepth.Height - 1))); //Now make sure that we draw the image from the temporary bitmap with white color set as transparent //so that we achive the 3D effect //Make sure that we take into consideration the transparency key ImageAttributes transparentColorKey = new ImageAttributes(); transparentColorKey.SetColorKey(viewPortData.TransparentColor, viewPortData.TransparentColor, ColorAdjustType.Default); transparentColorKey.SetColorKey(viewPortData.TransparentColor, viewPortData.TransparentColor, ColorAdjustType.Bitmap); viewPortGraphics.DrawImage(temporaryBitmap, new Rectangle(-viewPortData.ShadowDepth.Width, -viewPortData.ShadowDepth.Height, memoryBitmap.Width, memoryBitmap.Height), 0, 0, memoryBitmap.Width, memoryBitmap.Height, GraphicsUnit.Pixel, transparentColorKey); //Now dispose the temporary bitmap temporaryBitmap.Dispose(); } } } internal void OnThemeChange(object sender, EventArgs e) { ShadowDepth = WorkflowTheme.CurrentTheme.AmbientTheme.ShadowDepth; using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { ((IWorkflowDesignerMessageSink)filter).OnThemeChange(); } catch (Exception ex) { Debug.WriteLine(ex); } } } base.PerformLayout(); } private void OnEnsureVisible(object sender, EventArgs e) { if (this.ensureVisibleEventHandler != null) { Idle -= this.ensureVisibleEventHandler; this.ensureVisibleEventHandler = null; } ISelectionService selectionService = (ISelectionService)GetService(typeof(ISelectionService)); if (selectionService != null && selectionService.SelectionCount > 0) { //We do not want to regenerate a layout event in ensure visible ArrayList selectedComponents = new ArrayList(selectionService.GetSelectedComponents()); for (int i = selectedComponents.Count - 1; i >= 0; i--) { Rectangle rectangleToMakeVisible = Rectangle.Empty; if (selectedComponents[i] is Activity) { ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(selectedComponents[i] as Activity); if (activityDesigner != null) { rectangleToMakeVisible = activityDesigner.Bounds; rectangleToMakeVisible.Inflate(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize); rectangleToMakeVisible.Inflate(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize); } } else if (selectedComponents[i] is HitTestInfo) { rectangleToMakeVisible = ((HitTestInfo)selectedComponents[i]).Bounds; } if (!rectangleToMakeVisible.IsEmpty) EnsureVisible(rectangleToMakeVisible); } } } private void EnsureVisible(Rectangle rect) { Rectangle clientRectangle = ClientRectangleToLogical(new Rectangle(Point.Empty, ViewPortSize)); if (!clientRectangle.Contains(rect.Location) || !clientRectangle.Contains(new Point(rect.Right, rect.Bottom))) { Size scrollDelta = new Size(); if (!clientRectangle.Contains(new Point(rect.Left, clientRectangle.Top)) || !clientRectangle.Contains(new Point(rect.Right, clientRectangle.Top))) { if (rect.Width > clientRectangle.Width) scrollDelta.Width = (rect.Left + rect.Width / 2) - (clientRectangle.Left + clientRectangle.Width / 2); else if (rect.Left < clientRectangle.Left) scrollDelta.Width = (rect.Left - clientRectangle.Left); else scrollDelta.Width = (rect.Right - clientRectangle.Right); } if (!clientRectangle.Contains(new Point(clientRectangle.Left, rect.Top)) || !clientRectangle.Contains(new Point(clientRectangle.Left, rect.Bottom))) { if ((rect.Top < clientRectangle.Top) || (rect.Height > clientRectangle.Height)) scrollDelta.Height = (rect.Top - clientRectangle.Top); else scrollDelta.Height = rect.Bottom - clientRectangle.Bottom; } scrollDelta = LogicalSizeToClient(scrollDelta); Point scrollPosition = ScrollPosition; ScrollPosition = new Point(scrollPosition.X + scrollDelta.Width, scrollPosition.Y + scrollDelta.Height); } } private void OnScroll(object sender, EventArgs e) { //Lets speedup the scrolling logic InvalidateClientRectangle(Rectangle.Empty); ScrollBar scrollBar = sender as ScrollBar; if (scrollBar != null) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { ((IWorkflowDesignerMessageSink)filter).OnScroll(scrollBar, scrollBar.Value); } catch (Exception ex) { Debug.WriteLine(ex); } } } } } private void UpdateScrollRange() { if (ViewPortSize.Width < 0 || ViewPortSize.Height < 0) return; Size currentSize = ViewPortSize; Size maximumScrollSize = LogicalSizeToClient(this.activeLayout.Extent); Size largeChangeSize = new Size(Math.Min(maximumScrollSize.Width, currentSize.Width), Math.Min(maximumScrollSize.Height, currentSize.Height)); if (hScrollBar.Maximum != maximumScrollSize.Width) hScrollBar.Maximum = maximumScrollSize.Width; if (vScrollBar.Maximum != maximumScrollSize.Height) vScrollBar.Maximum = maximumScrollSize.Height; if (hScrollBar.LargeChange != largeChangeSize.Width) { hScrollBar.SmallChange = largeChangeSize.Width / 15; hScrollBar.LargeChange = largeChangeSize.Width + 1; } if (vScrollBar.LargeChange != largeChangeSize.Height) { vScrollBar.SmallChange = largeChangeSize.Height / 15; vScrollBar.LargeChange = largeChangeSize.Height + 1; } int xMaxScrollPos = maximumScrollSize.Width - hScrollBar.LargeChange; xMaxScrollPos = (xMaxScrollPos < 0) ? 0 : xMaxScrollPos; if (hScrollBar.Value > xMaxScrollPos) hScrollBar.Value = xMaxScrollPos; int yMaxScrollPos = maximumScrollSize.Height - vScrollBar.LargeChange; yMaxScrollPos = (yMaxScrollPos < 0) ? 0 : yMaxScrollPos; if (vScrollBar.Value > yMaxScrollPos) vScrollBar.Value = yMaxScrollPos; RefreshDynamicAction(); bool hScrollBarVisible = hScrollBar.Visible; if (Controls.Contains(hScrollBar)) hScrollBar.Visible = (hScrollBar.Maximum > currentSize.Width); bool vScrollBarVisible = vScrollBar.Visible; if (Controls.Contains(vScrollBar)) vScrollBar.Visible = (vScrollBar.Maximum > currentSize.Height); if (hScrollBarVisible != hScrollBar.Visible || vScrollBar.Visible != vScrollBarVisible) { base.PerformLayout(); Refresh(); } } private DynamicAction CreateDynamicAction() { DynamicAction fitAllAction = new DynamicAction(); fitAllAction.ButtonSize = DynamicAction.ButtonSizes.Large; fitAllAction.Doc----gnment = DesignerContentAlignment.BottomRight; fitAllAction.DockMargin = new Size(5, 5); ActionButton fitallButton = new ActionButton(new Image[] { DR.GetImage(DR.FitToScreen) as Bitmap }); fitallButton.StateChanged += new EventHandler(OnFitToScreen); fitAllAction.Buttons.Add(fitallButton); return fitAllAction; } private void RefreshDynamicAction() { DynamicActionMessageFilter dynamicActionFilter = GetService(typeof(DynamicActionMessageFilter)) as DynamicActionMessageFilter; if (dynamicActionFilter == null || this.fitAllAction == null) return; if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height) { //This means we need to show the zoomin icon this.fitAllAction.Buttons[0].Description = DR.GetString(DR.FitToScreenDescription); this.fitAllAction.Buttons[0].StateImages = new Bitmap[] { DR.GetImage(DR.FitToScreen) as Bitmap }; dynamicActionFilter.AddAction(this.fitAllAction); } else if (Zoom != 100) { //We need to show zoomout icon this.fitAllAction.Buttons[0].Description = DR.GetString(DR.FitToWorkflowDescription); this.fitAllAction.Buttons[0].StateImages = new Bitmap[] { DR.GetImage(DR.FitToWorkflow) as Bitmap }; dynamicActionFilter.AddAction(this.fitAllAction); } else { //In neither case we remove the action dynamicActionFilter.RemoveAction(this.fitAllAction); this.fitAllAction.Buttons[0].State = ActionButton.States.Normal; } } private void OnFitToScreen(object sender, EventArgs e) { ActionButton fitallButton = sender as ActionButton; if (fitallButton == null || fitallButton.State != ActionButton.States.Pressed) return; if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height) FitToScreenSize(); else if (Zoom != 100) FitToWorkflowSize(); } private void OnTabChange(object sender, TabSelectionChangeEventArgs e) { if (e.CurrentItem.Identifier == (int)TabButtonIds.MultiPage || e.CurrentItem.Identifier == (int)TabButtonIds.Zoom || e.CurrentItem.Identifier == (int)TabButtonIds.Pan) { Rectangle buttonRect = e.SelectedTabBounds; CommandID menuID = null; if (e.CurrentItem.Identifier == (int)TabButtonIds.MultiPage) menuID = WorkflowMenuCommands.PageLayoutMenu; else if (e.CurrentItem.Identifier == (int)TabButtonIds.Zoom) menuID = WorkflowMenuCommands.ZoomMenu; else menuID = WorkflowMenuCommands.PanMenu; IMenuCommandService menuCommandService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); if (menuCommandService != null) menuCommandService.ShowContextMenu(menuID, buttonRect.Right, buttonRect.Top); } } private void EnsureScrollBars(HScrollBar newHorizScrollBar, VScrollBar newVertScrollBar) { try { SuspendLayout(); if (this.hScrollBar != newHorizScrollBar) { if (this.hScrollBar != null) { this.hScrollBar.ValueChanged -= new EventHandler(OnScroll); if (Controls.Contains(this.hScrollBar)) Controls.Remove(this.hScrollBar); } this.hScrollBar = newHorizScrollBar; if (this.hScrollBar.Parent == null) { this.hScrollBar.TabStop = false; Controls.Add(this.hScrollBar); } } if (this.vScrollBar != newVertScrollBar) { if (this.vScrollBar != null) { this.vScrollBar.ValueChanged -= new EventHandler(OnScroll); if (Controls.Contains(this.vScrollBar)) Controls.Remove(this.vScrollBar); } this.vScrollBar = newVertScrollBar; if (this.vScrollBar.Parent == null) { this.vScrollBar.TabStop = false; Controls.Add(this.vScrollBar); } } this.hScrollBar.ValueChanged += new EventHandler(OnScroll); this.vScrollBar.ValueChanged += new EventHandler(OnScroll); } finally { ResumeLayout(true); } } private void PopulateMessageFilters(bool stockFilters) { IListfilters = (stockFilters) ? this.stockMessageFilters : this.customMessageFilters; Debug.Assert(filters.Count == 0); if (stockFilters) { filters.Add(new GlyphManager()); filters.Add(new WindowManager()); } else { Debug.Assert(this.rootDesigner != null); if (Capture) Capture = false; IList customFilters = ((IWorkflowRootDesigner)this.rootDesigner).MessageFilters; foreach (WorkflowDesignerMessageFilter filter in customFilters) filters.Add(filter); } foreach (WorkflowDesignerMessageFilter filter in filters) filter.SetParentView(this); } private void DisposeMessageFilters(bool stockFilters) { List filters = (stockFilters) ? this.stockMessageFilters : this.customMessageFilters; //We dispose all the message filters, this is done by copying because some of the //message filters might remove other dependent messagefilters ArrayList clonedFilterList = new ArrayList(filters.ToArray()); foreach (WorkflowDesignerMessageFilter filter in clonedFilterList) ((IDisposable)filter).Dispose(); filters.Clear(); } #endregion #region Coordinate Transformation Functions public void InvalidateClientRectangle(Rectangle clientRectangle) { if (this.layoutEventHandler == null) { if (!clientRectangle.IsEmpty) { //Inflate the invalidated rectangle. When zoom factor is less than 1; there is a loss of precision clientRectangle.Inflate(1, 1); base.Invalidate(clientRectangle); } else { base.Invalidate(); } } } public void InvalidateLogicalRectangle(Rectangle logicalRectangle) { InvalidateClientRectangle(LogicalRectangleToClient(logicalRectangle)); } public Point LogicalPointToScreen(Point logicalPoint) { return PointToScreen(LogicalPointToClient(logicalPoint)); } public Point ScreenPointToLogical(Point screenPoint) { return ClientPointToLogical(PointToClient(screenPoint)); } public Point LogicalPointToClient(Point logicalPoint) { return LogicalPointToClient(logicalPoint, true); } public Point ClientPointToLogical(Point clientPoint) { return ClientPointToLogical(clientPoint, true); } public Size LogicalSizeToClient(Size logicalSize) { Point[] points = new Point[] { new Point(logicalSize) }; //Scale the point Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); scalingMatrix.TransformPoints(points); return new Size(points[0]); } public Size ClientSizeToLogical(Size clientSize) { //Scale the size, size scaling does not require translate Point[] points = new Point[] { new Point(clientSize) }; Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); scalingMatrix.Invert(); scalingMatrix.TransformPoints(points); scalingMatrix.Invert(); return new Size(points[0]); } public Rectangle LogicalRectangleToClient(Rectangle rectangle) { Debug.Assert(this.activeLayout != null, "active layout should not be null"); Rectangle clientViewPort = (this.activeLayout != null) ? this.activeLayout.MapOutRectangleFromLayout(rectangle) : rectangle; // return new Rectangle(LogicalPointToClient(clientViewPort.Location, false), LogicalSizeToClient(clientViewPort.Size)); } public Rectangle ClientRectangleToLogical(Rectangle rectangle) { //We translate the client viewport to logical view port. //To do this we first get the view port rectangle scale it down //then translate it to area of page we would be viewing Rectangle scaledLogicalViewPort = new Rectangle(ClientPointToLogical(rectangle.Location, false), ClientSizeToLogical(rectangle.Size)); return this.activeLayout.MapInRectangleToLayout(scaledLogicalViewPort); } internal bool IsClientPointInActiveLayout(Point clientPoint) { Point logicalPoint = ClientPointToLogical(clientPoint, false); return this.activeLayout.IsCoOrdInLayout(logicalPoint); } /* * Client scale is when we transform the coordinate based on zoom and translate it based on scrolling position and layout * Logical scale is when we transform the coordinate and map it to a flat coordinate system which goes from 0,0 to m,n * We also consider the ActiveLayout to transform the coordinates. */ private Point LogicalPointToClient(Point point, bool mapToLayout) { if (mapToLayout) point = this.activeLayout.MapOutCoOrdFromLayout(point); //Scale the point Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); Point[] points = new Point[] { point }; scalingMatrix.TransformPoints(points); //Translate the point Matrix translateMatrix = new Matrix(); translateMatrix.Translate(-ScrollPosition.X, -ScrollPosition.Y); translateMatrix.TransformPoints(points); return points[0]; } private Point ClientPointToLogical(Point point, bool mapToLayout) { Point[] points = new Point[] { point }; //Translate the point Matrix translateMatrix = new Matrix(); translateMatrix.Translate(ScrollPosition.X, ScrollPosition.Y); translateMatrix.TransformPoints(points); //Scale down the point Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); scalingMatrix.Invert(); scalingMatrix.TransformPoints(points); scalingMatrix.Invert(); if (!mapToLayout) return points[0]; else return this.activeLayout.MapInCoOrdToLayout(points[0]); } #endregion #region IServiceProvider Implemetation object IServiceProvider.GetService(Type serviceType) { return GetService(serviceType); } protected override object GetService(Type serviceType) { object retVal = null; if (serviceType == typeof(CommandID)) retVal = new CommandID(new Guid("5f1c3c8d-60f1-4b98-b85b-8679f97e8eac"), 0); else retVal = this.serviceProvider.GetService(serviceType); return retVal; } #endregion #region Class WorkflowMessageDispatchData private sealed class WorkflowMessageDispatchData : IDisposable { private WorkflowView workflowView; private HitTestInfo messageContext = null; public WorkflowMessageDispatchData(WorkflowView workflowView, EventArgs e) { this.workflowView = workflowView; if (this.workflowView.RootDesigner != null && this.workflowView.stockMessageFilters.Count > 0) { Point clientPoint = Point.Empty; if (e is MouseEventArgs || e is DragEventArgs) { if (e is MouseEventArgs) { clientPoint = new Point(((MouseEventArgs)e).X, ((MouseEventArgs)e).Y); } else if (e is DragEventArgs) { clientPoint = this.workflowView.PointToClient(new Point(((DragEventArgs)e).X, ((DragEventArgs)e).Y)); this.workflowView.UpdateLayout(); } Point logicalPoint = this.workflowView.ClientPointToLogical(clientPoint); HitTestInfo hitTestInfo = this.workflowView.RootDesigner.HitTest(logicalPoint); this.messageContext = (hitTestInfo != null) ? hitTestInfo : HitTestInfo.Nowhere; this.workflowView.messageHitTestContexts.Push(this.messageContext); } } } void IDisposable.Dispose() { if (this.workflowView != null && this.messageContext != null) { HitTestInfo hittestInfo = this.workflowView.messageHitTestContexts.Pop(); if (hittestInfo != this.messageContext) Debug.Assert(false, "WorkflowView poped wrong message context"); } } public ReadOnlyCollection Filters { get { //We recreate a new list everytime as in some of the messages dispatched, we there can //be additional filters which might be added List mergedFilterList = new List (); mergedFilterList.AddRange(this.workflowView.customMessageFilters); mergedFilterList.AddRange(this.workflowView.stockMessageFilters); return mergedFilterList.AsReadOnly(); } } } #endregion #region IMessageFilter Implementation [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] bool IMessageFilter.PreFilterMessage(ref Message m) { bool handled = false; if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN || m.Msg == NativeMethods.WM_KEYUP || m.Msg == NativeMethods.WM_SYSKEYUP) { Control control = Control.FromHandle(m.HWnd); if (control != null && (control == this || Controls.Contains(control))) { KeyEventArgs eventArgs = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN) OnKeyDown(eventArgs); else OnKeyUp(eventArgs); handled = eventArgs.Handled; } } return handled; } #endregion } #region Class WorkflowViewAccessibleObject public class WorkflowViewAccessibleObject : Control.ControlAccessibleObject { private WorkflowView workflowView; public WorkflowViewAccessibleObject(WorkflowView workflowView) : base(workflowView) { if (workflowView == null) throw new ArgumentNullException("workflowView"); this.workflowView = workflowView; } public override Rectangle Bounds { get { return new Rectangle(this.workflowView.PointToScreen(Point.Empty), this.workflowView.ViewPortSize); } } public override string DefaultAction { get { return DR.GetString(DR.AccessibleAction); } } public override string Description { get { return DR.GetString(DR.WorkflowViewAccessibleDescription); } } public override string Help { get { return DR.GetString(DR.WorkflowViewAccessibleHelp); } } public override string Name { get { return DR.GetString(DR.WorkflowViewAccessibleName); } set { } } public override AccessibleRole Role { get { return AccessibleRole.Diagram; } } public override AccessibleObject GetChild(int index) { return (this.workflowView.RootDesigner != null && index == 0) ? this.workflowView.RootDesigner.AccessibilityObject : base.GetChild(index); } public override int GetChildCount() { return (this.workflowView.RootDesigner != null) ? 1 : -1; } public override AccessibleObject Navigate(AccessibleNavigation navdir) { if (navdir == AccessibleNavigation.FirstChild || navdir == AccessibleNavigation.LastChild) return GetChild(0); else return base.Navigate(navdir); } } #endregion #region WorkflowTimer internal sealed class WorkflowTimer : IDisposable { private static WorkflowTimer workflowTimer; private const int TimerInterval = 50; private Timer timer = null; private List elapsedEvents = new List (); internal static WorkflowTimer Default { get { if (WorkflowTimer.workflowTimer == null) WorkflowTimer.workflowTimer = new WorkflowTimer(); return WorkflowTimer.workflowTimer; } } private WorkflowTimer() { this.timer = new Timer(); this.timer.Interval = WorkflowTimer.TimerInterval; this.timer.Tick += new EventHandler(OnTimer); this.timer.Stop(); } ~WorkflowTimer() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (this.timer != null) { if (this.timer.Enabled) this.timer.Stop(); this.timer.Dispose(); this.timer = null; } } internal void Subscribe(int elapsedInterval, EventHandler elapsedEventHandler) { this.elapsedEvents.Add(new ElapsedEventUnit(elapsedInterval / WorkflowTimer.TimerInterval, elapsedEventHandler)); if (!this.timer.Enabled) this.timer.Start(); } internal void Unsubscribe(EventHandler elapsedEventHandler) { List removableElapsedEvents = new List (); foreach (ElapsedEventUnit elapsedEvent in this.elapsedEvents) { if (elapsedEvent.elapsedEventHandler == elapsedEventHandler) removableElapsedEvents.Add(elapsedEvent); } foreach (ElapsedEventUnit elapsedEvent in removableElapsedEvents) this.elapsedEvents.Remove(elapsedEvent); if (this.elapsedEvents.Count == 0 && this.timer.Enabled) this.timer.Stop(); } private void OnTimer(object sender, EventArgs e) { List clonedList = new List (this.elapsedEvents); foreach (ElapsedEventUnit elapsedEvent in clonedList) { elapsedEvent.elapsedTime += 1; if (elapsedEvent.elapsedInterval <= elapsedEvent.elapsedTime) { elapsedEvent.elapsedTime = 0; elapsedEvent.elapsedEventHandler(this, EventArgs.Empty); } } } private sealed class ElapsedEventUnit { internal EventHandler elapsedEventHandler; internal int elapsedInterval; internal int elapsedTime; internal ElapsedEventUnit(int interval, EventHandler eventHandler) { this.elapsedInterval = interval; this.elapsedEventHandler = eventHandler; } } } #endregion } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. namespace System.Workflow.ComponentModel.Design { using System; using System.IO; using System.Data; using System.Drawing; using System.Security; using System.Resources; using System.Reflection; using System.Diagnostics; using System.Collections; using System.Windows.Forms; using System.ComponentModel; using System.Drawing.Design; using System.Drawing.Imaging; using System.Drawing.Printing; using System.Drawing.Drawing2D; using System.Workflow.Interop; using System.Collections.Generic; using System.Windows.Forms.Design; using System.Security.Permissions; using System.ComponentModel.Design; using System.Runtime.InteropServices; using System.Collections.ObjectModel; ///What did I change in this file ///1. Eliminated the layout manager and introduced classes for WorkflowLayout and PrintPreviewLayout ///2. Eliminated the event syncing of PageSetupData change. We call performlayout on the current designer service whenever the pagesetupdata changes /// Designer Features: /// Selection on click and thru drag rectangle /// Reconfigurable background /// Scrolling /// Ensure Visible functionality /// Accessibility /// Zoom /// Rehostable /// Extensible /// Small memory footprint /// Ability to move around objects using drag drop /// Ability to drop the objects using drag drop /// Printing support /// Theme support /// Magnifier /// AutoScroll /// AutoExpand /// USE THIS FOR PERFORMANCE TEST: Debug.WriteLine("******Root drawing: " + Convert.ToString((DateTime.Now.Ticks - ticks) / 10000) + "ms"); /// /// Here are some details about the coordinate system, /// /// Screen CoOrdinate System: Starts at 0,0 of the screen /// Client CoOrdinate System: Starts at 0,0 of the control /// Logical CoOrdinate System: The workflowview supports zooming and scroll, we want to hide this /// complexity from the activity writter and hence whenever we get a coordinate we translate it based /// scroll position, zoom level and layout. This helps us to sheild the activity designers from complexity /// of zooming, scaling and layouting. The designer writters deal with one coordinate system which is unscaled and /// starts at 0,0 /// /// [ToolboxItem(false)] [ActivityDesignerTheme(typeof(AmbientTheme), Xml = WorkflowView.ThemeXml)] public class WorkflowView : UserControl, IServiceProvider, IMessageFilter { #region Theme Initializer XML internal const string ThemeXml = " "; #endregion #region Members Variables private enum TabButtonIds { MultiPage = 1, Zoom, Pan} //Designer Hookup private IServiceProvider serviceProvider = null; private ActivityDesigner rootDesigner = null; //Zoom private float zoomLevel = 1.0f; private int shadowDepth = WorkflowTheme.CurrentTheme.AmbientTheme.ShadowDepth; //MessageFilters private List stockMessageFilters = new List (); private List customMessageFilters = new List (); // private Bitmap viewPortBitmap = null; //Misc. private WorkflowToolTip workflowToolTip = null; private CommandSet commandSet = null; private DynamicAction fitAllAction = null; //print private int prePreviewZoom = 100; private Point prePreviewScroll = Point.Empty; private WorkflowPrintDocument printDocument = null; //Active layout private WorkflowLayout activeLayout = null; private WorkflowLayout defaultLayout = null; //One time callable delegates private EventHandler layoutEventHandler = null; private EventHandler ensureVisibleEventHandler = null; private Stack messageHitTestContexts = new Stack (); private HScrollBar hScrollBar; private VScrollBar vScrollBar; private TabControl toolContainer; private EventHandler idleEventListeners; private EventHandler idleEventHandler; private bool dragDropInProgress; #endregion #region Events public event EventHandler ZoomChanged; public event EventHandler RootDesignerChanged; #endregion #region Constructor and Dispose public WorkflowView() : this(new DesignSurface()) { } public WorkflowView(IServiceProvider serviceProvider) { Debug.Assert(serviceProvider != null); if (serviceProvider == null) throw new ArgumentNullException("serviceProvider"); SuspendLayout(); AllowDrop = true; AutoScroll = false; HScroll = false; VScroll = false; SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint | ControlStyles.Selectable | ControlStyles.EnableNotifyMessage, true); this.serviceProvider = serviceProvider; //*****Promote the services which are accessed from other components IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer; if (serviceContainer != null) { //Remove any existing designer service if there is any serviceContainer.RemoveService(typeof(WorkflowView)); serviceContainer.AddService(typeof(WorkflowView), this); } //set the UI Service to be used by themes IUIService uiService = this.serviceProvider.GetService(typeof(IUIService)) as IUIService; if(uiService != null) WorkflowTheme.UIService = uiService; //Make sure that we add scrollbars EnsureScrollBars(new HScrollBar(), new VScrollBar()); //Initialize the tooltip shown this.workflowToolTip = new WorkflowToolTip(this); //[....] the global theme change event, which is fired by the theme infrastructure for theme change WorkflowTheme.ThemeChanged += new EventHandler(OnThemeChange); //Create the core message filters PopulateMessageFilters(true); //Set the root designer, note that the dynamic action is dependent on the DynamicActionMessageFilter pushed //when the root is set. RootDesigner = ActivityDesigner.GetSafeRootDesigner(this); this.fitAllAction = CreateDynamicAction(); //If the active layout is still null then we will set the default layout as active layout if (this.activeLayout == null || this.defaultLayout == null) ActiveLayout = DefaultLayout = new WorkflowRootLayout(this.serviceProvider); //Create the local command set and update all the commands once IMenuCommandService menuCommandService = GetService(typeof(IMenuCommandService)) as IMenuCommandService; if (menuCommandService != null) { this.commandSet = new CommandSet(this); this.commandSet.UpdatePanCommands(true); } //Subscribe to selection change ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService; if (selectionService != null) selectionService.SelectionChanged += new EventHandler(OnSelectionChanged); //In case of non VS case we need to pumpin the Keyboard messages, the user control sets //focus to the child controls by default which is a problem so we need to trap the //messages by adding application level message filter, in case of VS this is not required and //the message filter is never called. Application.AddMessageFilter(this); //We make sure that during the construction we dont do perform layouts on idle event ResumeLayout(true); } protected override void Dispose(bool disposing) { //Remove the proffered services if (disposing) { try { SuspendLayout(); Application.RemoveMessageFilter(this); if (this.layoutEventHandler != null) { Idle -= this.layoutEventHandler; this.layoutEventHandler = null; } if (this.ensureVisibleEventHandler != null) { Idle -= this.ensureVisibleEventHandler; this.ensureVisibleEventHandler = null; } if (this.idleEventHandler != null) { this.idleEventListeners = null; Form host = TopLevelControl as Form; if (!Application.MessageLoop || (host != null && host.Modal)) WorkflowTimer.Default.Unsubscribe(this.idleEventHandler); else Application.Idle -= this.idleEventHandler; this.idleEventHandler = null; } ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService; if (selectionService != null) selectionService.SelectionChanged -= new EventHandler(OnSelectionChanged); //Unsubscribe the theme change WorkflowTheme.ThemeChanged -= new EventHandler(OnThemeChange); //Remove the dynamic action if (this.fitAllAction != null) { this.fitAllAction.Dispose(); this.fitAllAction = null; } if (this.workflowToolTip != null) { ((IDisposable)this.workflowToolTip).Dispose(); this.workflowToolTip = null; } DisposeMessageFilters(false); DisposeMessageFilters(true); //Dispose the layouts this.activeLayout = null; if (this.defaultLayout != null) { this.defaultLayout.Dispose(); this.defaultLayout = null; } //Destroy other resources if (this.viewPortBitmap != null) { this.viewPortBitmap.Dispose(); this.viewPortBitmap = null; } if (this.commandSet != null) { this.commandSet.Dispose(); this.commandSet = null; } HScrollBar.ValueChanged -= new EventHandler(OnScroll); VScrollBar.ValueChanged -= new EventHandler(OnScroll); if (this.toolContainer != null) { Controls.Remove(this.toolContainer); this.toolContainer.TabStrip.Tabs.Clear(); this.toolContainer.Dispose(); this.toolContainer = null; } IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer; if (serviceContainer != null) { serviceContainer.RemoveService(typeof(WorkflowView)); } } finally { ResumeLayout(false); } } base.Dispose( disposing ); } #endregion #region Public Properties public int Zoom { get { return Convert.ToInt32(this.zoomLevel * 100); } set { if (Zoom == value) return; if (value < AmbientTheme.MinZoom || value > AmbientTheme.MaxZoom) throw new NotSupportedException(DR.GetString(DR.ZoomLevelException2, AmbientTheme.MinZoom, AmbientTheme.MaxZoom)); ScrollBar hScrollBar = HScrollBar; ScrollBar vScrollBar = VScrollBar; if (hScrollBar != null && vScrollBar != null) { PointF oldRelativeCenter = Point.Empty; Point oldCenter = new Point(ScrollPosition.X, ScrollPosition.Y); oldRelativeCenter = new PointF((float)oldCenter.X / (float)hScrollBar.Maximum, (float)oldCenter.Y / (float)vScrollBar.Maximum); //recalculate the zoom and scroll range this.zoomLevel = (float)value / 100.0f; UpdateScrollRange(); //center the view again Point newCenter = new Point((int)((float)hScrollBar.Maximum * oldRelativeCenter.X), (int)((float)vScrollBar.Maximum * oldRelativeCenter.Y)); ScrollPosition = new Point(newCenter.X, newCenter.Y); if (this.rootDesigner != null) this.rootDesigner.Location = this.activeLayout.RootDesignerAlignment; InvalidateClientRectangle(Rectangle.Empty); // this.activeLayout.Update(null, WorkflowLayout.LayoutUpdateReason.ZoomChanged); //force command refresh //this is to workarond VS not refreshing Zoom drop down when doing area zoom-in IUIService uis = GetService(typeof(IUIService)) as IUIService; if (uis != null) uis.SetUIDirty(); //We need to update the zoom commands when the zoom is updated if (this.commandSet != null) this.commandSet.UpdateZoomCommands(true); OnZoomChanged(); } } } public ActivityDesigner RootDesigner { get { return this.rootDesigner; } set { if (this.rootDesigner == value) return; DisposeMessageFilters(false); this.rootDesigner = value; if (this.rootDesigner != null) { PopulateMessageFilters(false); ActiveLayout = DefaultLayout = this.rootDesigner.SupportedLayout; } OnRootDesignerChanged(); base.PerformLayout(); } } public int ShadowDepth { get { return this.shadowDepth; } set { if (value < AmbientTheme.MinShadowDepth || value > AmbientTheme.MaxShadowDepth) throw new NotSupportedException(DR.GetString(DR.ShadowDepthException, AmbientTheme.MinShadowDepth, AmbientTheme.MaxShadowDepth)); if (this.shadowDepth == value) return; this.shadowDepth = value; InvalidateClientRectangle(Rectangle.Empty); } } public Rectangle ViewPortRectangle { get { return new Rectangle(ScrollPosition, ViewPortSize); } } public Size ViewPortSize { get { Size viewPortSize = ClientSize; if (HScrollBar.Visible) viewPortSize.Height = Math.Max(0, viewPortSize.Height - HScrollBar.Height); if (VScrollBar.Visible) viewPortSize.Width = Math.Max(0, viewPortSize.Width - VScrollBar.Width); return viewPortSize; } } public Point ScrollPosition { get { return new Point(HScrollBar.Value, VScrollBar.Value); } set { ScrollBar hScrollBar = HScrollBar; if (hScrollBar != null) { value.X = Math.Min(value.X, hScrollBar.Maximum - hScrollBar.LargeChange + 1); value.X = Math.Max(value.X, hScrollBar.Minimum); hScrollBar.Value = value.X; } ScrollBar vScrollBar = VScrollBar; if (vScrollBar != null) { value.Y = Math.Min(value.Y, vScrollBar.Maximum - vScrollBar.LargeChange + 1); value.Y = Math.Max(value.Y, vScrollBar.Minimum); vScrollBar.Value = value.Y; } } } public bool PrintPreviewMode { get { return (this.activeLayout == ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout); } set { if (PrintPreviewMode == value) return; if (value && PrinterSettings.InstalledPrinters.Count == 0) { DesignerHelpers.ShowError(this, DR.GetString(DR.ThereIsNoPrinterInstalledErrorMessage)); value = false; } ActiveLayout = (value) ? ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout : DefaultLayout; if (this.commandSet != null) this.commandSet.UpdatePageLayoutCommands(true); if (PrintPreviewMode) { this.prePreviewZoom = Zoom; this.prePreviewScroll = ScrollPosition; Zoom = 40; } else { Zoom = this.prePreviewZoom; ScrollPosition = this.prePreviewScroll; } } } public PrintDocument PrintDocument { get { if (this.printDocument == null) this.printDocument = new WorkflowPrintDocument(this); return this.printDocument; } } public event EventHandler Idle { add { //Add the listener to our list this.idleEventListeners += value; if (this.idleEventHandler == null) { this.idleEventHandler = new EventHandler(OnWorkflowIdle); Form host = TopLevelControl as Form; if (!Application.MessageLoop || (host != null && host.Modal)) WorkflowTimer.Default.Subscribe(100, this.idleEventHandler); else Application.Idle += this.idleEventHandler; } } remove { this.idleEventListeners -= value; if (this.idleEventHandler != null && this.idleEventListeners == null) { Form host = TopLevelControl as Form; if (host != null && host.Modal) WorkflowTimer.Default.Unsubscribe(this.idleEventHandler); else Application.Idle -= this.idleEventHandler; this.idleEventHandler = null; } } } public HScrollBar HScrollBar { get { return this.hScrollBar; } } public VScrollBar VScrollBar { get { return this.vScrollBar; } } public bool EnableFitToScreen { get { return (this.fitAllAction != null); } set { if (EnableFitToScreen == value) return; if (value) { if (this.fitAllAction == null) this.fitAllAction = CreateDynamicAction(); } else { if (this.fitAllAction != null) { this.fitAllAction.Dispose(); this.fitAllAction = null; } } InvalidateClientRectangle(Rectangle.Empty); } } #endregion #region Protected Properties #endregion #region Private Properties internal bool DragDropInProgress { get { return this.dragDropInProgress; } } internal bool ShowToolContainer { get { return (this.toolContainer != null); } set { if (ShowToolContainer == value) return; try { SuspendLayout(); if (value) { this.toolContainer = new TabControl(DockStyle.Right, AnchorAlignment.Far); Controls.Add(this.toolContainer); EnsureScrollBars(this.hScrollBar, this.toolContainer.ScrollBar as VScrollBar); string[,] tabButtonInfo = new string[/*Caption Resource ID*/,/*Bitmap Resource ID*/ ] { { "MultipageLayoutCaption", "MultipageLayout" }, { "ZoomCaption", "Zoom" }, { "PanCaption", "AutoPan" } }; for (int i = 0; i < tabButtonInfo.GetLength(0); i++) { Bitmap tabImage = DR.GetImage(tabButtonInfo[i, 1]) as Bitmap; string buttonCaption = DR.GetString(tabButtonInfo[i, 0]); this.toolContainer.TabStrip.Tabs.Add(new ItemInfo(i + 1, tabImage, buttonCaption)); } this.toolContainer.TabStrip.TabChange += new SelectionChangeEventHandler (OnTabChange); if (this.commandSet != null) { this.commandSet.UpdatePageLayoutCommands(true); this.commandSet.UpdateZoomCommands(true); this.commandSet.UpdatePanCommands(true); } } else { this.toolContainer.TabStrip.TabChange -= new SelectionChangeEventHandler (OnTabChange); this.toolContainer.TabStrip.Tabs.Clear(); Controls.Remove(this.toolContainer); this.toolContainer.Dispose(); this.toolContainer = null; EnsureScrollBars(this.hScrollBar, new VScrollBar()); } } finally { ResumeLayout(true); } } } internal HitTestInfo MessageHitTestContext { get { return this.messageHitTestContexts.Peek(); } } internal WorkflowLayout ActiveLayout { get { return this.activeLayout; } set { Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("Layout cannot be null!"); Cursor cursor = Cursor.Current; try { Cursor.Current = Cursors.WaitCursor; this.activeLayout = value; if (this.activeLayout != ((WorkflowPrintDocument)PrintDocument).PrintPreviewLayout) DefaultLayout = this.activeLayout; base.PerformLayout(); if (this.commandSet != null) this.commandSet.UpdatePageLayoutCommands(true); } finally { Cursor.Current = cursor; } } } private WorkflowLayout DefaultLayout { get { if (this.defaultLayout == null) this.defaultLayout = new WorkflowRootLayout(this); return this.defaultLayout; } set { if (value == null) throw new ArgumentNullException(DR.GetString(DR.Error_WorkflowLayoutNull)); if (this.defaultLayout == value) return; if (this.defaultLayout != null) this.defaultLayout.Dispose(); this.defaultLayout = value; } } private float ScaleZoomFactor { get { return (this.zoomLevel * this.activeLayout.Scaling); } } #endregion #region Public Methods public void AddDesignerMessageFilter(WorkflowDesignerMessageFilter designerMessageFilter) { if (designerMessageFilter == null) throw new ArgumentNullException("designerMessageFilter"); if (Capture) Capture = false; this.customMessageFilters.Insert(0, designerMessageFilter); designerMessageFilter.SetParentView(this); } public void RemoveDesignerMessageFilter(WorkflowDesignerMessageFilter designerMessageFilter) { if (designerMessageFilter == null) throw new ArgumentNullException("designerMessageFilter"); if (this.customMessageFilters.Contains(designerMessageFilter)) { if (Capture) Capture = false; this.customMessageFilters.Remove(designerMessageFilter); ((IDisposable)designerMessageFilter).Dispose(); } } public void ShowInPlaceToolTip(string toolTipText, Rectangle toolTipRectangle) { if (toolTipText == null) throw new ArgumentNullException("toolTipText"); if (toolTipRectangle.IsEmpty) throw new ArgumentException(SR.GetString(SR.Error_EmptyToolTipRectangle)); this.workflowToolTip.SetText(toolTipText, toolTipRectangle); } public void ShowInfoTip(string text) { if (text == null) throw new ArgumentNullException("text"); this.workflowToolTip.SetText(String.Empty, text); } public void ShowInfoTip(string title, string text) { if (title == null) throw new ArgumentNullException("title"); if (text == null) throw new ArgumentNullException("text"); this.workflowToolTip.SetText(title, text); } public void EnsureVisible(object selectableObject) { if (selectableObject == null) throw new ArgumentNullException("selectableObject"); // make sure that all the parents are expanded Activity activity = selectableObject as Activity; while (activity != null) { ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(activity); CompositeActivityDesigner parentDesigner = activityDesigner.ParentDesigner; if (parentDesigner != null) { if (activityDesigner != null) parentDesigner.EnsureVisibleContainedDesigner(activityDesigner); activity = parentDesigner.Activity; } else { activity = null; } } //this is to handle the case when we call ensure visible of a scope which currently has //activity from the secondary flow selected. instead we should always switch to the main flow activity = selectableObject as Activity; if (activity != null) { CompositeActivityDesigner compositeDesigner = ActivityDesigner.GetDesigner(activity) as CompositeActivityDesigner; if (compositeDesigner != null) compositeDesigner.EnsureVisibleContainedDesigner(compositeDesigner); } PerformLayout(false); if (this.ensureVisibleEventHandler == null) { this.ensureVisibleEventHandler = new EventHandler(OnEnsureVisible); Idle += this.ensureVisibleEventHandler; } } public void PerformLayout(bool immediateUpdate) { if (immediateUpdate) { if (this.layoutEventHandler != null) { Idle -= this.layoutEventHandler; this.layoutEventHandler = null; } base.PerformLayout();//invalidate rectangle really cares for the this.layoutEventHandler being null } else if (this.layoutEventHandler == null) { this.layoutEventHandler = new EventHandler(OnPerformLayout); Idle += this.layoutEventHandler; } } public void SaveViewState(Stream viewState) { if (viewState == null) throw new ArgumentNullException("viewState"); IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); if (designerHost == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName)); BinaryWriter writer = new BinaryWriter(viewState); // write workflow properties writer.Write(this.PrintPreviewMode); writer.Write(this.Zoom); // write components DesignerHelpers.SerializeDesignerStates(designerHost, writer); // write scroll position writer.Write(this.ScrollPosition.X); writer.Write(this.ScrollPosition.Y); } public void LoadViewState(Stream viewState) { if (viewState == null) throw new ArgumentNullException("viewState"); bool outdated = false; Point scrollPosition = new Point(0, 0); IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost)); if (designerHost == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName)); viewState.Position = 0; BinaryReader reader = new BinaryReader(viewState); // read workflow properties this.PrintPreviewMode = reader.ReadBoolean(); this.Zoom = reader.ReadInt32(); try { // get activities outdated = DesignerHelpers.DeserializeDesignerStates(designerHost, reader); // we will apply the scrolling only if if there is perfect match // between the components in the workflow and the persisted data. // It might be different if files were updated outside of VS, or if // VS crashes. if (!outdated) { scrollPosition.X = reader.ReadInt32(); scrollPosition.Y = reader.ReadInt32(); } } finally { // flush the layout to apply the new settings, this will set the scrollers extents base.PerformLayout(); this.ScrollPosition = scrollPosition; } } /// /// Changes zoom level on the design surface such that the entire workflow is displayed in the view /// public void FitToScreenSize() { if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height) { int newZoom = (int)(100.0f / ActiveLayout.Scaling * Math.Min((float)ViewPortSize.Width / (float)ActiveLayout.Extent.Width, (float)ViewPortSize.Height / (float)ActiveLayout.Extent.Height)); Zoom = Math.Min(Math.Max(newZoom, AmbientTheme.MinZoom), AmbientTheme.MaxZoom); } } ////// Sets the zoom level to 100% so that the workflow size is restored to actial workflow size /// public void FitToWorkflowSize() { if (Zoom != 100) Zoom = 100; } ////// Saves workflow as image to a file based on the format specified /// /// Path to file where to save the image /// Format in which to save the image public void SaveWorkflowImage(string imageFile, ImageFormat imageFormat) { if (imageFile == null) throw new ArgumentNullException("imageFile"); if (imageFormat == null) throw new ArgumentNullException("imageFormat"); Bitmap workflowBitmap = TakeWorkflowSnapShot(); if (workflowBitmap != null) { workflowBitmap.Save(imageFile, imageFormat); workflowBitmap.Dispose(); } } ////// Saves workflow as image to a stream based on encoding specified /// /// Stream where to save the workflow /// Format in which to save the image public void SaveWorkflowImage(Stream stream, ImageFormat imageFormat) { if (stream == null) throw new ArgumentNullException("stream"); if (imageFormat == null) throw new ArgumentNullException("imageFormat"); Bitmap workflowBitmap = TakeWorkflowSnapShot(); if (workflowBitmap != null) { workflowBitmap.Save(stream, imageFormat); workflowBitmap.Dispose(); } } ////// Stores the workflow image to clipboard. /// public void SaveWorkflowImageToClipboard() { Bitmap workflowBitmap = TakeWorkflowSnapShot(); if (workflowBitmap != null) { Clipboard.SetDataObject(workflowBitmap, true); workflowBitmap.Dispose(); } } #endregion #region Protected Methods #region Overridden Methods handling UI events #region Drawing protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //We set the highest quality interpolation so that we do not loose the image quality GraphicsContainer graphicsState = e.Graphics.BeginContainer(); e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; e.Graphics.CompositingQuality = CompositingQuality.HighQuality; bool takeWorkflowSnapShot = (this.viewPortBitmap == null || this.viewPortBitmap.Size != ViewPortSize); if (takeWorkflowSnapShot) { if (this.viewPortBitmap != null) this.viewPortBitmap.Dispose(); this.viewPortBitmap = new Bitmap(Math.Max(1, ViewPortSize.Width), Math.Max(1, ViewPortSize.Height), e.Graphics); } //Create viewport information and take the workflow snapshot before passing on the information to the active layout ViewPortData viewPortData = new ViewPortData(); viewPortData.LogicalViewPort = ClientRectangleToLogical(new Rectangle(Point.Empty, ViewPortSize)); viewPortData.MemoryBitmap = this.viewPortBitmap; viewPortData.Scaling = new SizeF(ScaleZoomFactor, ScaleZoomFactor); viewPortData.Translation = ScrollPosition; viewPortData.ShadowDepth = new Size(this.shadowDepth, this.shadowDepth); viewPortData.ViewPortSize = ViewPortSize; //capture the workflow onto in-memory bitmap if (this.layoutEventHandler == null || takeWorkflowSnapShot) WorkflowView.TakeWorkflowSnapShot(this, viewPortData); //copy workflow from the bitmap onto corresponding pages on the screen try { this.activeLayout.OnPaintWorkflow(e, viewPortData); } catch (Exception ex) { //If a layout throws an exception then we will not draw the layout // Debug.WriteLine(ex); } //If any of the message filters throws an exception we continue to draw using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { if (((IWorkflowDesignerMessageSink)filter).OnPaintWorkflowAdornments(e, ViewPortRectangle)) break; } catch (Exception ex) { //Ignore the filter throwing the exception and continue to function Debug.WriteLine(ex); } } } e.Graphics.EndContainer(graphicsState); e.Graphics.FillRectangle(SystemBrushes.Control, new Rectangle(Width - SystemInformation.VerticalScrollBarWidth, Height - SystemInformation.HorizontalScrollBarHeight, SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight)); } protected virtual void OnZoomChanged() { if (this.ZoomChanged != null) this.ZoomChanged(this, EventArgs.Empty); } protected virtual void OnRootDesignerChanged() { if (this.RootDesignerChanged != null) this.RootDesignerChanged(this, EventArgs.Empty); } #endregion #region Mouse Events protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseDown(e)) break; } } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseMove(e)) break; } } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseUp(e)) break; } } } protected override void OnMouseDoubleClick(MouseEventArgs e) { base.OnMouseDoubleClick(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseDoubleClick(e)) break; } } } protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); Point clientPoint = PointToClient(Control.MousePosition); MouseEventArgs eventArgs = new MouseEventArgs(Control.MouseButtons, 1, clientPoint.X, clientPoint.Y, 0); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, eventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseEnter(eventArgs)) break; } } } protected override void OnMouseHover(EventArgs e) { base.OnMouseHover(e); Point clientPoint = PointToClient(Control.MousePosition); MouseEventArgs eventArgs = new MouseEventArgs(Control.MouseButtons, 1, clientPoint.X, clientPoint.Y, 0); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, eventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseHover(eventArgs)) break; } } } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseLeave()) break; } } } protected override void OnMouseCaptureChanged(EventArgs e) { base.OnMouseCaptureChanged(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseCaptureChanged()) break; } } } protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnMouseWheel(e)) break; } } } #endregion #region Keyboard Events protected override void OnKeyDown(KeyEventArgs e) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnKeyDown(e)) break; } } if (!e.Handled) base.OnKeyDown(e); } protected override void OnKeyUp(KeyEventArgs e) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnKeyUp(e)) break; } } if (!e.Handled) base.OnKeyUp(e); } #endregion #region Layouting Events protected override void OnLayout(LayoutEventArgs levent) { base.OnLayout(levent); ScrollBar hScrollBar = HScrollBar; ScrollBar vScrollBar = VScrollBar; if (Controls.Contains(hScrollBar)) hScrollBar.Bounds = new Rectangle(0, Math.Max(0, Height - SystemInformation.HorizontalScrollBarHeight), Math.Max(Width - ((vScrollBar.Visible) ? SystemInformation.VerticalScrollBarWidth : 0), 0), SystemInformation.HorizontalScrollBarHeight); if (Controls.Contains(vScrollBar)) vScrollBar.Bounds = new Rectangle(Math.Max(0, Width - SystemInformation.VerticalScrollBarWidth), 0, SystemInformation.VerticalScrollBarWidth, Math.Max(Height - ((hScrollBar.Visible) ? SystemInformation.HorizontalScrollBarHeight : 0), 0)); if (this.toolContainer != null) { this.toolContainer.Location = new Point(Width - this.toolContainer.Width, 0); this.toolContainer.Height = Height - ((hScrollBar.Visible) ? hScrollBar.Height :0); } using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, levent)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) ((IWorkflowDesignerMessageSink)filter).OnLayout(levent); } //Layout the designers using (Graphics graphics = CreateGraphics()) { this.activeLayout.Update(graphics, WorkflowLayout.LayoutUpdateReason.LayoutChanged); if (this.rootDesigner != null) this.rootDesigner.Location = this.activeLayout.RootDesignerAlignment; } //Update the scroll range and redraw UpdateScrollRange(); InvalidateClientRectangle(Rectangle.Empty); } #endregion #region DragDrop Events protected override void OnDragEnter(DragEventArgs dragEventArgs) { base.OnDragEnter(dragEventArgs); this.dragDropInProgress = true; using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragEnter(dragEventArgs)) break; } } } protected override void OnDragOver(DragEventArgs dragEventArgs) { base.OnDragOver(dragEventArgs); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragOver(dragEventArgs)) break; } } } protected override void OnDragLeave(EventArgs e) { base.OnDragLeave(e); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragLeave()) break; } } this.dragDropInProgress = false; } protected override void OnDragDrop(DragEventArgs dragEventArgs) { base.OnDragDrop(dragEventArgs); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, dragEventArgs)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnDragDrop(dragEventArgs)) break; } } this.dragDropInProgress = false; } protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent) { base.OnGiveFeedback(gfbevent); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, gfbevent)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnGiveFeedback(gfbevent)) break; } } } protected override void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent) { base.OnQueryContinueDrag(qcdevent); using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, qcdevent)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnQueryContinueDrag(qcdevent)) break; } } } #endregion #region General Events //Handle context menus here reason being it can come from mouse r button click //or shift+F10, or there might be other keys too //We need to handle the WndProc and not the OnNotifyMessage because we need to set //the m.Result to handled (IntPtr.Zero) and dont let the base class see the message at all //see WinOE #787 "The keyboard "key" to launch the context menu launches the menu at 0,0" [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] [UIPermission(SecurityAction.Assert, Window = UIPermissionWindow.AllWindows)] protected override void WndProc(ref Message m) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).ProcessMessage(m)) break; } const int WM_CONTEXTMENU = 0x007B; if (m.Msg == WM_CONTEXTMENU) { int LParam = (int)m.LParam; Point location = (LParam != -1) ? new Point(LParam) : Control.MousePosition; foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { if (((IWorkflowDesignerMessageSink)filter).OnShowContextMenu(location)) break; } //mark the message handled m.Result = IntPtr.Zero; //dont pass the message to the base but return immediatly return; } } if (this.workflowToolTip != null && m.Msg == NativeMethods.WM_NOTIFY) this.workflowToolTip.RelayParentNotify(ref m); try { if (m.Result == IntPtr.Zero) base.WndProc(ref m); } catch (Exception e) { if (e != CheckoutException.Canceled) DesignerHelpers.ShowError(this, e); } } protected override void OnControlAdded(ControlEventArgs e) { if (e.Control != VScrollBar && e.Control != HScrollBar && e.Control != this.toolContainer) throw new InvalidOperationException(SR.GetString(SR.Error_InsertingChildControls)); } protected override AccessibleObject CreateAccessibilityInstance() { return new WorkflowViewAccessibleObject(this); } #endregion #endregion #endregion #region Private Methods private void OnWorkflowIdle(object sender, EventArgs e) { if (this.idleEventListeners != null) this.idleEventListeners(this, e); } private void UpdateLayout() { if (this.layoutEventHandler != null) { PerformLayout(true); InvalidateClientRectangle(Rectangle.Empty); } } internal void OnCommandKey(KeyEventArgs e) { this.OnKeyDown(e); this.OnKeyUp(e); } private void OnSelectionChanged(object sender, EventArgs e) { if (this.commandSet != null) this.commandSet.UpdateCommandSet(); //Make sure that the ensure visible also works when the component is selected //from property browser dropdown //Make sure that when there is a selection change using the property browser //drop down we make sure that the designer associated with component selected by the user in the dropdown //is made visible. //To enable this functionality please note that selection change is not a good event as it will get //fired in multiple cases, instead we should add a event in extended ui service which will do this and move //the following code in the event handler of that event //Ref Bug#3925 if (RootDesigner != null && RootDesigner.Activity != null) { ISelectionService selectionService = GetService(typeof(ISelectionService)) as ISelectionService; if (selectionService != null && selectionService.GetComponentSelected(RootDesigner.Activity)) { IHelpService helpService = GetService(typeof(IHelpService)) as IHelpService; if (helpService != null) helpService.AddContextAttribute("Keyword", RootDesigner.Activity.GetType().FullName, HelpKeywordType.F1Keyword); } } } private void OnPerformLayout(object sender, EventArgs e) { if (this.layoutEventHandler != null) { Idle -= this.layoutEventHandler; this.layoutEventHandler = null; base.PerformLayout(); } } //Gets the snapshot of the entire workflow private Bitmap TakeWorkflowSnapShot() { Bitmap bitmap = null; ActivityDesigner rootDesigner = RootDesigner; if (rootDesigner != null) { using (Graphics graphics = CreateGraphics()) { ViewPortData viewPortData = new ViewPortData(); viewPortData.LogicalViewPort = new Rectangle(Point.Empty, new Size(rootDesigner.Bounds.Width + 2 * DefaultWorkflowLayout.Separator.Width, rootDesigner.Bounds.Height + 2 * DefaultWorkflowLayout.Separator.Height)); viewPortData.MemoryBitmap = new Bitmap(viewPortData.LogicalViewPort.Width, viewPortData.LogicalViewPort.Height, graphics); viewPortData.Scaling = new SizeF(1, 1); viewPortData.Translation = Point.Empty; viewPortData.ShadowDepth = new Size(0, 0); viewPortData.ViewPortSize = viewPortData.LogicalViewPort.Size; TakeWorkflowSnapShot(this, viewPortData); bitmap = viewPortData.MemoryBitmap; } } return bitmap; } //This function will give snapshot of what is drawn on the screen at any point of time //It will scale and translate the designers and drawing based on the viewport data //We need this function in OnPaint and taking snapshot of magnifier bitmap //At the end of this function; the ViewPortData.MemoryBitmap will contain the bitmap of the //workflow to be drawn as per layout internal static void TakeWorkflowSnapShot(WorkflowView workflowView, ViewPortData viewPortData) { //Get the drawing canvas Bitmap memoryBitmap = viewPortData.MemoryBitmap; Debug.Assert(memoryBitmap != null); using (Graphics viewPortGraphics = Graphics.FromImage(memoryBitmap)) { //We set the highest quality interpolation so that we do not loose the image quality viewPortGraphics.SmoothingMode = SmoothingMode.HighQuality; viewPortGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic; using (PaintEventArgs eventArgs = new PaintEventArgs(viewPortGraphics, viewPortData.LogicalViewPort)) { workflowView.ActiveLayout.OnPaint(eventArgs, viewPortData); } //Create the scaling matrix Matrix transformationMatrix = new Matrix(); transformationMatrix.Scale(viewPortData.Scaling.Width, viewPortData.Scaling.Height, MatrixOrder.Prepend); //When we draw on the viewport we draw in scaled and translated. //So that we minimize the calls to DrawImage //Make sure that we scale down the logical view port origin in order to take care of scaling factor //Before we select the transform factor we make sure that logicalviewport origin is scaled down Point[] logicalViewPortOrigin = new Point[] { viewPortData.LogicalViewPort.Location }; transformationMatrix.TransformPoints(logicalViewPortOrigin); //For performance improvement and to eliminate one extra DrawImage...we draw the designers on the viewport //bitmap with visual depth consideration transformationMatrix.Translate(-logicalViewPortOrigin[0].X + viewPortData.ShadowDepth.Width, -logicalViewPortOrigin[0].Y + viewPortData.ShadowDepth.Height, MatrixOrder.Append); //Select the transform into viewport graphics. //Viewport bitmap has the scaled and translated designers which we then map to //the actual graphics based on page layout viewPortGraphics.Transform = transformationMatrix; //Draw the designers on bitmap if (workflowView.RootDesigner != null) { using (Region clipRegion = new Region()) using (GraphicsPath designerPath = ActivityDesignerPaint.GetDesignerPath(workflowView.RootDesigner, false)) { Region oldRegion = viewPortGraphics.Clip; //First draw the grid and rectangle with the designer clip region clipRegion.MakeEmpty(); clipRegion.Union(designerPath); viewPortGraphics.Clip = clipRegion; AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme; viewPortGraphics.FillRectangle(ambientTheme.BackgroundBrush, workflowView.RootDesigner.Bounds); if (ambientTheme.ShowGrid) ActivityDesignerPaint.DrawGrid(viewPortGraphics, workflowView.RootDesigner.Bounds); viewPortGraphics.Clip = oldRegion; //Then draw the root with clip region extended try { using (PaintEventArgs paintEventArgs = new PaintEventArgs(viewPortGraphics, viewPortData.LogicalViewPort)) { ((IWorkflowDesignerMessageSink)workflowView.RootDesigner).OnPaint(paintEventArgs, viewPortData.LogicalViewPort); } } catch (Exception e) { //Eat the exception thrown in draw Debug.WriteLine(e); } } } //Draw all the filters using (PaintEventArgs paintArgs = new PaintEventArgs(viewPortGraphics, workflowView.RootDesigner.Bounds)) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(workflowView, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { if (((IWorkflowDesignerMessageSink)filter).OnPaint(paintArgs, viewPortData.LogicalViewPort)) break; } catch (Exception e) { Debug.WriteLine(e); } } } } viewPortGraphics.Transform = new Matrix(); //Now that we have a bitmap which is bit offseted based visual depth we need to take copy of it //This is done so as to avoid expensive DrawImage call, what I am assuming here is that time it //will take to create a new bitmap from an existing one is less expensive in terms of speed than space //As you just need to copy bitmap bits in memory than to perform expesive Image Drawing operation if (!viewPortData.ShadowDepth.IsEmpty) { Bitmap temporaryBitmap = new Bitmap(memoryBitmap); //THEMETODO: WE JUST NEED TO GRAYSCALE THIS, RATHER THAN DRAWING A SHADOW //Now that we have taken a copy we will draw over the existing bitmap so that we can make it as shadow bitmap using (Brush shadowDepthBrush = new SolidBrush(Color.FromArgb(220, Color.White))) viewPortGraphics.FillRectangle(shadowDepthBrush, new Rectangle(Point.Empty, new Size(memoryBitmap.Size.Width - viewPortData.ShadowDepth.Width - 1, memoryBitmap.Size.Height - viewPortData.ShadowDepth.Height - 1))); //Now make sure that we draw the image from the temporary bitmap with white color set as transparent //so that we achive the 3D effect //Make sure that we take into consideration the transparency key ImageAttributes transparentColorKey = new ImageAttributes(); transparentColorKey.SetColorKey(viewPortData.TransparentColor, viewPortData.TransparentColor, ColorAdjustType.Default); transparentColorKey.SetColorKey(viewPortData.TransparentColor, viewPortData.TransparentColor, ColorAdjustType.Bitmap); viewPortGraphics.DrawImage(temporaryBitmap, new Rectangle(-viewPortData.ShadowDepth.Width, -viewPortData.ShadowDepth.Height, memoryBitmap.Width, memoryBitmap.Height), 0, 0, memoryBitmap.Width, memoryBitmap.Height, GraphicsUnit.Pixel, transparentColorKey); //Now dispose the temporary bitmap temporaryBitmap.Dispose(); } } } internal void OnThemeChange(object sender, EventArgs e) { ShadowDepth = WorkflowTheme.CurrentTheme.AmbientTheme.ShadowDepth; using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, EventArgs.Empty)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { ((IWorkflowDesignerMessageSink)filter).OnThemeChange(); } catch (Exception ex) { Debug.WriteLine(ex); } } } base.PerformLayout(); } private void OnEnsureVisible(object sender, EventArgs e) { if (this.ensureVisibleEventHandler != null) { Idle -= this.ensureVisibleEventHandler; this.ensureVisibleEventHandler = null; } ISelectionService selectionService = (ISelectionService)GetService(typeof(ISelectionService)); if (selectionService != null && selectionService.SelectionCount > 0) { //We do not want to regenerate a layout event in ensure visible ArrayList selectedComponents = new ArrayList(selectionService.GetSelectedComponents()); for (int i = selectedComponents.Count - 1; i >= 0; i--) { Rectangle rectangleToMakeVisible = Rectangle.Empty; if (selectedComponents[i] is Activity) { ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(selectedComponents[i] as Activity); if (activityDesigner != null) { rectangleToMakeVisible = activityDesigner.Bounds; rectangleToMakeVisible.Inflate(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize); rectangleToMakeVisible.Inflate(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize); } } else if (selectedComponents[i] is HitTestInfo) { rectangleToMakeVisible = ((HitTestInfo)selectedComponents[i]).Bounds; } if (!rectangleToMakeVisible.IsEmpty) EnsureVisible(rectangleToMakeVisible); } } } private void EnsureVisible(Rectangle rect) { Rectangle clientRectangle = ClientRectangleToLogical(new Rectangle(Point.Empty, ViewPortSize)); if (!clientRectangle.Contains(rect.Location) || !clientRectangle.Contains(new Point(rect.Right, rect.Bottom))) { Size scrollDelta = new Size(); if (!clientRectangle.Contains(new Point(rect.Left, clientRectangle.Top)) || !clientRectangle.Contains(new Point(rect.Right, clientRectangle.Top))) { if (rect.Width > clientRectangle.Width) scrollDelta.Width = (rect.Left + rect.Width / 2) - (clientRectangle.Left + clientRectangle.Width / 2); else if (rect.Left < clientRectangle.Left) scrollDelta.Width = (rect.Left - clientRectangle.Left); else scrollDelta.Width = (rect.Right - clientRectangle.Right); } if (!clientRectangle.Contains(new Point(clientRectangle.Left, rect.Top)) || !clientRectangle.Contains(new Point(clientRectangle.Left, rect.Bottom))) { if ((rect.Top < clientRectangle.Top) || (rect.Height > clientRectangle.Height)) scrollDelta.Height = (rect.Top - clientRectangle.Top); else scrollDelta.Height = rect.Bottom - clientRectangle.Bottom; } scrollDelta = LogicalSizeToClient(scrollDelta); Point scrollPosition = ScrollPosition; ScrollPosition = new Point(scrollPosition.X + scrollDelta.Width, scrollPosition.Y + scrollDelta.Height); } } private void OnScroll(object sender, EventArgs e) { //Lets speedup the scrolling logic InvalidateClientRectangle(Rectangle.Empty); ScrollBar scrollBar = sender as ScrollBar; if (scrollBar != null) { using (WorkflowMessageDispatchData dispatchData = new WorkflowMessageDispatchData(this, e)) { foreach (WorkflowDesignerMessageFilter filter in dispatchData.Filters) { try { ((IWorkflowDesignerMessageSink)filter).OnScroll(scrollBar, scrollBar.Value); } catch (Exception ex) { Debug.WriteLine(ex); } } } } } private void UpdateScrollRange() { if (ViewPortSize.Width < 0 || ViewPortSize.Height < 0) return; Size currentSize = ViewPortSize; Size maximumScrollSize = LogicalSizeToClient(this.activeLayout.Extent); Size largeChangeSize = new Size(Math.Min(maximumScrollSize.Width, currentSize.Width), Math.Min(maximumScrollSize.Height, currentSize.Height)); if (hScrollBar.Maximum != maximumScrollSize.Width) hScrollBar.Maximum = maximumScrollSize.Width; if (vScrollBar.Maximum != maximumScrollSize.Height) vScrollBar.Maximum = maximumScrollSize.Height; if (hScrollBar.LargeChange != largeChangeSize.Width) { hScrollBar.SmallChange = largeChangeSize.Width / 15; hScrollBar.LargeChange = largeChangeSize.Width + 1; } if (vScrollBar.LargeChange != largeChangeSize.Height) { vScrollBar.SmallChange = largeChangeSize.Height / 15; vScrollBar.LargeChange = largeChangeSize.Height + 1; } int xMaxScrollPos = maximumScrollSize.Width - hScrollBar.LargeChange; xMaxScrollPos = (xMaxScrollPos < 0) ? 0 : xMaxScrollPos; if (hScrollBar.Value > xMaxScrollPos) hScrollBar.Value = xMaxScrollPos; int yMaxScrollPos = maximumScrollSize.Height - vScrollBar.LargeChange; yMaxScrollPos = (yMaxScrollPos < 0) ? 0 : yMaxScrollPos; if (vScrollBar.Value > yMaxScrollPos) vScrollBar.Value = yMaxScrollPos; RefreshDynamicAction(); bool hScrollBarVisible = hScrollBar.Visible; if (Controls.Contains(hScrollBar)) hScrollBar.Visible = (hScrollBar.Maximum > currentSize.Width); bool vScrollBarVisible = vScrollBar.Visible; if (Controls.Contains(vScrollBar)) vScrollBar.Visible = (vScrollBar.Maximum > currentSize.Height); if (hScrollBarVisible != hScrollBar.Visible || vScrollBar.Visible != vScrollBarVisible) { base.PerformLayout(); Refresh(); } } private DynamicAction CreateDynamicAction() { DynamicAction fitAllAction = new DynamicAction(); fitAllAction.ButtonSize = DynamicAction.ButtonSizes.Large; fitAllAction.Doc----gnment = DesignerContentAlignment.BottomRight; fitAllAction.DockMargin = new Size(5, 5); ActionButton fitallButton = new ActionButton(new Image[] { DR.GetImage(DR.FitToScreen) as Bitmap }); fitallButton.StateChanged += new EventHandler(OnFitToScreen); fitAllAction.Buttons.Add(fitallButton); return fitAllAction; } private void RefreshDynamicAction() { DynamicActionMessageFilter dynamicActionFilter = GetService(typeof(DynamicActionMessageFilter)) as DynamicActionMessageFilter; if (dynamicActionFilter == null || this.fitAllAction == null) return; if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height) { //This means we need to show the zoomin icon this.fitAllAction.Buttons[0].Description = DR.GetString(DR.FitToScreenDescription); this.fitAllAction.Buttons[0].StateImages = new Bitmap[] { DR.GetImage(DR.FitToScreen) as Bitmap }; dynamicActionFilter.AddAction(this.fitAllAction); } else if (Zoom != 100) { //We need to show zoomout icon this.fitAllAction.Buttons[0].Description = DR.GetString(DR.FitToWorkflowDescription); this.fitAllAction.Buttons[0].StateImages = new Bitmap[] { DR.GetImage(DR.FitToWorkflow) as Bitmap }; dynamicActionFilter.AddAction(this.fitAllAction); } else { //In neither case we remove the action dynamicActionFilter.RemoveAction(this.fitAllAction); this.fitAllAction.Buttons[0].State = ActionButton.States.Normal; } } private void OnFitToScreen(object sender, EventArgs e) { ActionButton fitallButton = sender as ActionButton; if (fitallButton == null || fitallButton.State != ActionButton.States.Pressed) return; if (HScrollBar.Maximum > ViewPortSize.Width || VScrollBar.Maximum > ViewPortSize.Height) FitToScreenSize(); else if (Zoom != 100) FitToWorkflowSize(); } private void OnTabChange(object sender, TabSelectionChangeEventArgs e) { if (e.CurrentItem.Identifier == (int)TabButtonIds.MultiPage || e.CurrentItem.Identifier == (int)TabButtonIds.Zoom || e.CurrentItem.Identifier == (int)TabButtonIds.Pan) { Rectangle buttonRect = e.SelectedTabBounds; CommandID menuID = null; if (e.CurrentItem.Identifier == (int)TabButtonIds.MultiPage) menuID = WorkflowMenuCommands.PageLayoutMenu; else if (e.CurrentItem.Identifier == (int)TabButtonIds.Zoom) menuID = WorkflowMenuCommands.ZoomMenu; else menuID = WorkflowMenuCommands.PanMenu; IMenuCommandService menuCommandService = (IMenuCommandService)GetService(typeof(IMenuCommandService)); if (menuCommandService != null) menuCommandService.ShowContextMenu(menuID, buttonRect.Right, buttonRect.Top); } } private void EnsureScrollBars(HScrollBar newHorizScrollBar, VScrollBar newVertScrollBar) { try { SuspendLayout(); if (this.hScrollBar != newHorizScrollBar) { if (this.hScrollBar != null) { this.hScrollBar.ValueChanged -= new EventHandler(OnScroll); if (Controls.Contains(this.hScrollBar)) Controls.Remove(this.hScrollBar); } this.hScrollBar = newHorizScrollBar; if (this.hScrollBar.Parent == null) { this.hScrollBar.TabStop = false; Controls.Add(this.hScrollBar); } } if (this.vScrollBar != newVertScrollBar) { if (this.vScrollBar != null) { this.vScrollBar.ValueChanged -= new EventHandler(OnScroll); if (Controls.Contains(this.vScrollBar)) Controls.Remove(this.vScrollBar); } this.vScrollBar = newVertScrollBar; if (this.vScrollBar.Parent == null) { this.vScrollBar.TabStop = false; Controls.Add(this.vScrollBar); } } this.hScrollBar.ValueChanged += new EventHandler(OnScroll); this.vScrollBar.ValueChanged += new EventHandler(OnScroll); } finally { ResumeLayout(true); } } private void PopulateMessageFilters(bool stockFilters) { IListfilters = (stockFilters) ? this.stockMessageFilters : this.customMessageFilters; Debug.Assert(filters.Count == 0); if (stockFilters) { filters.Add(new GlyphManager()); filters.Add(new WindowManager()); } else { Debug.Assert(this.rootDesigner != null); if (Capture) Capture = false; IList customFilters = ((IWorkflowRootDesigner)this.rootDesigner).MessageFilters; foreach (WorkflowDesignerMessageFilter filter in customFilters) filters.Add(filter); } foreach (WorkflowDesignerMessageFilter filter in filters) filter.SetParentView(this); } private void DisposeMessageFilters(bool stockFilters) { List filters = (stockFilters) ? this.stockMessageFilters : this.customMessageFilters; //We dispose all the message filters, this is done by copying because some of the //message filters might remove other dependent messagefilters ArrayList clonedFilterList = new ArrayList(filters.ToArray()); foreach (WorkflowDesignerMessageFilter filter in clonedFilterList) ((IDisposable)filter).Dispose(); filters.Clear(); } #endregion #region Coordinate Transformation Functions public void InvalidateClientRectangle(Rectangle clientRectangle) { if (this.layoutEventHandler == null) { if (!clientRectangle.IsEmpty) { //Inflate the invalidated rectangle. When zoom factor is less than 1; there is a loss of precision clientRectangle.Inflate(1, 1); base.Invalidate(clientRectangle); } else { base.Invalidate(); } } } public void InvalidateLogicalRectangle(Rectangle logicalRectangle) { InvalidateClientRectangle(LogicalRectangleToClient(logicalRectangle)); } public Point LogicalPointToScreen(Point logicalPoint) { return PointToScreen(LogicalPointToClient(logicalPoint)); } public Point ScreenPointToLogical(Point screenPoint) { return ClientPointToLogical(PointToClient(screenPoint)); } public Point LogicalPointToClient(Point logicalPoint) { return LogicalPointToClient(logicalPoint, true); } public Point ClientPointToLogical(Point clientPoint) { return ClientPointToLogical(clientPoint, true); } public Size LogicalSizeToClient(Size logicalSize) { Point[] points = new Point[] { new Point(logicalSize) }; //Scale the point Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); scalingMatrix.TransformPoints(points); return new Size(points[0]); } public Size ClientSizeToLogical(Size clientSize) { //Scale the size, size scaling does not require translate Point[] points = new Point[] { new Point(clientSize) }; Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); scalingMatrix.Invert(); scalingMatrix.TransformPoints(points); scalingMatrix.Invert(); return new Size(points[0]); } public Rectangle LogicalRectangleToClient(Rectangle rectangle) { Debug.Assert(this.activeLayout != null, "active layout should not be null"); Rectangle clientViewPort = (this.activeLayout != null) ? this.activeLayout.MapOutRectangleFromLayout(rectangle) : rectangle; // return new Rectangle(LogicalPointToClient(clientViewPort.Location, false), LogicalSizeToClient(clientViewPort.Size)); } public Rectangle ClientRectangleToLogical(Rectangle rectangle) { //We translate the client viewport to logical view port. //To do this we first get the view port rectangle scale it down //then translate it to area of page we would be viewing Rectangle scaledLogicalViewPort = new Rectangle(ClientPointToLogical(rectangle.Location, false), ClientSizeToLogical(rectangle.Size)); return this.activeLayout.MapInRectangleToLayout(scaledLogicalViewPort); } internal bool IsClientPointInActiveLayout(Point clientPoint) { Point logicalPoint = ClientPointToLogical(clientPoint, false); return this.activeLayout.IsCoOrdInLayout(logicalPoint); } /* * Client scale is when we transform the coordinate based on zoom and translate it based on scrolling position and layout * Logical scale is when we transform the coordinate and map it to a flat coordinate system which goes from 0,0 to m,n * We also consider the ActiveLayout to transform the coordinates. */ private Point LogicalPointToClient(Point point, bool mapToLayout) { if (mapToLayout) point = this.activeLayout.MapOutCoOrdFromLayout(point); //Scale the point Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); Point[] points = new Point[] { point }; scalingMatrix.TransformPoints(points); //Translate the point Matrix translateMatrix = new Matrix(); translateMatrix.Translate(-ScrollPosition.X, -ScrollPosition.Y); translateMatrix.TransformPoints(points); return points[0]; } private Point ClientPointToLogical(Point point, bool mapToLayout) { Point[] points = new Point[] { point }; //Translate the point Matrix translateMatrix = new Matrix(); translateMatrix.Translate(ScrollPosition.X, ScrollPosition.Y); translateMatrix.TransformPoints(points); //Scale down the point Matrix scalingMatrix = new Matrix(); scalingMatrix.Scale(ScaleZoomFactor, ScaleZoomFactor); scalingMatrix.Invert(); scalingMatrix.TransformPoints(points); scalingMatrix.Invert(); if (!mapToLayout) return points[0]; else return this.activeLayout.MapInCoOrdToLayout(points[0]); } #endregion #region IServiceProvider Implemetation object IServiceProvider.GetService(Type serviceType) { return GetService(serviceType); } protected override object GetService(Type serviceType) { object retVal = null; if (serviceType == typeof(CommandID)) retVal = new CommandID(new Guid("5f1c3c8d-60f1-4b98-b85b-8679f97e8eac"), 0); else retVal = this.serviceProvider.GetService(serviceType); return retVal; } #endregion #region Class WorkflowMessageDispatchData private sealed class WorkflowMessageDispatchData : IDisposable { private WorkflowView workflowView; private HitTestInfo messageContext = null; public WorkflowMessageDispatchData(WorkflowView workflowView, EventArgs e) { this.workflowView = workflowView; if (this.workflowView.RootDesigner != null && this.workflowView.stockMessageFilters.Count > 0) { Point clientPoint = Point.Empty; if (e is MouseEventArgs || e is DragEventArgs) { if (e is MouseEventArgs) { clientPoint = new Point(((MouseEventArgs)e).X, ((MouseEventArgs)e).Y); } else if (e is DragEventArgs) { clientPoint = this.workflowView.PointToClient(new Point(((DragEventArgs)e).X, ((DragEventArgs)e).Y)); this.workflowView.UpdateLayout(); } Point logicalPoint = this.workflowView.ClientPointToLogical(clientPoint); HitTestInfo hitTestInfo = this.workflowView.RootDesigner.HitTest(logicalPoint); this.messageContext = (hitTestInfo != null) ? hitTestInfo : HitTestInfo.Nowhere; this.workflowView.messageHitTestContexts.Push(this.messageContext); } } } void IDisposable.Dispose() { if (this.workflowView != null && this.messageContext != null) { HitTestInfo hittestInfo = this.workflowView.messageHitTestContexts.Pop(); if (hittestInfo != this.messageContext) Debug.Assert(false, "WorkflowView poped wrong message context"); } } public ReadOnlyCollection Filters { get { //We recreate a new list everytime as in some of the messages dispatched, we there can //be additional filters which might be added List mergedFilterList = new List (); mergedFilterList.AddRange(this.workflowView.customMessageFilters); mergedFilterList.AddRange(this.workflowView.stockMessageFilters); return mergedFilterList.AsReadOnly(); } } } #endregion #region IMessageFilter Implementation [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] bool IMessageFilter.PreFilterMessage(ref Message m) { bool handled = false; if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN || m.Msg == NativeMethods.WM_KEYUP || m.Msg == NativeMethods.WM_SYSKEYUP) { Control control = Control.FromHandle(m.HWnd); if (control != null && (control == this || Controls.Contains(control))) { KeyEventArgs eventArgs = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys); if (m.Msg == NativeMethods.WM_KEYDOWN || m.Msg == NativeMethods.WM_SYSKEYDOWN) OnKeyDown(eventArgs); else OnKeyUp(eventArgs); handled = eventArgs.Handled; } } return handled; } #endregion } #region Class WorkflowViewAccessibleObject public class WorkflowViewAccessibleObject : Control.ControlAccessibleObject { private WorkflowView workflowView; public WorkflowViewAccessibleObject(WorkflowView workflowView) : base(workflowView) { if (workflowView == null) throw new ArgumentNullException("workflowView"); this.workflowView = workflowView; } public override Rectangle Bounds { get { return new Rectangle(this.workflowView.PointToScreen(Point.Empty), this.workflowView.ViewPortSize); } } public override string DefaultAction { get { return DR.GetString(DR.AccessibleAction); } } public override string Description { get { return DR.GetString(DR.WorkflowViewAccessibleDescription); } } public override string Help { get { return DR.GetString(DR.WorkflowViewAccessibleHelp); } } public override string Name { get { return DR.GetString(DR.WorkflowViewAccessibleName); } set { } } public override AccessibleRole Role { get { return AccessibleRole.Diagram; } } public override AccessibleObject GetChild(int index) { return (this.workflowView.RootDesigner != null && index == 0) ? this.workflowView.RootDesigner.AccessibilityObject : base.GetChild(index); } public override int GetChildCount() { return (this.workflowView.RootDesigner != null) ? 1 : -1; } public override AccessibleObject Navigate(AccessibleNavigation navdir) { if (navdir == AccessibleNavigation.FirstChild || navdir == AccessibleNavigation.LastChild) return GetChild(0); else return base.Navigate(navdir); } } #endregion #region WorkflowTimer internal sealed class WorkflowTimer : IDisposable { private static WorkflowTimer workflowTimer; private const int TimerInterval = 50; private Timer timer = null; private List elapsedEvents = new List (); internal static WorkflowTimer Default { get { if (WorkflowTimer.workflowTimer == null) WorkflowTimer.workflowTimer = new WorkflowTimer(); return WorkflowTimer.workflowTimer; } } private WorkflowTimer() { this.timer = new Timer(); this.timer.Interval = WorkflowTimer.TimerInterval; this.timer.Tick += new EventHandler(OnTimer); this.timer.Stop(); } ~WorkflowTimer() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (this.timer != null) { if (this.timer.Enabled) this.timer.Stop(); this.timer.Dispose(); this.timer = null; } } internal void Subscribe(int elapsedInterval, EventHandler elapsedEventHandler) { this.elapsedEvents.Add(new ElapsedEventUnit(elapsedInterval / WorkflowTimer.TimerInterval, elapsedEventHandler)); if (!this.timer.Enabled) this.timer.Start(); } internal void Unsubscribe(EventHandler elapsedEventHandler) { List removableElapsedEvents = new List (); foreach (ElapsedEventUnit elapsedEvent in this.elapsedEvents) { if (elapsedEvent.elapsedEventHandler == elapsedEventHandler) removableElapsedEvents.Add(elapsedEvent); } foreach (ElapsedEventUnit elapsedEvent in removableElapsedEvents) this.elapsedEvents.Remove(elapsedEvent); if (this.elapsedEvents.Count == 0 && this.timer.Enabled) this.timer.Stop(); } private void OnTimer(object sender, EventArgs e) { List clonedList = new List (this.elapsedEvents); foreach (ElapsedEventUnit elapsedEvent in clonedList) { elapsedEvent.elapsedTime += 1; if (elapsedEvent.elapsedInterval <= elapsedEvent.elapsedTime) { elapsedEvent.elapsedTime = 0; elapsedEvent.elapsedEventHandler(this, EventArgs.Empty); } } } private sealed class ElapsedEventUnit { internal EventHandler elapsedEventHandler; internal int elapsedInterval; internal int elapsedTime; internal ElapsedEventUnit(int interval, EventHandler eventHandler) { this.elapsedInterval = interval; this.elapsedEventHandler = eventHandler; } } } #endregion } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CodePrimitiveExpression.cs
- XPathNavigatorKeyComparer.cs
- GeneralTransform3D.cs
- DataServiceRequestException.cs
- XamlToRtfWriter.cs
- SynchronizationLockException.cs
- DrawingContextWalker.cs
- DataGridViewCellPaintingEventArgs.cs
- Image.cs
- XmlAttributeAttribute.cs
- SchemaContext.cs
- NameSpaceExtractor.cs
- SqlDataSourceCustomCommandPanel.cs
- ComponentResourceManager.cs
- UIntPtr.cs
- VerificationAttribute.cs
- HwndHostAutomationPeer.cs
- DetailsViewRowCollection.cs
- InputMethod.cs
- WebPartActionVerb.cs
- AddIn.cs
- MutexSecurity.cs
- BaseTemplateCodeDomTreeGenerator.cs
- GeneratedContractType.cs
- FontEmbeddingManager.cs
- CellLabel.cs
- ArrayTypeMismatchException.cs
- CacheMemory.cs
- ImageConverter.cs
- FullTextState.cs
- ErrorsHelper.cs
- XmlLanguageConverter.cs
- DbParameterHelper.cs
- TraceSwitch.cs
- EntityConnectionStringBuilder.cs
- CodeDirectionExpression.cs
- UpdateException.cs
- XmlCountingReader.cs
- DllHostedComPlusServiceHost.cs
- ProfileSettingsCollection.cs
- GenericsInstances.cs
- AudioException.cs
- _SslStream.cs
- MethodMessage.cs
- MapPathBasedVirtualPathProvider.cs
- ScriptRegistrationManager.cs
- Helper.cs
- PeerTransportSecurityElement.cs
- DataGridViewUtilities.cs
- MasterPage.cs
- SerializableAttribute.cs
- AuthenticationServiceManager.cs
- SafeViewOfFileHandle.cs
- SchemaCollectionPreprocessor.cs
- Rotation3DKeyFrameCollection.cs
- StreamWithDictionary.cs
- ProtocolElementCollection.cs
- PersistencePipeline.cs
- MarkupProperty.cs
- DataGridViewLayoutData.cs
- ExeContext.cs
- EditingCommands.cs
- StructuralCache.cs
- UIElement3D.cs
- DiscriminatorMap.cs
- XPathDocumentNavigator.cs
- DynamicValidatorEventArgs.cs
- APCustomTypeDescriptor.cs
- TableItemStyle.cs
- TagNameToTypeMapper.cs
- BindingMAnagerBase.cs
- ValueTypePropertyReference.cs
- NamespaceList.cs
- Privilege.cs
- RemoteHelper.cs
- ProfileParameter.cs
- PageBuildProvider.cs
- ObjectResult.cs
- OutputCacheSettings.cs
- OdbcConnectionStringbuilder.cs
- WebBrowserNavigatedEventHandler.cs
- CustomCategoryAttribute.cs
- QilXmlWriter.cs
- EventProvider.cs
- MatchingStyle.cs
- TextBoxAutomationPeer.cs
- ELinqQueryState.cs
- LinqDataSourceValidationException.cs
- MdiWindowListItemConverter.cs
- XmlFormatReaderGenerator.cs
- httpapplicationstate.cs
- ActivationServices.cs
- HttpProfileBase.cs
- ToolboxCategory.cs
- XmlSchemaCollection.cs
- ComponentEditorPage.cs
- TransactionChannelFaultConverter.cs
- sqlser.cs
- DataStorage.cs
- QuaternionAnimationBase.cs