using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; using System.Security.Permissions; using System.Runtime.InteropServices; using VS = System.Windows.Forms.VisualStyles; /*
  • Base class for custom tooltips.
  • Office-2007-like tooltip class.
  • */ namespace Sheng.Winform.Controls.PopupControl { /// /// Represents a pop-up window. /// //[CLSCompliant(true), ToolboxItem(false)] public partial class Popup : ToolStripDropDown { #region " Fields & Properties " private Control content; /// /// Gets the content of the pop-up. /// public Control Content { get { return content; } } private PopupAnimations showingAnimation; /// /// Determines which animation to use while showing the pop-up window. /// public PopupAnimations ShowingAnimation { get { return showingAnimation; } set { if (showingAnimation != value) showingAnimation = value; } } private PopupAnimations hidingAnimation; /// /// Determines which animation to use while hiding the pop-up window. /// public PopupAnimations HidingAnimation { get { return hidingAnimation; } set { if (hidingAnimation != value) hidingAnimation = value; } } private int animationDuration; /// /// Determines the duration of the animation. /// public int AnimationDuration { get { return animationDuration; } set { if (animationDuration != value) animationDuration = value; } } private bool focusOnOpen = true; /// /// Gets or sets a value indicating whether the content should receive the focus after the pop-up has been opened. /// /// true if the content should be focused after the pop-up has been opened; otherwise, false. /// If the FocusOnOpen property is set to false, then pop-up cannot use the fade effect. public bool FocusOnOpen { get { return focusOnOpen; } set { focusOnOpen = value; } } private bool acceptAlt = true; /// /// Gets or sets a value indicating whether presing the alt key should close the pop-up. /// /// true if presing the alt key does not close the pop-up; otherwise, false. public bool AcceptAlt { get { return acceptAlt; } set { acceptAlt = value; } } private Control opener; private Popup ownerPopup; private Popup childPopup; private bool resizableTop; private bool resizableLeft; private bool isChildPopupOpened; private bool resizable; /// /// Gets or sets a value indicating whether the is resizable. /// /// true if resizable; otherwise, false. public bool Resizable { get { return resizable && !isChildPopupOpened; } set { resizable = value; } } private ToolStripControlHost host; private Size minSize; /// /// Gets or sets a minimum size of the pop-up. /// /// An ordered pair of type representing the width and height of a rectangle. public new Size MinimumSize { get { return minSize; } set { minSize = value; } } private Size maxSize; /// /// Gets or sets a maximum size of the pop-up. /// /// An ordered pair of type representing the width and height of a rectangle. public new Size MaximumSize { get { return maxSize; } set { maxSize = value; } } /// /// Gets parameters of a new window. /// /// An object of type used when creating a new window. protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ExStyle |= NativeMethods.WS_EX_NOACTIVATE; return cp; } } #endregion #region " Constructors " /// /// Initializes a new instance of the class. /// /// The content of the pop-up. /// /// Pop-up will be disposed immediately after disposion of the content control. /// /// is null. public Popup(Control content) { if (content == null) { throw new ArgumentNullException("content"); } this.content = content; this.showingAnimation = PopupAnimations.SystemDefault; this.hidingAnimation = PopupAnimations.None; this.animationDuration = 100; this.isChildPopupOpened = false; InitializeComponent(); AutoSize = false; DoubleBuffered = true; ResizeRedraw = true; host = new ToolStripControlHost(content); Padding = Margin = host.Padding = host.Margin = Padding.Empty; MinimumSize = content.MinimumSize; content.MinimumSize = content.Size; MaximumSize = content.MaximumSize; content.MaximumSize = content.Size; Size = content.Size; TabStop = content.TabStop = true; content.Location = Point.Empty; Items.Add(host); content.Disposed += delegate(object sender, EventArgs e) { content = null; Dispose(true); }; content.RegionChanged += delegate(object sender, EventArgs e) { UpdateRegion(); }; content.Paint += delegate(object sender, PaintEventArgs e) { PaintSizeGrip(e); }; UpdateRegion(); } #endregion #region " Methods " /// /// Raises the event. /// /// An that contains the event data. protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); if ((Visible && ShowingAnimation == PopupAnimations.None) || (!Visible && HidingAnimation == PopupAnimations.None)) { return; } NativeMethods.AnimationFlags flags = Visible ? NativeMethods.AnimationFlags.Roll : NativeMethods.AnimationFlags.Hide; PopupAnimations _flags = Visible ? ShowingAnimation : HidingAnimation; if (_flags == PopupAnimations.SystemDefault) { if (SystemInformation.IsMenuAnimationEnabled) { if (SystemInformation.IsMenuFadeEnabled) { _flags = PopupAnimations.Blend; } else { _flags = PopupAnimations.Slide | (Visible ? PopupAnimations.TopToBottom : PopupAnimations.BottomToTop); } } else { _flags = PopupAnimations.None; } } if ((_flags & (PopupAnimations.Blend | PopupAnimations.Center | PopupAnimations.Roll | PopupAnimations.Slide)) == PopupAnimations.None) { return; } if (resizableTop) // popup is “inverted”, so the animation must be { if ((_flags & PopupAnimations.BottomToTop) != PopupAnimations.None) { _flags = (_flags & ~PopupAnimations.BottomToTop) | PopupAnimations.TopToBottom; } else if ((_flags & PopupAnimations.TopToBottom) != PopupAnimations.None) { _flags = (_flags & ~PopupAnimations.TopToBottom) | PopupAnimations.BottomToTop; } } if (resizableLeft) // popup is “inverted”, so the animation must be { if ((_flags & PopupAnimations.RightToLeft) != PopupAnimations.None) { _flags = (_flags & ~PopupAnimations.RightToLeft) | PopupAnimations.LeftToRight; } else if ((_flags & PopupAnimations.LeftToRight) != PopupAnimations.None) { _flags = (_flags & ~PopupAnimations.LeftToRight) | PopupAnimations.RightToLeft; } } flags = flags | (NativeMethods.AnimationFlags.Mask & (NativeMethods.AnimationFlags)(int)_flags); NativeMethods.AnimateWindow(this, AnimationDuration, flags); } /// /// Processes a dialog box key. /// /// One of the values that represents the key to process. /// /// true if the key was processed by the control; otherwise, false. /// [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] protected override bool ProcessDialogKey(Keys keyData) { if (acceptAlt && ((keyData & Keys.Alt) == Keys.Alt)) { if ((keyData & Keys.F4) != Keys.F4) { return false; } else { this.Close(); } } bool processed = base.ProcessDialogKey(keyData); if (!processed && (keyData == Keys.Tab || keyData == (Keys.Tab | Keys.Shift))) { bool backward = (keyData & Keys.Shift) == Keys.Shift; this.Content.SelectNextControl(null, !backward, true, true, true); } return processed; } /// /// Updates the pop-up region. /// protected void UpdateRegion() { if (this.Region != null) { this.Region.Dispose(); this.Region = null; } if (content.Region != null) { this.Region = content.Region.Clone(); } } /// /// Shows the pop-up window below the specified control. /// /// The control below which the pop-up will be shown. /// /// When there is no space below the specified control, the pop-up control is shown above it. /// /// is null. public void Show(Control control) { if (control == null) { throw new ArgumentNullException("control"); } Show(control, control.ClientRectangle); } /// /// Shows the pop-up window below the specified area of the specified control. /// /// The control used to compute screen location of specified area. /// The area of control below which the pop-up will be shown. /// /// When there is no space below specified area, the pop-up control is shown above it. /// /// is null. public void Show(Control control, Rectangle area) { if (control == null) { throw new ArgumentNullException("control"); } SetOwnerItem(control); resizableTop = resizableLeft = false; Point location = control.PointToScreen(new Point(area.Left, area.Top + area.Height)); Rectangle screen = Screen.FromControl(control).WorkingArea; if (location.X + Size.Width > (screen.Left + screen.Width)) { resizableLeft = true; location.X = (screen.Left + screen.Width) - Size.Width; } if (location.Y + Size.Height > (screen.Top + screen.Height)) { resizableTop = true; location.Y -= Size.Height + area.Height; } location = control.PointToClient(location); Show(control, location, ToolStripDropDownDirection.BelowRight); } private void SetOwnerItem(Control control) { if (control == null) { return; } if (control is Popup) { Popup popupControl = control as Popup; ownerPopup = popupControl; ownerPopup.childPopup = this; OwnerItem = popupControl.Items[0]; return; } else if (opener == null) { opener = control; } if (control.Parent != null) { SetOwnerItem(control.Parent); } } /// /// Raises the event. /// /// An that contains the event data. protected override void OnSizeChanged(EventArgs e) { content.MinimumSize = Size; content.MaximumSize = Size; content.Size = Size; content.Location = Point.Empty; base.OnSizeChanged(e); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnOpening(CancelEventArgs e) { if (content.IsDisposed || content.Disposing) { e.Cancel = true; return; } UpdateRegion(); base.OnOpening(e); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnOpened(EventArgs e) { if (ownerPopup != null) { ownerPopup.isChildPopupOpened = true; } if (focusOnOpen) { content.Focus(); } base.OnOpened(e); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnClosed(ToolStripDropDownClosedEventArgs e) { opener = null; if (ownerPopup != null) { ownerPopup.isChildPopupOpened = false; } base.OnClosed(e); } #endregion #region " Resizing Support " /// /// Processes Windows messages. /// /// The Windows to process. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { //if (m.Msg == NativeMethods.WM_PRINT && !Visible) //{ // Visible = true; //} if (InternalProcessResizing(ref m, false)) { return; } base.WndProc(ref m); } /// /// Processes the resizing messages. /// /// The message. /// true, if the WndProc method from the base class shouldn't be invoked. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public bool ProcessResizing(ref Message m) { return InternalProcessResizing(ref m, true); } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] private bool InternalProcessResizing(ref Message m, bool contentControl) { if (m.Msg == NativeMethods.WM_NCACTIVATE && m.WParam != IntPtr.Zero && childPopup != null && childPopup.Visible) { childPopup.Hide(); } if (!Resizable) { return false; } if (m.Msg == NativeMethods.WM_NCHITTEST) { return OnNcHitTest(ref m, contentControl); } else if (m.Msg == NativeMethods.WM_GETMINMAXINFO) { return OnGetMinMaxInfo(ref m); } return false; } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] private bool OnGetMinMaxInfo(ref Message m) { NativeMethods.MINMAXINFO minmax = (NativeMethods.MINMAXINFO)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.MINMAXINFO)); if (!this.MaximumSize.IsEmpty) { minmax.maxTrackSize = this.MaximumSize; } minmax.minTrackSize = this.MinimumSize; Marshal.StructureToPtr(minmax, m.LParam, false); return true; } private bool OnNcHitTest(ref Message m, bool contentControl) { int x = NativeMethods.LOWORD(m.LParam); int y = NativeMethods.HIWORD(m.LParam); Point clientLocation = PointToClient(new Point(x, y)); GripBounds gripBouns = new GripBounds(contentControl ? content.ClientRectangle : ClientRectangle); IntPtr transparent = new IntPtr(NativeMethods.HTTRANSPARENT); if (resizableTop) { if (resizableLeft && gripBouns.TopLeft.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTTOPLEFT; return true; } if (!resizableLeft && gripBouns.TopRight.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTTOPRIGHT; return true; } if (gripBouns.Top.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTTOP; return true; } } else { if (resizableLeft && gripBouns.BottomLeft.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTBOTTOMLEFT; return true; } if (!resizableLeft && gripBouns.BottomRight.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTBOTTOMRIGHT; return true; } if (gripBouns.Bottom.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTBOTTOM; return true; } } if (resizableLeft && gripBouns.Left.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTLEFT; return true; } if (!resizableLeft && gripBouns.Right.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTRIGHT; return true; } return false; } private VS.VisualStyleRenderer sizeGripRenderer; /// /// Paints the sizing grip. /// /// The instance containing the event data. public void PaintSizeGrip(PaintEventArgs e) { if (e == null || e.Graphics == null || !resizable) { return; } Size clientSize = content.ClientSize; using (Bitmap gripImage = new Bitmap(0x10, 0x10)) { using (Graphics g = Graphics.FromImage(gripImage)) { if (Application.RenderWithVisualStyles) { if (this.sizeGripRenderer == null) { this.sizeGripRenderer = new VS.VisualStyleRenderer(VS.VisualStyleElement.Status.Gripper.Normal); } this.sizeGripRenderer.DrawBackground(g, new Rectangle(0, 0, 0x10, 0x10)); } else { ControlPaint.DrawSizeGrip(g, content.BackColor, 0, 0, 0x10, 0x10); } } GraphicsState gs = e.Graphics.Save(); e.Graphics.ResetTransform(); if (resizableTop) { if (resizableLeft) { e.Graphics.RotateTransform(180); e.Graphics.TranslateTransform(-clientSize.Width, -clientSize.Height); } else { e.Graphics.ScaleTransform(1, -1); e.Graphics.TranslateTransform(0, -clientSize.Height); } } else if (resizableLeft) { e.Graphics.ScaleTransform(-1, 1); e.Graphics.TranslateTransform(-clientSize.Width, 0); } e.Graphics.DrawImage(gripImage, clientSize.Width - 0x10, clientSize.Height - 0x10 + 1, 0x10, 0x10); e.Graphics.Restore(gs); } } #endregion } }