public Data(SnapPoint snapPoint, IntPtr hWndChild, IntPtr hWndOwner, IntPtr hWndSnap) { this.snapPoint = snapPoint; SnapHandle = hWndSnap; nwChild = new ChildNW(hWndChild, this); nwOwner = new OwnerNW(hWndOwner, this); }
protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_SHOWWINDOW) { RECT rChild = new RECT(); RECT rOwner = new RECT(); GetWindowRect(data.SnapHandle, ref rOwner); SnapPoint sp = data.snapPoint; if (sp.NeedsChildRect) { GetWindowRect(Handle, ref rChild); } // must do this, otherwise when the window is invisible (e.g. owner is minimized) then its location resets Point pt = sp.GetLocation(rChild, rOwner); SetWindowPos(Handle, IntPtr.Zero, pt.X, pt.Y, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } }
protected override void WndProc(ref Message m) { base.WndProc(ref m); // several different approaches were tried, such as WM_MOVING, WM_MOVED, WM_WINDOWPOSCHANGING // WM_MOVING and WM_MOVED do not account for resizing the window. WM_WINDOWPOSCHANGING fires // false events (e.g. clicking on the title bar near (but not on) the minimize window button). if (m.Msg == WM_WINDOWPOSCHANGED) { WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS)); // -32000 means the window is minimized. if (pos.x > -32000 && pos.y > -32000) { RECT rChild = new RECT(); RECT rOwner = new RECT(); if (data.SnapHandle == Handle) { rOwner.Top = pos.y; rOwner.Left = pos.x; rOwner.Bottom = pos.y + pos.cy; rOwner.Right = pos.x + pos.cx; } else { GetWindowRect(data.SnapHandle, ref rOwner); } SnapPoint sp = data.snapPoint; if (sp.NeedsChildRect) { GetWindowRect(data.nwChild.Handle, ref rChild); } Point pt = sp.GetLocation(rChild, rOwner); SetWindowPos(data.nwChild.Handle, IntPtr.Zero, pt.X, pt.Y, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } }
public static void SetOwner(IntPtr hWndChild, IntPtr hWndParent, IntPtr hWndTopLevel, SnapPoint snapPoint) { Data d = (Data)htData[hWndChild]; if (d != null) { d.nwOwner.ReleaseHandle(); if (hWndTopLevel != IntPtr.Zero) { d.nwOwner = new OwnerNW(hWndTopLevel, d); } else { d.nwChild.ReleaseHandle(); htData.Remove(hWndChild); } } else { if (hWndTopLevel == IntPtr.Zero) { return; } d = new Data(snapPoint, hWndChild, hWndTopLevel, hWndParent); htData[hWndChild] = d; } }
///<summary>Snaps the child window to the top level owner window, relative to the hWndSnap window. As ///the top level owner window is moved, resized, minimized or maximized, the child window moves too.</summary> ///<param name="child">The control that is automtically moved, e.g. a ToolStripDropDown window.</param> ///<param name="hWndTopLevel">A handle to a top-level window, e.g. a Form control's Handle.</param> ///<param name="hWndParent">A handle to a control that child window is relatively positioned, e.g. a TextBox or ComoBox.</param> ///<param name="snapPoint">Parameters that specify the X and Y location.</param> public static void SnapWindow(this Control child, IntPtr hWndParent, IntPtr hWndTopLevel, SnapPoint snapPoint) { if (child.IsHandleCreated) { SetOwner(child.Handle, hWndParent, hWndTopLevel, snapPoint); } else { child.HandleCreated += delegate { SetOwner(child.Handle, hWndParent, hWndTopLevel, snapPoint); }; } }