Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / Behavior / BehaviorService.cs / 3 / BehaviorService.cs
namespace System.Windows.Forms.Design.Behavior { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Design; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; using System.ComponentModel.Design.Serialization; using System.Security; using System.Security.Permissions; using System.Windows.Forms.Design; using System.Runtime.InteropServices; using System.Globalization; using Microsoft.Win32; ////// /// The BehaviorService essentially manages all things UI in the designer. /// When the BehaviorService is created it adds a transparent window over the /// designer frame. The BehaviorService can then use this window to render UI /// elements (called Glyphs) as well as catch all mouse messages. By doing /// so - the BehaviorService can control designer behavior. The BehaviorService /// supports a BehaviorStack. 'Behavior' objects can be pushed onto this stack. /// When a message is intercepted via the transparent window, the BehaviorService /// can send the message to the Behavior at the top of the stack. This allows /// for different UI modes depending on the currently pushed Behavior. The /// BehaviorService is used to render all 'Glyphs': selection borders, grab handles, /// smart tags etc... as well as control many of the design-time behaviors: dragging, /// selection, snap lines, etc... /// public sealed class BehaviorService : IDisposable { private IServiceProvider serviceProvider; //standard service provider private AdornerWindow adornerWindow; //the transparent window all glyphs are drawn to private BehaviorServiceAdornerCollection adorners; //we manage all adorners (glyph-containers) here private ArrayList behaviorStack; //the stack behavior objects can be pushed to and popped from private Behavior captureBehavior; //the behavior that currently has capture; may be null private Glyph hitTestedGlyph; //the last valid glyph that was hit tested private IToolboxService toolboxSvc; //allows us to have the toolbox choose a cursor private Control dropSource; //actual control used to call .dodragdrop private DragEventArgs validDragArgs; //if valid - this is used to fabricate drag enter/leave envents private BehaviorDragDropEventHandler beginDragHandler; //fired directly before we call .DoDragDrop() private BehaviorDragDropEventHandler endDragHandler; //fired directly after we call .DoDragDrop() private EventHandler synchronizeEventHandler; //fired when we want to synchronize the selection private NativeMethods.TRACKMOUSEEVENT trackMouseEvent; //demand created (once) used to track the mouse hover event private bool trackingMouseEvent; //state identifying current mouse tracking private string[] testHook_RecentSnapLines; //we keep track of the last snaplines we found - for testing purposes private MenuCommandHandler menuCommandHandler; //private object that handles all menu commands private bool useSnapLines; //indicates if this designer session is using snaplines or snapping to a grid private bool queriedSnapLines; //only query for this once since we require the user restart design sessions when this changes private Hashtable dragEnterReplies; // we keep track of whether glyph has already responded to a DragEnter this D&D. private static TraceSwitch dragDropSwitch = new TraceSwitch("BSDRAGDROP", "Behavior service drag & drop messages"); private bool dragging = false; // are we in a drag private bool cancelDrag = false; // should we cancel the drag on the next QueryContinueDrag private int adornerWindowIndex = -1; //test hooks for SnapLines private static int WM_GETALLSNAPLINES; private static int WM_GETRECENTSNAPLINES; private DesignerActionUI actionPointer;//pointer to the designer action service so we can supply mouse over notifications private const string ToolboxFormat = ".NET Toolbox Item"; // used to detect if a drag is coming from the toolbox. ////// This constructor is called from DocumentDesigner's Initialize method. /// [SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")] internal BehaviorService(IServiceProvider serviceProvider, Control windowFrame) { this.serviceProvider = serviceProvider; //create the AdornerWindow adornerWindow = new AdornerWindow(this, windowFrame); //use the adornerWindow as an overlay IOverlayService os = (IOverlayService)serviceProvider.GetService(typeof(IOverlayService)); if (os != null) { adornerWindowIndex = os.PushOverlay(adornerWindow); } dragEnterReplies = new Hashtable(); //start with an empty adorner collection & no behavior on the stack adorners = new BehaviorServiceAdornerCollection(this); behaviorStack = new ArrayList(); hitTestedGlyph = null; validDragArgs = null; actionPointer = null; trackMouseEvent = null; trackingMouseEvent = false; //create out object that will handle all menucommands IMenuCommandService menuCommandService = serviceProvider.GetService(typeof(IMenuCommandService)) as IMenuCommandService; IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; if (menuCommandService != null && host != null) { menuCommandHandler = new MenuCommandHandler(this, menuCommandService); host.RemoveService(typeof(IMenuCommandService)); host.AddService(typeof(IMenuCommandService), menuCommandHandler); } //default layoutmode is SnapToGrid. useSnapLines = false; queriedSnapLines = false; //test hooks WM_GETALLSNAPLINES = SafeNativeMethods.RegisterWindowMessage("WM_GETALLSNAPLINES"); WM_GETRECENTSNAPLINES = SafeNativeMethods.RegisterWindowMessage("WM_GETRECENTSNAPLINES"); // Listen to the SystemEvents so that we can resync selection based on display settings etc. SystemEvents.DisplaySettingsChanged += new EventHandler(this.OnSystemSettingChanged); SystemEvents.InstalledFontsChanged += new EventHandler(this.OnSystemSettingChanged); SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); } ////// /// Read-only property that returns the AdornerCollection that the BehaivorService manages. /// public BehaviorServiceAdornerCollection Adorners { get { return adorners; } } ////// Returns the index of the transparent AdornerWindow. /// internal int AdornerWindowIndex { get { return adornerWindowIndex; } } ////// Returns the actual Control that represents the transparent AdornerWindow. /// internal Control AdornerWindowControl { get { return adornerWindow; } } ////// /// Creates and returns a Graphics object for the AdornerWindow /// public Graphics AdornerWindowGraphics { get { Graphics result = adornerWindow.CreateGraphics(); result.Clip = new Region(adornerWindow.DesignerFrameDisplayRectangle); return result; } } public Behavior CurrentBehavior { get { if(behaviorStack != null && behaviorStack.Count > 0) { return (behaviorStack[0] as Behavior); } else { return null; } } } ////// If the drag operation should be cancelled (say we get a WM_CANCELMODE), set /// CancelDrag to true. The next time QueryContinueDrag is called, the drag operation /// will be cancelled. /// internal bool CancelDrag { get { return cancelDrag; } set { cancelDrag = value; } } ///// This value will be set by the DocumentDesigner. 'actionpointer' will be called // when we get a mouse enter of a new component glyph. This is so the Designer // Actions can 'hover-active' if needed. /// internal DesignerActionUI DesignerActionUI { get { return actionPointer; } set { actionPointer = value; } } internal bool Dragging { get{ return dragging; } } internal bool HasCapture { get { return captureBehavior != null; } } ////// /// Returns the LayoutMode setting of the current designer session. Either /// SnapLines or SnapToGrid. /// internal bool UseSnapLines { get { //we only check for this service/value once since we require the //user to re-open the designer session after these types of option //have been modified if (!queriedSnapLines) { queriedSnapLines = true; useSnapLines = DesignerUtils.UseSnapLines(serviceProvider); } return useSnapLines; } } ////// /// Translates a point in the AdornerWindow to screen coords. /// public Point AdornerWindowPointToScreen(Point p) { NativeMethods.POINT offset = new NativeMethods.POINT(p.X, p.Y); NativeMethods.MapWindowPoints(adornerWindow.Handle, IntPtr.Zero, offset, 1); return new Point(offset.x, offset.y); } ////// /// Gets the location (upper-left corner) of the AdornerWindow in screen coords. /// public Point AdornerWindowToScreen() { Point origin = new Point(0, 0); return AdornerWindowPointToScreen(origin); } ////// /// Returns the location of a Control translated to AdornerWindow coords. /// public Point ControlToAdornerWindow(Control c) { if (c.Parent == null) { return Point.Empty; } NativeMethods.POINT pt = new NativeMethods.POINT(); pt.x = c.Left; pt.y = c.Top; NativeMethods.MapWindowPoints(c.Parent.Handle, adornerWindow.Handle, pt, 1); if(c.Parent.IsMirrored) { pt.x -= c.Width; } return new Point(pt.x, pt.y); } ////// /// Converts a point in handle's coordinate system to AdornerWindow coords. /// public Point MapAdornerWindowPoint(IntPtr handle, Point pt) { NativeMethods.POINT nativePoint = new NativeMethods.POINT(); nativePoint.x = pt.X; nativePoint.y = pt.Y; NativeMethods.MapWindowPoints(handle, adornerWindow.Handle, nativePoint, 1); return new Point(nativePoint.x, nativePoint.y); } ////// /// Returns the bounding rectangle of a Control translated to AdornerWindow coords. /// public Rectangle ControlRectInAdornerWindow(Control c){ if(c.Parent == null) { return Rectangle.Empty; } Point loc = ControlToAdornerWindow(c); return new Rectangle(loc, c.Size); } internal bool IsDisposed { get { return adornerWindow == null || adornerWindow.IsDisposed; } } ////// We demand create a Control to call .DoDragDrop() on. With /// this control, we'll hook the drag events: querycontinue and // givefeedback and forward them along to the DropSourceBehavior. /// private Control DropSource { get { if (dropSource == null) { dropSource = new Control(); } return dropSource; } } ////// Called by the DragAssistanceManager after a snapline/drag op /// has completed - we store this data for testing purposes. See /// TestHook_GetRecentSnapLines method. /// internal string[] RecentSnapLines { set { this.testHook_RecentSnapLines = value; } } ////// /// The BehaviorService fires the BeginDrag event immediately /// before it starts a drop/drop operation via DoBeginDragDrop. /// public event BehaviorDragDropEventHandler BeginDrag { add { beginDragHandler += value; } remove { beginDragHandler -= value; } } ////// /// The BehaviorService fires the EndDrag event immediately /// after the drag operation has completed. /// public event BehaviorDragDropEventHandler EndDrag { add { endDragHandler += value; } remove { endDragHandler -= value; } } ////// /// The BehaviorService fires the Synchronize event when the current selection should be synchronized (refreshed). /// public event EventHandler Synchronize { add { synchronizeEventHandler += value; } remove { synchronizeEventHandler -= value; } } ////// /// Disposes the behavior service. /// public void Dispose() { // remove adorner window from overlay service IOverlayService os = (IOverlayService)serviceProvider.GetService(typeof(IOverlayService)); if (os != null) { os.RemoveOverlay(adornerWindow); } if (dropSource != null) { dropSource.Dispose(); } IMenuCommandService menuCommandService = serviceProvider.GetService(typeof(IMenuCommandService)) as IMenuCommandService; IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; MenuCommandHandler menuCommandHandler = null; if (menuCommandService != null) menuCommandHandler = menuCommandService as MenuCommandHandler; if (menuCommandHandler != null && host != null) { IMenuCommandService oldMenuCommandService = menuCommandHandler.MenuService; host.RemoveService(typeof(IMenuCommandService)); host.AddService(typeof(IMenuCommandService), oldMenuCommandService); } adornerWindow.Dispose(); SystemEvents.DisplaySettingsChanged -= new EventHandler(this.OnSystemSettingChanged); SystemEvents.InstalledFontsChanged -= new EventHandler(this.OnSystemSettingChanged); SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged); } ////// /// Enables Behaviors to call DoDragDrop. /// internal DragDropEffects DoDragDrop(DropSourceBehavior dropSourceBehavior) { //hook events DropSource.QueryContinueDrag += new QueryContinueDragEventHandler(dropSourceBehavior.QueryContinueDrag); DropSource.GiveFeedback += new GiveFeedbackEventHandler(dropSourceBehavior.GiveFeedback); DragDropEffects res = DragDropEffects.None; //build up the eventargs for firing our dragbegin/end events ICollection dragComponents = ((DropSourceBehavior.BehaviorDataObject)dropSourceBehavior.DataObject).DragComponents; BehaviorDragDropEventArgs eventArgs = new BehaviorDragDropEventArgs(dragComponents); try { try { OnBeginDrag(eventArgs); dragging = true; cancelDrag = false; // This is normally cleared on OnMouseUp, but we might not get an OnMouseUp to clear it. VSWhidbey #474259 // So let's make sure it is really cleared when we start the drag. dragEnterReplies.Clear(); res = DropSource.DoDragDrop(dropSourceBehavior.DataObject, dropSourceBehavior.AllowedEffects); } finally { DropSource.QueryContinueDrag -= new QueryContinueDragEventHandler(dropSourceBehavior.QueryContinueDrag); DropSource.GiveFeedback -= new GiveFeedbackEventHandler(dropSourceBehavior.GiveFeedback); //If the drop gets cancelled, we won't get a OnDragDrop, so let's make sure that we stop //processing drag notifications. Also VSWhidbey #354552 and 133339. EndDragNotification(); validDragArgs = null; dragging = false; cancelDrag = false; OnEndDrag(eventArgs); } } catch(CheckoutException cex) { if (cex == CheckoutException.Canceled) { res = DragDropEffects.None; } else { throw; } } finally { // VSWhidbey 306626 and 281813 // It's possible we did not receive an EndDrag, and therefore // we weren't able to cleanup the drag. We will do that here. // Scenarios where this happens: dragging from designer to recycle-bin, // or over the taskbar. if (dropSourceBehavior != null) { dropSourceBehavior.CleanupDrag(); } } return res; } ////// Here, we reflect on all of our components to get all SnapLines /// [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] private void TestHook_GetAllSnapLines(ref Message m) { string snapLineInfo = ""; IDesignerHost host = serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost; if (host == null) { return; } foreach (Component comp in host.Container.Components) { if (!(comp is Control)) { continue; } ControlDesigner designer = host.GetDesigner(comp) as ControlDesigner; if (designer != null) { foreach (SnapLine line in designer.SnapLines) { snapLineInfo += line.ToString() + "\tAssociated Control = " + designer.Control.Name + ":::"; } } } TestHook_SetText(ref m, snapLineInfo); } ////// Called by ControlDesigner when it receives a DragDrop /// message - we'll let the AdornerWindow know so it can /// exit from 'drag mode'. /// internal void EndDragNotification() { adornerWindow.EndDragNotification(); } ////// Called by our meucommand handling object, we will attempt to see /// if the appropriate hittested glyph's behavior wants to intercept and/or /// modify this command in any way. /// private MenuCommand FindCommand(CommandID commandID, IMenuCommandService menuService) { Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior != null) { //if the behavior wants all commands disabled.. if (behavior.DisableAllCommands) { MenuCommand menuCommand = menuService.FindCommand(commandID); if (menuCommand != null) { menuCommand.Enabled = false; } return menuCommand; } //check to see if the behavior wants to interrupt this command else { MenuCommand menuCommand = behavior.FindCommand(commandID); if (menuCommand != null) { //the behavior chose to interrupt - so return the new command return menuCommand; } } } return menuService.FindCommand(commandID); } ////// Pushes recent snaplines into the message structure. /// [SuppressMessage("Microsoft.Performance", "CA1818:DoNotConcatenateStringsInsideLoops")] [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] private void TestHook_GetRecentSnapLines(ref Message m) { string snapLineInfo = ""; if (this.testHook_RecentSnapLines != null) { foreach(string line in this.testHook_RecentSnapLines) { snapLineInfo += line + "\n"; } } TestHook_SetText(ref m, snapLineInfo); } ////// Pushes the testhook string into the message structure /// [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] private void TestHook_SetText(ref Message m, string text) { if (m.LParam == IntPtr.Zero) { m.Result = (IntPtr)((text.Length + 1) * Marshal.SystemDefaultCharSize); return; } if (unchecked((int)(long)m.WParam) < text.Length + 1) { m.Result = (IntPtr)(-1); return; } // Copy the name into the given IntPtr // char[] nullChar = new char[] {(char)0}; byte[] nullBytes; byte[] bytes; if (Marshal.SystemDefaultCharSize == 1) { bytes = System.Text.Encoding.Default.GetBytes(text); nullBytes = System.Text.Encoding.Default.GetBytes(nullChar); } else { bytes = System.Text.Encoding.Unicode.GetBytes(text); nullBytes = System.Text.Encoding.Unicode.GetBytes(nullChar); } Marshal.Copy(bytes, 0, m.LParam, bytes.Length); Marshal.Copy(nullBytes, 0, unchecked((IntPtr)((long)m.LParam + (long)bytes.Length)), nullBytes.Length); m.Result = (IntPtr)((bytes.Length + nullBytes.Length)/Marshal.SystemDefaultCharSize); } ////// This method defines the hueristic used to determine where a /// message is sent once the AdornerWindow intercepts it. First, we'll /// try to send it to the top-most Behavior on the stack. Next, well /// send the message along to the supplied Glyph. Finally, we'll /// return null. /// private Behavior GetAppropriateBehavior(Glyph g) { if (behaviorStack != null && behaviorStack.Count > 0) { return behaviorStack[0] as Behavior; } if (g != null && g.Behavior != null) { return g.Behavior; } return null; } ////// Given a behavior returns the behavior immediately /// after the behavior in the behaviorstack. /// Can return null. /// public Behavior GetNextBehavior(Behavior behavior) { if (behaviorStack != null && behaviorStack.Count > 0) { int index = behaviorStack.IndexOf(behavior); if ((index != -1) && (index < behaviorStack.Count - 1)) { return behaviorStack[index + 1] as Behavior; } } return null; } ////// Used by other designers (toolstrip designer for ex) this method will return /// the array of glyphs whose bounds intersect with the 'primaryGlyph' passed in. /// internal Glyph[] GetIntersectingGlyphs(Glyph primaryGlyph) { if (primaryGlyph == null) { Debug.Fail("The primary glyph cannot be null!"); return new Glyph[0]; } Rectangle primaryBounds = primaryGlyph.Bounds; ArrayList intersectingGlyphs = new ArrayList(); //loop through the glyphs in the same order as hit testing... for (int i = adorners.Count - 1; i >= 0; i--) { if (!adorners[i].Enabled) { continue; } for (int j = 0; j < adorners[i].Glyphs.Count; j++) { Glyph g = adorners[i].Glyphs[j]; if (primaryBounds.IntersectsWith(g.Bounds)) { intersectingGlyphs.Add(g); } } } if (intersectingGlyphs.Count == 0) { return new Glyph[0]; } return (Glyph[])intersectingGlyphs.ToArray(typeof(Glyph)); } ////// Called in response to a MouseMove message, we'll call TrackMouseEvents /// to make sure we continue to get WM_HOVER messages so we can send them /// to any relevant glyphs. /// private void HookMouseEvent() { if (!trackingMouseEvent) { trackingMouseEvent = true; if (trackMouseEvent == null) { trackMouseEvent = new NativeMethods.TRACKMOUSEEVENT(); trackMouseEvent.dwFlags = NativeMethods.TME_HOVER; trackMouseEvent.hwndTrack = adornerWindow.Handle; } SafeNativeMethods.TrackMouseEvent(trackMouseEvent); } } ////// /// Invalidates the BehaviorService's AdornerWindow. This will force a refesh of all Adorners /// and, in turn, all Glyphs. /// public void Invalidate() { adornerWindow.InvalidateAdornerWindow(); } ////// /// Invalidates the BehaviorService's AdornerWindow. This will force a refesh of all Adorners /// and, in turn, all Glyphs. /// public void Invalidate(Rectangle rect) { adornerWindow.InvalidateAdornerWindow(rect); } ////// /// Invalidates the BehaviorService's AdornerWindow. This will force a refesh of all Adorners /// and, in turn, all Glyphs. /// public void Invalidate(Region r) { adornerWindow.InvalidateAdornerWindow(r); } ////// Called during hittesting, this method will send artificial /// enter/leave mouse events to the appropriate behavior. /// private void InvokeMouseEnterLeave(Glyph leaveGlyph, Glyph enterGlyph) { if (leaveGlyph != null) { if (enterGlyph != null && leaveGlyph.Equals(enterGlyph)) { //same glyph - no change return; } if (validDragArgs != null) { OnDragLeave(leaveGlyph, EventArgs.Empty); } else { OnMouseLeave(leaveGlyph); } } if (enterGlyph != null) { if (validDragArgs != null) { OnDragEnter(enterGlyph, validDragArgs); } else { OnMouseEnter(enterGlyph); } } } ////// /// Synchronizes all selection glyphs. /// public void SyncSelection() { if (synchronizeEventHandler != null) { synchronizeEventHandler(this, EventArgs.Empty); } } private void OnSystemSettingChanged(object sender, EventArgs e) { SyncSelection(); DesignerUtils.SyncBrushes(); } private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { SyncSelection(); DesignerUtils.SyncBrushes(); } ////// /// Removes the behavior from the behavior stack /// public Behavior PopBehavior(Behavior behavior) { if (behaviorStack.Count == 0) { throw new InvalidOperationException(); } int index = behaviorStack.IndexOf(behavior); if (index == -1) { Debug.Assert(false, "Could not find the behavior to pop - did it already get popped off? " + behavior.ToString()); return null; } behaviorStack.RemoveAt(index); if (behavior == captureBehavior) { adornerWindow.Capture = false; // Defensive: adornerWindow should get a WM_CAPTURECHANGED, // but do this by hand if it didn't. if (captureBehavior != null) { OnLoseCapture(); Debug.Assert(captureBehavior == null, "OnLostCapture should have cleared captureBehavior"); } } return behavior; } ////// ControlDesigner calls this internal method in response to a WmPaint. /// We need to know when a ControlDesigner paints - 'cause we will need /// to re-paint any glyphs above of this Control. /// internal void ProcessPaintMessage(Rectangle paintRect) { //Note, we don't call BehSvc.Invalidate because //this will just cause the messages to recurse. //Instead, invalidating this adornerWindow will //just cause a "propagatePaint" and draw the glyphs. adornerWindow.Invalidate(paintRect); } ////// Called in response to WM_NCHITTEST message intercepted by the transparent /// AdornerWindow. The BehaviorService will cycle through all enabled /// Adorners and pass this hitTest info along. /// Hit testing goes backwards through the list of adorners and forward /// through the list of glyphs... think about it... /// /// This returns true if it should let the default handling for the designer, /// and will fill in which control was hit tested. /// private bool PropagateHitTest(Point pt) { for (int i = adorners.Count - 1; i >= 0; i--) { if (!adorners[i].Enabled) { continue; } for (int j = 0; j < adorners[i].Glyphs.Count; j++) { Cursor hitTestCursor = adorners[i].Glyphs[j].GetHitTest(pt); if (hitTestCursor != null) { // InvokeMouseEnterGlyph will cause the selection to change, which // might change the number of glyphs, so we need to remember the new glyph // before calling InvokeMouseEnterLeave. VSWhidbey #396611 Glyph newGlyph = adorners[i].Glyphs[j]; //with a valid hit test, fire enter/leave events // InvokeMouseEnterLeave(hitTestedGlyph, newGlyph); if (validDragArgs == null) { //if we're not dragging, set the appropriate cursor // SetAppropriateCursor(hitTestCursor); } hitTestedGlyph = newGlyph; //return true if we hit on a transparentBehavior, otherwise false return (hitTestedGlyph.Behavior is ControlDesigner.TransparentBehavior); } } } InvokeMouseEnterLeave(hitTestedGlyph, null); if (validDragArgs == null) { Cursor cursor = Cursors.Default; if ((behaviorStack != null) && (behaviorStack.Count > 0)) { Behavior behavior = behaviorStack[0] as Behavior; if (behavior != null) { cursor = behavior.Cursor; } } SetAppropriateCursor(cursor); } hitTestedGlyph = null; return true;//Returning false will cause the transparent window to return HTCLIENT when handling WM_NCHITTEST, thus blocking underline window to receive mouse events. } ////// Called in response to WM_PAINT message intercepted by the transparent /// AdornerWindow. The BehaviorService will cycle through all enabled /// Adorners and pass this paint event along. /// Painting should go forward through the list of adorners and /// backwards through each glyph... think about it... /// private void PropagatePaint(PaintEventArgs pe) { for (int i = 0; i < adorners.Count; i ++) { if (!adorners[i].Enabled) { continue; } for (int j = adorners[i].Glyphs.Count -1; j >= 0; j--) { adorners[i].Glyphs[j].Paint(pe); } } } ////// /// Pushes a Behavior object onto the BehaviorStack. This is often done through hit-tested /// Glyph. /// public void PushBehavior(Behavior behavior) { if (behavior == null) { throw new ArgumentNullException("behavior"); } // Should we catch this behaviorStack.Insert(0, behavior); // If there is a capture behavior, and it isn't this behavior, // notify it that it no longer has capture. if (captureBehavior != null && captureBehavior != behavior) { OnLoseCapture(); } } ////// /// Pushes a Behavior object onto the BehaviorStack and assigns mouse capture to the behavior. /// This is often done through hit-tested Glyph. If a behavior calls this the behavior's OnLoseCapture /// will be called if mouse capture is lost. /// public void PushCaptureBehavior(Behavior behavior) { PushBehavior(behavior); captureBehavior = behavior; adornerWindow.Capture = true; //VSWhidbey #373836. Since we are now capturing all mouse messages, we might miss some //WM_MOUSEACTIVATE which would have activated the app. //So if the DialogOwnerWindow (e.g. VS) is not the active window, let's activate it here. IUIService uiService = (IUIService)serviceProvider.GetService(typeof(IUIService)); if(uiService != null) { IWin32Window hwnd = uiService.GetDialogOwnerWindow(); if (hwnd != null && hwnd.Handle != IntPtr.Zero && hwnd.Handle != UnsafeNativeMethods.GetActiveWindow()) { UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, hwnd.Handle)); } } } ////// /// Translates a screen coord into a coord relative to the BehaviorService's AdornerWindow. /// public Point ScreenToAdornerWindow(Point p) { NativeMethods.POINT offset = new NativeMethods.POINT(); offset.x = p.X; offset.y = p.Y; NativeMethods.MapWindowPoints(IntPtr.Zero, adornerWindow.Handle, offset, 1); return new Point(offset.x, offset.y); } ////// Called after a successful Glyph hittest. This method will first /// request the Cursor from the toolbox. If this fails, then the /// Glyph's hittested cursor will be used. /// private void SetAppropriateCursor(Cursor cursor) { //default cursors will let the toolbox svc set a cursor if needed if (cursor == Cursors.Default) { if (toolboxSvc == null) { toolboxSvc = (IToolboxService)serviceProvider.GetService(typeof(IToolboxService)); } if (toolboxSvc != null && toolboxSvc.SetCursor()) { cursor = new Cursor(NativeMethods.GetCursor()); } } adornerWindow.Cursor = cursor; } ////// Displays the given exception to the user. /// private void ShowError(Exception ex) { IUIService uis = serviceProvider.GetService(typeof(IUIService)) as IUIService; if (uis != null) { uis.ShowError(ex); } } ////// Called by ControlDesigner when it receives a DragEnter /// message - we'll let the AdornerWindow know so it can /// listen to all Mouse Messages for sending drag /// notifcations. /// internal void StartDragNotification() { adornerWindow.StartDragNotification(); } ////// Called in response to a MouseLeave message, we'll /// set state signalling that we are no longer tracking the /// mouse event. This is used for capturing MouseHover /// notifications. /// private void UnHookMouseEvent() { trackingMouseEvent = false; } ////// Invokes the BeginDrag event for any listeners. /// private void OnBeginDrag(BehaviorDragDropEventArgs e) { if (beginDragHandler != null) { beginDragHandler(this, e); } } ////// Invokes the EndDrag event for any listeners. /// private void OnEndDrag(BehaviorDragDropEventArgs e) { if (endDragHandler != null) { endDragHandler(this, e); } } ////// Called by the adorner window when it loses capture. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] internal void OnLoseCapture() { if (captureBehavior != null) { Behavior b = captureBehavior; captureBehavior = null; try { b.OnLoseCapture(hitTestedGlyph, EventArgs.Empty); } catch { } } } ////// Called when any MouseDown message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseDoubleClick(MouseButtons button, Point mouseLoc) { Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return false; } return behavior.OnMouseDoubleClick(hitTestedGlyph, button, mouseLoc); } ////// Called when any MouseDown message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseDown(MouseButtons button, Point mouseLoc) { Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return false; } return behavior.OnMouseDown(hitTestedGlyph, button, mouseLoc); } ////// Called when any MouseDown message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseEnter(Glyph g) { Behavior behavior = GetAppropriateBehavior(g); if (behavior == null) { return false; } return behavior.OnMouseEnter(g); } ////// Called when the MouseHover message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseHover(Point mouseLoc) { Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return false; } return behavior.OnMouseHover(hitTestedGlyph, mouseLoc); } ////// Called when any MouseDown message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseLeave(Glyph g) { //stop tracking mouse events for MouseHover UnHookMouseEvent(); Behavior behavior = GetAppropriateBehavior(g); if (behavior == null) { return false; } return behavior.OnMouseLeave(g); } ////// Called when any MouseMove message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseMove(MouseButtons button, Point mouseLoc) { //hook mouse events (if we haven't already) for MouseHover HookMouseEvent(); Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return false; } return behavior.OnMouseMove(hitTestedGlyph, button, mouseLoc); } ////// Called when any MouseUp message enters the BehaviorService's /// AdornerWindow::WndProc(). From here, the message is sent to /// the appropriate behavior. /// private bool OnMouseUp(MouseButtons button) { dragEnterReplies.Clear(); validDragArgs = null; Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return false; } return behavior.OnMouseUp(hitTestedGlyph, button); } //OLEDragDrop messages... ////// Used to send this Drag/Drop event from the AdornerWindow /// to the appropriate Behavior (or hit tested Glyph). /// private void OnDragDrop(DragEventArgs e) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "BS::OnDragDrop"); validDragArgs = null;//be sure to null out our cached drag args Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tNo behavior. returning"); return; } Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tForwarding to behavior"); behavior.OnDragDrop(hitTestedGlyph, e); } ////// Used to send this Drag/Drop event from the AdornerWindow /// to the appropriate Behavior (or hit tested Glyph). /// private void OnDragEnter(Glyph g, DragEventArgs e) { //if the AdornerWindow receives a drag message, this fn() //will be called w/o a glyph - so we'll assign the last //hit tested one Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "BS::OnDragEnter"); if (g == null) { g = hitTestedGlyph; } Behavior behavior = GetAppropriateBehavior(g); if (behavior == null) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tNo behavior, returning"); return; } Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tForwarding to behavior"); behavior.OnDragEnter(g, e); if (g != null && g is ControlBodyGlyph && e.Effect == DragDropEffects.None) { dragEnterReplies[g] = this; // dummy value, we just need to set something. Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tCalled DragEnter on this glyph. Caching"); } } ////// Used to send this Drag/Drop event from the AdornerWindow /// to the appropriate Behavior (or hit tested Glyph). /// private void OnDragLeave(Glyph g, EventArgs e) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "BS::DragLeave"); // This is normally cleared on OnMouseUp, but we might not get an OnMouseUp to clear it. VSWhidbey #474259 // So let's make sure it is really cleared when we start the drag. dragEnterReplies.Clear(); //if the AdornerWindow receives a drag message, this fn() //will be called w/o a glyph - so we'll assign the last //hit tested one if (g == null) { g = hitTestedGlyph; } Behavior behavior = GetAppropriateBehavior(g); if (behavior == null) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\t No behavior returning "); return; } Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tBehavior found calling OnDragLeave"); behavior.OnDragLeave(g, e); } ////// Used to send this Drag/Drop event from the AdornerWindow /// to the appropriate Behavior (or hit tested Glyph). /// private void OnDragOver(DragEventArgs e) { //cache off our validDragArgs so we can //re-fabricate enter/leave drag events validDragArgs = e; Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "BS::DragOver"); Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tNo behavior, exiting with DragDropEffects.None"); e.Effect = DragDropEffects.None; return; } if (hitTestedGlyph == null || (hitTestedGlyph != null && !dragEnterReplies.ContainsKey(hitTestedGlyph))) { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tFound glyph, forwarding to behavior"); behavior.OnDragOver(hitTestedGlyph, e); } else { Debug.WriteLineIf(dragDropSwitch.TraceVerbose, "\tFell through"); e.Effect = DragDropEffects.None; } } ////// Used to send this Drag/Drop event from the AdornerWindow /// to the appropriate Behavior (or hit tested Glyph). /// private void OnGiveFeedback(GiveFeedbackEventArgs e) { Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return; } behavior.OnGiveFeedback(hitTestedGlyph, e); } ////// Used to send this Drag/Drop event from the AdornerWindow /// to the appropriate Behavior (or hit tested Glyph). /// private void OnQueryContinueDrag(QueryContinueDragEventArgs e) { Behavior behavior = GetAppropriateBehavior(hitTestedGlyph); if (behavior == null) { return; } behavior.OnQueryContinueDrag(hitTestedGlyph, e); } ////// The AdornerWindow is a transparent window that resides ontop of the /// Designer's Frame. This window is used by the BehaviorService to /// intercept all messages. It also serves as a unified canvas on which /// to paint Glyphs. /// private class AdornerWindow : Control { private BehaviorService behaviorService;//ptr back to BehaviorService private Control designerFrame;//the designer's frame private MouseHook mouseHook; ////// Constructor that parents itself to the Designer Frame and hooks all /// necessary events. /// [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] internal AdornerWindow(BehaviorService behaviorService, Control designerFrame) { this.behaviorService = behaviorService; this.designerFrame = designerFrame; this.Dock = DockStyle.Fill; this.AllowDrop = true; this.Text = "AdornerWindow"; SetStyle(ControlStyles.Opaque,true); designerFrame.HandleDestroyed += new EventHandler(OnDesignerFrameHandleDestroyed); } ////// The key here is to set the appropriate TransparetWindow style. /// protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.Style &= ~(NativeMethods.WS_CLIPCHILDREN | NativeMethods.WS_CLIPSIBLINGS); cp.ExStyle |= 0x00000020/*WS_EX_TRANSPARENT*/; return cp; } } ////// We'll use CreateHandle as our notification for creating // our mouse hooker. /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); if (mouseHook == null) { mouseHook = new MouseHook(this, designerFrame); } mouseHook.HookMouseMessages = true; } ////// Unhook and null out our mouseHook. /// protected override void OnHandleDestroyed(EventArgs e) { if (mouseHook != null) { mouseHook.HookMouseMessages = false; mouseHook = null; } base.OnHandleDestroyed(e); } ////// Null out our mouseHook and unhook any events. /// protected override void Dispose(bool disposing) { if (disposing) { if (mouseHook != null) { mouseHook.Dispose(); mouseHook = null; } if (designerFrame != null) { designerFrame.HandleDestroyed -= new EventHandler(OnDesignerFrameHandleDestroyed); designerFrame = null; } } base.Dispose(disposing); } ////// Returns the display rectangle for the adorner window /// internal Rectangle DesignerFrameDisplayRectangle { get { if(DesignerFrameValid) { return ((DesignerFrame)designerFrame).DisplayRectangle; } else { return Rectangle.Empty; } } } ////// Returns true if the DesignerFrame is created & not being disposed. /// private bool DesignerFrameValid { get { if (designerFrame == null || designerFrame.IsDisposed || !designerFrame.IsHandleCreated) { return false; } return true; } } ////// Ultimately called by ControlDesigner when it receives a DragDrop /// message - here, we'll exit from 'drag mode'. /// internal void EndDragNotification() { this.mouseHook.ProcessingDrag = false; } ////// Invalidates the transparent AdornerWindow by asking /// the Designer Frame beneath it to invalidate. Note the /// they use of the .Update() call for perf. purposes. /// internal void InvalidateAdornerWindow() { if (DesignerFrameValid) { designerFrame.Invalidate(true); designerFrame.Update(); } } ////// Invalidates the transparent AdornerWindow by asking /// the Designer Frame beneath it to invalidate. Note the /// they use of the .Update() call for perf. purposes. /// internal void InvalidateAdornerWindow(Region region) { if (DesignerFrameValid) { //translate for non-zero scroll positions Point scrollPosition = ((DesignerFrame)designerFrame).AutoScrollPosition; region.Translate(scrollPosition.X, scrollPosition.Y); designerFrame.Invalidate(region, true); designerFrame.Update(); } } ////// Invalidates the transparent AdornerWindow by asking /// the Designer Frame beneath it to invalidate. Note the /// they use of the .Update() call for perf. purposes. /// internal void InvalidateAdornerWindow(Rectangle rectangle) { if (DesignerFrameValid) { //translate for non-zero scroll positions Point scrollPosition = ((DesignerFrame)designerFrame).AutoScrollPosition; rectangle.Offset(scrollPosition.X, scrollPosition.Y); designerFrame.Invalidate(rectangle, true); designerFrame.Update(); } } private void OnDesignerFrameHandleDestroyed(object s, EventArgs e) { if (mouseHook != null) { mouseHook.Dispose(); mouseHook = null; } } ////// The AdornerWindow hooks all Drag/Drop notification so /// they can be forwarded to the appropriate Behavior via /// the BehaviorService. /// protected override void OnDragDrop(DragEventArgs e) { try { behaviorService.OnDragDrop(e); } finally { this.mouseHook.ProcessingDrag = false; } } private static bool IsLocalDrag(DragEventArgs e) { if (e.Data is DropSourceBehavior.BehaviorDataObject) { return true; } else { // Gets all the data formats and data conversion formats in the data object. String[] allFormats = e.Data.GetFormats(); for (int i = 0; i < allFormats.Length; i++) { if (allFormats[i].Length == ToolboxFormat.Length && string.Equals(ToolboxFormat, allFormats[i])) { return true; } } } return false; } ////// The AdornerWindow hooks all Drag/Drop notification so /// they can be forwarded to the appropriate Behavior via /// the BehaviorService. /// protected override void OnDragEnter(DragEventArgs e) { this.mouseHook.ProcessingDrag = true; // determine if this is a local drag, if it is, do normal processing // otherwise, force a PropagateHitTest. We need to force // this because the OLE D&D service suspends mouse messages // when the drag is not local so the mouse hook never sees them. if (!IsLocalDrag(e)) { behaviorService.validDragArgs = e; NativeMethods.POINT pt = new NativeMethods.POINT(); NativeMethods.GetCursorPos(pt); NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt, 1); Point mousePos = new Point(pt.x, pt.y); behaviorService.PropagateHitTest(mousePos); } behaviorService.OnDragEnter(null, e); } ////// The AdornerWindow hooks all Drag/Drop notification so /// they can be forwarded to the appropriate Behavior via /// the BehaviorService. /// protected override void OnDragLeave(EventArgs e) { //set our dragArgs to null so we know not to send //drag enter/leave events when we re-enter the //dragging area behaviorService.validDragArgs = null; try { behaviorService.OnDragLeave(null, e); } finally { this.mouseHook.ProcessingDrag = false; } } ////// The AdornerWindow hooks all Drag/Drop notification so /// they can be forwarded to the appropriate Behavior via /// the BehaviorService. /// protected override void OnDragOver(DragEventArgs e) { this.mouseHook.ProcessingDrag = true; if (!IsLocalDrag(e)) { behaviorService.validDragArgs = e; NativeMethods.POINT pt = new NativeMethods.POINT(); NativeMethods.GetCursorPos(pt); NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt, 1); Point mousePos = new Point(pt.x, pt.y); behaviorService.PropagateHitTest(mousePos); } behaviorService.OnDragOver(e); } ////// The AdornerWindow hooks all Drag/Drop notification so /// they can be forwarded to the appropriate Behavior via /// the BehaviorService. /// protected override void OnGiveFeedback(GiveFeedbackEventArgs e) { behaviorService.OnGiveFeedback(e); } ////// The AdornerWindow hooks all Drag/Drop notification so /// they can be forwarded to the appropriate Behavior via /// the BehaviorService. /// protected override void OnQueryContinueDrag(QueryContinueDragEventArgs e) { behaviorService.OnQueryContinueDrag(e); } ////// Called by ControlDesigner when it receives a DragEnter /// message - we'll let listen to all Mouse Messages /// so we can send drag notifcations. /// internal void StartDragNotification() { this.mouseHook.ProcessingDrag = true; } ////// The AdornerWindow intercepts all designer-related messages and forwards them /// to the BehaviorService for appropriate actions. Note that Paint and HitTest /// messages are correctly parsed and translated to AdornerWindow coords. /// protected override void WndProc(ref Message m) { //special test hooks if (m.Msg == BehaviorService.WM_GETALLSNAPLINES) { behaviorService.TestHook_GetAllSnapLines(ref m); } else if(m.Msg == BehaviorService.WM_GETRECENTSNAPLINES) { behaviorService.TestHook_GetRecentSnapLines(ref m); } switch(m.Msg) { case NativeMethods.WM_PAINT: // // Stash off the region we have to update // IntPtr hrgn = NativeMethods.CreateRectRgn(0, 0, 0, 0); NativeMethods.GetUpdateRgn(m.HWnd, hrgn, true); // // The region we have to update in terms of the smallest rectangle // that completely encloses the update region of the window gives // us the clip rectangle // NativeMethods.RECT clip = new NativeMethods.RECT(); NativeMethods.GetUpdateRect(m.HWnd, ref clip, true); Rectangle paintRect = new Rectangle(clip.left, clip.top, clip.right - clip.left, clip.bottom - clip.top); try { using (Region r = Region.FromHrgn(hrgn)) { // // Call the base class to do its painting. // DefWndProc(ref m); // // Now do our own painting. // using (Graphics g = Graphics.FromHwnd(m.HWnd)) { using (PaintEventArgs pevent = new PaintEventArgs(g, paintRect)) { g.Clip = r; behaviorService.PropagatePaint(pevent); } } } } finally { NativeMethods.DeleteObject(hrgn); } break; case NativeMethods.WM_NCHITTEST: Point pt = new Point((short)NativeMethods.Util.LOWORD((int)m.LParam), (short)NativeMethods.Util.HIWORD((int)m.LParam)); NativeMethods.POINT pt1 = new NativeMethods.POINT(); pt1.x = 0; pt1.y = 0; NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, pt1, 1); pt.Offset(pt1.x, pt1.y); if (behaviorService.PropagateHitTest(pt) && !mouseHook.ProcessingDrag) { m.Result = (IntPtr)(NativeMethods.HTTRANSPARENT); } else { m.Result = (IntPtr)(NativeMethods.HTCLIENT); } break; case NativeMethods.WM_CAPTURECHANGED: base.WndProc(ref m); behaviorService.OnLoseCapture(); break; default: base.WndProc(ref m); break; } } ////// Called by our mouseHook when it spies a mouse message that the adornerWindow /// would be interested in. Returning 'true' signifies that the message was processed /// and should not continue to child windows. /// private bool WndProcProxy(ref Message m, int x, int y) { Point mouseLoc = new Point(x,y); behaviorService.PropagateHitTest(mouseLoc); switch (m.Msg) { case NativeMethods.WM_LBUTTONDOWN: if (behaviorService.OnMouseDown(MouseButtons.Left, mouseLoc)) { return false; } break; case NativeMethods.WM_RBUTTONDOWN: if (behaviorService.OnMouseDown(MouseButtons.Right, mouseLoc)) { return false; } break; case NativeMethods.WM_MOUSEMOVE: if (behaviorService.OnMouseMove(Control.MouseButtons, mouseLoc)) { return false; } break; case NativeMethods.WM_LBUTTONUP: if (behaviorService.OnMouseUp(MouseButtons.Left)) { return false; } break; case NativeMethods.WM_RBUTTONUP: if (behaviorService.OnMouseUp(MouseButtons.Right)) { return false; } break; case NativeMethods.WM_MOUSEHOVER: if (behaviorService.OnMouseHover(mouseLoc)) { return false; } break; case NativeMethods.WM_LBUTTONDBLCLK: if (behaviorService.OnMouseDoubleClick(MouseButtons.Left, mouseLoc)) { return false; } break; case NativeMethods.WM_RBUTTONDBLCLK: if (behaviorService.OnMouseDoubleClick(MouseButtons.Right, mouseLoc)) { return false; } break; } return true; } ////// /// This class knows how to hook all the messages to a given process/thread. On any mouse clicks, it asks the designer what to do with the message, /// that is to eat it or propogate it to the control it was meant for. This allows us to synchrounously process mouse messages when /// the AdornerWindow itself may be pumping messages. /// ///[SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] private class MouseHook { private AdornerWindow adornerWindow; private Control designerFrame; private int thisProcessID = 0; private GCHandle mouseHookRoot; [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] private IntPtr mouseHookHandle = IntPtr.Zero; private bool processingMessage; private bool processingDrag; private bool isHooked = false; //VSWHIDBEY # 474112 private int lastLButtonDownTimeStamp; public MouseHook(AdornerWindow aw, Control df) { this.designerFrame = df; this.adornerWindow = aw; #if DEBUG callingStack = Environment.StackTrace; #endif } #if DEBUG string callingStack; ~MouseHook() { Debug.Assert(mouseHookHandle == IntPtr.Zero, "Finalizing an active mouse hook. This will crash the process. Calling stack: " + callingStack); } #endif public virtual bool HookMouseMessages { get{ return mouseHookHandle != IntPtr.Zero; } set{ if (value) { HookMouse(); } else { UnhookMouse(); } } } public bool ProcessingDrag { get { return processingDrag; } set { processingDrag = value; } } public void Dispose() { UnhookMouse(); } private void HookMouse() { lock(this) { if (mouseHookHandle != IntPtr.Zero) { return; } if (thisProcessID == 0) { UnsafeNativeMethods.GetWindowThreadProcessId(new HandleRef(adornerWindow, adornerWindow.Handle), out thisProcessID); } UnsafeNativeMethods.HookProc hook = new UnsafeNativeMethods.HookProc(this.MouseHookProc); mouseHookRoot = GCHandle.Alloc(hook); #pragma warning disable 618 mouseHookHandle = UnsafeNativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE, hook, new HandleRef(null, IntPtr.Zero), AppDomain.GetCurrentThreadId()); #pragma warning restore 618 if (mouseHookHandle != IntPtr.Zero) { isHooked = true; } Debug.Assert(mouseHookHandle != IntPtr.Zero, "Failed to install mouse hook"); } } [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private unsafe IntPtr MouseHookProc(int nCode, IntPtr wparam, IntPtr lparam) { if (isHooked && nCode == NativeMethods.HC_ACTION) { NativeMethods.MOUSEHOOKSTRUCT* mhs = (NativeMethods.MOUSEHOOKSTRUCT*)lparam; if (mhs != null) { try { if (ProcessMouseMessage(mhs->hWnd, (int)wparam, mhs->pt_x, mhs->pt_y)) { return (IntPtr)1; } } catch (Exception ex) { adornerWindow.Capture = false; if (ex != CheckoutException.Canceled) { adornerWindow.behaviorService.ShowError(ex); } if (ClientUtils.IsCriticalException(ex)) { throw; } } } } Debug.Assert(isHooked, "How did we get here when we are diposed?"); return UnsafeNativeMethods.CallNextHookEx(new HandleRef(this, mouseHookHandle), nCode, wparam, lparam); } private void UnhookMouse() { lock(this) { if (mouseHookHandle != IntPtr.Zero) { UnsafeNativeMethods.UnhookWindowsHookEx(new HandleRef(this, mouseHookHandle)); mouseHookRoot.Free(); mouseHookHandle = IntPtr.Zero; isHooked = false; } } } /* * Here is where we force validation on any clicks outside the */ private bool ProcessMouseMessage(IntPtr hWnd, int msg, int x, int y) { if (processingMessage) { return false; } // We could have hooked a control in a semitrust web page. This would put // semitrust frames above us, which could cause this to fail. // SECREVIEW, new NamedPermissionSet("FullTrust").Assert(); IntPtr handle = designerFrame.Handle; // if it's us or one of our children, just process as normal // if (processingDrag || (hWnd != handle && SafeNativeMethods.IsChild(new HandleRef(this, handle), new HandleRef(this, hWnd)))) { Debug.Assert(thisProcessID != 0, "Didn't get our process id!"); // make sure the window is in our process int pid; UnsafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, hWnd), out pid); // if this isn't our process, bail if (pid != thisProcessID) { return false; } try { processingMessage = true; NativeMethods.POINT pt = new NativeMethods.POINT(); pt.x = x; pt.y = y; NativeMethods.MapWindowPoints(IntPtr.Zero, adornerWindow.Handle, pt, 1); Message m = Message.Create(hWnd, msg, (IntPtr)0, (IntPtr)MAKELONG(pt.y, pt.x)); // DevDiv Bugs 79616, No one knows why we get an extra click here from VS. // As a workaround, we check the TimeStamp and discard it. if (m.Msg == NativeMethods.WM_LBUTTONDOWN) { lastLButtonDownTimeStamp = UnsafeNativeMethods.GetMessageTime(); } else if (m.Msg == NativeMethods.WM_LBUTTONDBLCLK) { int lButtonDoubleClickTimeStamp = UnsafeNativeMethods.GetMessageTime(); if (lButtonDoubleClickTimeStamp == lastLButtonDownTimeStamp) { return true; } } if (!adornerWindow.WndProcProxy(ref m, pt.x, pt.y)) { // we did the work, stop the message propogation return true; } } finally { processingMessage = false; } } return false; } public static int MAKELONG(int low, int high) { return (high << 16) | (low & 0xffff); } } } /// /// This class is used to notifiy the BehaviorService when a 'FindCommand' /// call on the ImenuCommandService has fired. When this happens the /// BehaviorService will ask the appropriate glyph's behavior if it intends /// to interrupt the processing of the command. /// private class MenuCommandHandler : IMenuCommandService { private BehaviorService owner;//ptr back to the behavior service private IMenuCommandService menuService;//core service used for most implementations of the IMCS interface private StackcurrentCommands = new Stack (); /// /// Cache off the behsvc and the menucommand service... /// public MenuCommandHandler(BehaviorService owner, IMenuCommandService menuService) { this.owner = owner; this.menuService = menuService; } ////// get menucommand service /// public IMenuCommandService MenuService { get{ return menuService; } } ////// Just call straight through to the IMCS /// void IMenuCommandService.AddCommand(MenuCommand command) { menuService.AddCommand(command); } ////// Just call straight through to the IMCS /// void IMenuCommandService.RemoveVerb(DesignerVerb verb) { menuService.RemoveVerb(verb); } ////// Just call straight through to the IMCS /// void IMenuCommandService.RemoveCommand(MenuCommand command) { menuService.RemoveCommand(command); } ////// Give the behavior service (specifically: the hittestedglyph's behavior) /// a chance to interrupt this command. /// MenuCommand IMenuCommandService.FindCommand(CommandID commandID) { try { if (currentCommands.Contains(commandID)) { return null; } currentCommands.Push(commandID); return owner.FindCommand(commandID, menuService); } finally { currentCommands.Pop(); } } ////// Just call straight through to the IMCS /// bool IMenuCommandService.GlobalInvoke(CommandID commandID) { return menuService.GlobalInvoke(commandID); } ////// Just call straight through to the IMCS /// void IMenuCommandService.ShowContextMenu(CommandID menuID, int x, int y) { menuService.ShowContextMenu(menuID,x, y); } ////// Just call straight through to the IMCS /// void IMenuCommandService.AddVerb(DesignerVerb verb) { menuService.AddVerb(verb); } ////// Just call straight through to the IMCS /// DesignerVerbCollection IMenuCommandService.Verbs { get { return menuService.Verbs; } } } } } // 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
- CacheModeValueSerializer.cs
- StrongName.cs
- ChtmlLinkAdapter.cs
- PackageFilter.cs
- Validator.cs
- DataGridViewRowDividerDoubleClickEventArgs.cs
- propertytag.cs
- BitmapCodecInfoInternal.cs
- ParallelTimeline.cs
- EmbeddedMailObject.cs
- XmlSiteMapProvider.cs
- TextBoxLine.cs
- Binding.cs
- WorkflowTransactionService.cs
- ToolStripHighContrastRenderer.cs
- DataObjectEventArgs.cs
- Italic.cs
- ApplicationInfo.cs
- PaperSize.cs
- DeviceSpecificDialogCachedState.cs
- ToolStripDropDownItemDesigner.cs
- WebPartActionVerb.cs
- InstanceNameConverter.cs
- XomlCompilerParameters.cs
- Cursor.cs
- OLEDB_Util.cs
- ListViewCommandEventArgs.cs
- UnsafeNativeMethodsMilCoreApi.cs
- RTLAwareMessageBox.cs
- SelectorAutomationPeer.cs
- SizeAnimationUsingKeyFrames.cs
- TraceData.cs
- ExtendedPropertyCollection.cs
- DataGridViewAddColumnDialog.cs
- Attributes.cs
- _DomainName.cs
- AssemblyResolver.cs
- contentDescriptor.cs
- Directory.cs
- Matrix.cs
- Rect.cs
- BoolExpressionVisitors.cs
- CqlLexer.cs
- Ray3DHitTestResult.cs
- PositiveTimeSpanValidator.cs
- UnsafeNativeMethods.cs
- ContactManager.cs
- HtmlElementCollection.cs
- DependencyObjectType.cs
- SqlError.cs
- ButtonBaseAdapter.cs
- Stack.cs
- PrintingPermission.cs
- documentation.cs
- CodeAttributeDeclaration.cs
- DrawingState.cs
- DataFormats.cs
- IsolatedStorageFileStream.cs
- CodeCatchClause.cs
- SystemResources.cs
- ProxyWebPartManagerDesigner.cs
- AsmxEndpointPickerExtension.cs
- EntityTransaction.cs
- DbResourceAllocator.cs
- KeyEvent.cs
- XmlCountingReader.cs
- Baml2006KnownTypes.cs
- XmlReaderDelegator.cs
- DataGridHeadersVisibilityToVisibilityConverter.cs
- DoWorkEventArgs.cs
- prefixendpointaddressmessagefilter.cs
- ItemDragEvent.cs
- Number.cs
- UnsafeNativeMethods.cs
- ThreadAbortException.cs
- TypeInitializationException.cs
- SimpleHandlerBuildProvider.cs
- HttpException.cs
- IPHostEntry.cs
- GeometryValueSerializer.cs
- GPRECTF.cs
- DataReceivedEventArgs.cs
- SmtpFailedRecipientsException.cs
- TypedTableBase.cs
- ProtectedConfigurationSection.cs
- DecimalFormatter.cs
- XsdSchemaFileEditor.cs
- DeviceContexts.cs
- ResolveCompletedEventArgs.cs
- _UncName.cs
- XhtmlTextWriter.cs
- SqlDataReaderSmi.cs
- DtrList.cs
- TextTreeDeleteContentUndoUnit.cs
- IteratorFilter.cs
- WindowsSysHeader.cs
- VBIdentifierTrimConverter.cs
- Site.cs
- wgx_sdk_version.cs
- HtmlInputFile.cs