【问题标题】:Is it possible to add a ToolTip to the form titlebar?是否可以将工具提示添加到表单标题栏?
【发布时间】:2022-01-09 03:57:26
【问题描述】:

在表单上,​​表单的标题是在设计器文件中设置的文本。我遇到的问题是,当标题国际化时,标题会缩短并添加省略号。是否可以添加当我们将鼠标悬停在标题上时将显示的工具提示?因此,如果我将鼠标悬停在“Form1”上,应该会显示一个显示“Form1”的工具提示。

表单是用 C# 编写的。由于标题不是表单中的控件,所以无法设置工具提示。

有什么想法吗?

Form in designer

【问题讨论】:

标签: c# .net winforms tooltip titlebar


【解决方案1】:

我已经在linked post 中分享了一个示例,以展示如何为表单的非客户区(包括其标题栏)引发悬停事件并显示标题栏的工具提示。

在这篇文章中,我将通过添加对以下事件和属性的支持来重构和扩展其他答案:

  • NonClientMouseHover 当鼠标悬停在非客户区时引发。
  • NonClientMouseLeave:鼠标离开非客户区时触发。
  • TitleRectangle:标题栏的矩形,不包括系统菜单。

然后通过使用这些事件和属性,我将显示标题栏的工具提示:

通过在从BaseForm 类驱动并处理NonClientMouseHover 和NonClientMouseLeave 的表单上添加工具提示组件很容易:

public partial class SampleForm : BaseForm
{
    public SampleForm()
    {
        InitializeComponent();
    }

    private void SampleForm_NonClientMouseHover(object sender, EventArgs e)
    {
        if (TitleRectangle.Contains(Cursor.Position))
            toolTip1.Show(Text, this, TitleRectangle.Left - this.Left + 1,
                TitleRectangle.Top - this.Top - 2, 5000);
    }

    private void SampleForm_NonClientMouseLeave(object sender, EventArgs e)
    {
        if (!TitleRectangle.Contains(Cursor.Position))
            toolTip1.Hide(this);
    }
}

这里是本地方法和基类。

原生方法

这些是基类中用于处理非客户区消息的本地方法:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
public static class NativeMethods
{
    public const int WM_NCMOUSEMOVE = 0xA0;
    public const int WM_NCMOUSEHOVER = 0x2A0;
    public const int WM_NCMOUSELEAVE = 0x2A2;
    public const int TME_HOVER = 0x1;
    public const int TME_LEAVE = 0x2;
    public const int TME_NONCLIENT = 0x10;

    [DllImport("user32.dll")]
    public static extern int TrackMouseEvent(ref TRACK_MOUSE_EVENT lpEventTrack);

    [DllImport("user32.dll")]
    public static extern bool GetTitleBarInfo(IntPtr hwnd, ref TITLEBARINFO pti);
    public static Rectangle GetTitleBarRectangle(IntPtr hwnd)
    {
        var info = new TITLEBARINFO() 
        { cbSize = (uint)Marshal.SizeOf(typeof(TITLEBARINFO)) };
        GetTitleBarInfo(hwnd, ref info);
        return info.rcTitleBar.ToRectangle();
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TRACK_MOUSE_EVENT
    {
        public uint cbSize;
        public uint dwFlags;
        public IntPtr hwndTrack;
        public uint dwHoverTime;
        public static readonly TRACK_MOUSE_EVENT Empty;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TITLEBARINFO
    {
        public const int CCHILDREN_TITLEBAR = 5;
        public uint cbSize;
        public RECT rcTitleBar;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
        public uint[] rgstate;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left, Top, Right, Bottom;
        public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
    }
}

BaseForm

这是一个引发非客户区事件的基础Form 类:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using static NativeMethods;
public class BaseForm : Form
{
    public event EventHandler NonClientMouseHover;
    public event EventHandler NonClientMouseLeave;
    public event EventHandler NonClientMouseMove;
    private TRACK_MOUSE_EVENT track = TRACK_MOUSE_EVENT.Empty;
    public Rectangle TitleRectangle => GetTitleBarRectangle(Handle);
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == WM_NCMOUSEMOVE)
        {
            track.hwndTrack = this.Handle;
            track.cbSize = (uint)Marshal.SizeOf(track);
            track.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT;
            track.dwHoverTime = 500;
            TrackMouseEvent(ref track);
            NonClientMouseMove?.Invoke(this, EventArgs.Empty);
        }
        else if (m.Msg == WM_NCMOUSEHOVER)
        {
            NonClientMouseHover?.Invoke(this, EventArgs.Empty);
        }
        else if (m.Msg == WM_NCMOUSELEAVE)
        {
            NonClientMouseLeave?.Invoke(this, EventArgs.Empty);
        }
    }
}

【讨论】:

    【解决方案2】:

    一种方法是这样做:

    using System.Drawing;
    using System.Threading.Tasks;
    
    public Form1()
    {
        InitializeComponent();
        ImplementToolBarToolTip();
    }
    private readonly ToolTip _toolTip1 = new ToolTip();
    private async void ImplementToolBarToolTip()
    {
        while (!IsDisposed)
        {
            await Task.Delay(200);
            var right = Left + Width;
            var bottom = Top + 39;
            var x = Cursor.Position.X;
            var y = Cursor.Position.Y;
            if (IsDisposed) return;
            if (x > Left && x < right && y > Top && y < bottom)
            {
                _toolTip1.Show(Text, this, PointToClient(new Point(x, y)));
            }
            else _toolTip1.Hide(this);
        }
    }
    

    注意:如果您不想在用户将鼠标悬停在最右侧的关闭、最小化或最大化按钮上时显示工具提示,请从 right 值中减去 149:

    var right = Left + Width - 149;
    

    此外,有关处理 MDI 子表单的相关问题的更复杂的方法,请参阅 Reza Aghaei 的 This 帖子,他现在也通过重构的实现回复了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 2014-12-17
      • 2018-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-16
      相关资源
      最近更新 更多