首页 文章

使用ToolStripDropDown模拟工具提示功能

提问于
浏览
1

我创建了一个自定义UserControl,它在ToolStripDropDown中显示自己以模拟ToolTip功能 . 当我订阅他们的MouseEnter和MouseLeave事件时,它几乎适用于任何Control .

我也想将它用于自定义对象(而不是控件) . 我创建了一个定义MouseEnter和MouseLeave事件的接口,以便我可以将任何对象(例如自定义绘制的基元)订阅到此工具提示 . 这些类自己完成工作以确定何时触发MouseEnter和MouseLeave .

我的问题是,当显示工具提示时,包含自定义对象的UserControls不会收到MouseMove事件,即使工具提示显示在侧面而不是鼠标下方 . 我正在生成我自己的MouseLeave事件,如果鼠标不再位于相关对象上,则基于检查MouseMove . 但显然没有MouseMove事件,MouseLeave永远不会触发 .

当我在Control上显示工具提示时,除了MouseLeave仍然触发之外,同样的事情发生(没有MouseMove事件) .

1)如何模拟MouseLeave功能?我是否必须使用p / invoke进行SetCapture鼠标移动,或者有人知道更简单的方法吗?

2)当工具提示显示时,即使其中的ToolStripDropDown或我的UserControl都没有触发“GotFocus”事件,只要显示工具提示,我仍然会失去键盘焦点,这也是不理想的工具提示行为 . 我可以避免吗?

基本上我希望它是一个完全不可聚焦,无干扰的工具提示 . 我查看了一个名为SuperTooltip的示例项目,但它具有相同的缺陷功能 . 我已经尝试将ControlStyles.Selectable设置为false并且没有注意到任何更改 .

这是我创建工具提示UserControl的代码:

public CustomTooltip()
{
    this.SetStyle(ControlStyles.Selectable, false);

    dropDown = new ToolStripDropDown();
    dropDown.AutoSize = false;
    dropDown.Margin = Padding.Empty;
    dropDown.Padding = Padding.Empty;

    host = new ToolStripControlHost(this);
    host.AutoSize = false;
    host.Margin = Padding.Empty;
    host.Padding = Padding.Empty;

    this.Location = new Point(0, 0);
    dropDown.Items.Add(host);
}

我用它来表明:

dropDown.Show(
    new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Current.Size.Height),
    ToolStripDropDownDirection.BelowRight
);

1 回答

  • 1

    我发现我能够通过从Form派生而不是使用ToolStripDropDown来实现这一目标 . 此类模拟工具提示的功能,并允许自定义淡入/淡出参数 . 您可以订阅控件或任何为MouseEnter和MouseLeave定义和实现ITooltipTarget的类 .

    public abstract class CustomTooltip : Form
    {
        #region Static
        protected static readonly int FadeInterval = 25;
        protected static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
        private const int SWP_NOSIZE = 0x0001;
        private const int SWP_NOMOVE = 0x0002;
        private const int SWP_NOACTIVATE = 0x0010;
        private const int WS_POPUP = unchecked((int)0x80000000);
        private const int WS_EX_TOPMOST = 0x00000008;
        private const int WS_EX_NOACTIVATE = 0x08000000;
    
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        #endregion
    
        protected Dictionary<object, object> subscriptions;
        protected Timer popupTimer;
        protected Timer fadeTimer;
        protected bool isFading = false;
        protected int fadeDirection = 1;
    
        [DefaultValue(500)]
        /// <summary>
        /// Delay in milliseconds before the tooltip is shown.  0 means no delay.
        /// </summary>
        public int PopupDelay
        {
            get
            {
                return _popupDelay;
            }
            set
            {
                _popupDelay = value;
    
                if (value > 0)
                    popupTimer.Interval = value;
                else
                    popupTimer.Interval = 1;
            }
        }
        private int _popupDelay = 500;
    
        [DefaultValue(0)]
        /// <summary>
        /// How long to spend fading in and out in milliseconds.  0 means no fade.
        /// </summary>
        public int FadeTime
        {
            get
            {
                return _fadeTime;
            }
            set
            {
                _fadeTime = value;
            }
        }
        private int _fadeTime = 0;
    
        public virtual new object Tag
        {
            get
            {
                return base.Tag;
            }
            set
            {
                base.Tag = value;
    
                OnTagChanged(EventArgs.Empty);
            }
        }
    
        public CustomTooltip()
        {
            this.SetStyle(ControlStyles.Selectable, false);
    
            subscriptions = new Dictionary<object, object>();
    
            popupTimer = new Timer();
            popupTimer.Interval = PopupDelay;
            popupTimer.Tick += new EventHandler(popupTimer_Tick);
    
            fadeTimer = new Timer();
            fadeTimer.Interval = FadeInterval;
            fadeTimer.Tick += new EventHandler(fadeTimer_Tick);
    
            this.Visible = false;
            this.ShowInTaskbar = false;
            this.FormBorderStyle = FormBorderStyle.None;
            this.ControlBox = false;
            this.StartPosition = FormStartPosition.Manual;
        }
    
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
    
                cp.Style |= WS_POPUP;
                cp.ExStyle |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
    
                return cp;
            }
        }
    
        protected override bool ShowWithoutActivation
        {
            get
            {
                return true;
            }
        }
    
        protected virtual void Subscribe(Control control, object tag)
        {
            subscriptions.Add(control, tag);
            control.MouseEnter += new EventHandler(Item_MouseEnter);
            control.MouseLeave += new EventHandler(Item_MouseLeave);
        }
    
        protected virtual void Subscribe(ITooltipTarget item, object tag)
        {
            subscriptions.Add(item, tag);
            item.MouseEnter += new EventHandler(Item_MouseEnter);
            item.MouseLeave += new EventHandler(Item_MouseLeave);
        }
    
        public virtual void Unsubscribe(Control control)
        {
            control.MouseEnter -= new EventHandler(Item_MouseEnter);
            control.MouseLeave -= new EventHandler(Item_MouseLeave);
            subscriptions.Remove(control);
        }
    
        public virtual void Unsubcribe(ITooltipTarget item)
        {
            item.MouseEnter -= new EventHandler(Item_MouseEnter);
            item.MouseLeave -= new EventHandler(Item_MouseLeave);
            subscriptions.Remove(item);
        }
    
        public void ClearSubscriptions()
        {
            foreach (object o in subscriptions.Keys)
            {
                if (o is Control)
                    Unsubscribe((Control)o);
                else if (o is ITooltipTarget)
                    Unsubscribe((ITooltipTarget)o);
            }
        }
    
        protected virtual void OnTagChanged(EventArgs e)
        {
        }
    
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
        }
    
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
    
            Item_MouseLeave(null, EventArgs.Empty);
        }
    
        private void Item_MouseEnter(object sender, EventArgs e)
        {
            Tag = subscriptions[sender];
            popupTimer.Start();
        }
    
        private void Item_MouseLeave(object sender, EventArgs e)
        {
            if (FadeTime > 0)
                FadeOut();
            else
                this.Hide();
    
            popupTimer.Stop();
        }
    
        protected virtual void FadeIn()
        {
            isFading = true;
            Opacity = 0;
            fadeDirection = 1;
            fadeTimer.Start();
        }
    
        protected virtual void FadeOut()
        {
            isFading = true;
            Opacity = 1;
            fadeDirection = -1;
            fadeTimer.Start();
        }
    
        private void popupTimer_Tick(object sender, EventArgs e)
        {
            if (isFading)
                this.Hide();
    
            if (FadeTime > 0)
                FadeIn();
    
            Location = new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Size.Height);
            SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
            Show();
    
            popupTimer.Stop();
        }
    
        private void fadeTimer_Tick(object sender, EventArgs e)
        {
            if (Opacity == 0 && fadeDirection == -1)
            {
                isFading = false;
                fadeTimer.Stop();
                this.Hide();
            }
            else if (Opacity == 1 && fadeDirection == 1)
            {
                fadeTimer.Stop();
                isFading = false;
            }
            else
            {
                double change = ((double)fadeTimer.Interval / (double)FadeTime) * (double)fadeDirection;
                Opacity += change;
            }
        }
    }
    
    public interface ITooltipTarget
    {
        event EventHandler MouseEnter;
        event EventHandler MouseLeave;
    }
    

    要使用上述类,您只需从CustomTooltip派生自己的自定义绘制工具提示 . 派生类将使用Tag属性来确定显示的内容 . 例如,如果我想要一个将Image与一个对象相关联并绘制该图像的工具提示,我会做类似的事情:

    public class CustomImageTooltip : CustomTooltip
    {
         public Image Image
         {
            get
            {
                if (Tag is Image)
                    return Tag as Image;
                else
                    return null;
            }
         }
    
        public CustomImageTooltip()
        {
            InitializeComponent();
    
            this.SetStyle(ControlStyles.DoubleBuffer |
                          ControlStyles.AllPaintingInWmPaint |
                          ControlStyles.UserPaint, true);
        }
    
        public void Subscribe(Control control, Image image)
        {
            base.Subscribe(control, image);
        }
    
        public void Subscribe(ITooltipTarget item, Image image)
        {
            base.Subscribe(item, image);
        }
    
        protected override void OnTagChanged(EventArgs e)
        {
            base.OnTagChanged(e);
            if (Image != null)
                this.Size = Image.Size;
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
    
            g.Clear(Color.White);
    
            if (Image != null)
                g.DrawImage(
                    Image,
                    new RectangleF(0, 0, ClientSize.Width, ClientSize.Height),
                    new RectangleF(0, 0, Image.Size.Width, Image.Size.Height),
                    GraphicsUnit.Pixel
                );
    
            g.DrawRectangle(Pens.Black, 0, 0, ClientRectangle.Width - 1, ClientRectangle.Height - 1);
        }
    }
    

    要在您的应用程序中使用此CustomImageTooltip类,您只需订阅和取消订阅该类的单个实例:

    // Constructor
    customImageTooltip = new CustomImageTooltip();
    
    foreach (CustomObject o in myCustomObjects)
    {
        customImageTooltip.Subscribe(o, o.Image);
    }
    
    // Destructor
    foreach (CustomObject o in myCustomObjects)
    {
        customImageTooltip.Unsubscribe(o);
    }
    

相关问题