【问题标题】:Display tooltip when a item of ComboBox exceeds the visible limit?当 ComboBox 的一项超过可见限制时显示工具提示?
【发布时间】:2013-07-01 10:06:44
【问题描述】:

在我的应用程序中,这是一个 WF 项目,我有一个带有目录的组合框:

如果目录太大,用户将看不到整个目录名称。

现在,在大多数应用程序中,我看到如果组合框或文本框的项目超过可见限制,则在项目/鼠标位置显示/展开完整字符串(完整我的意思是目录名)

我的问题是如何做到这一点,我不知道如何使用默认工具提示来做到这一点。

更新:

我有这个用户控件,但是打开下拉列表时它变得非常慢,当悬停组合框项目时,项目之间的“导航”非常慢,就像我说的那样打开下拉列表也很慢列表后单击展开列表。

我想像默认组合框一样提高速度:

PS:我对C#一无所知

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class ComboBoxTooltip : ComboBox
{
    private DropdownWindow mDropdown;
    public delegate void DropdownItemSelectedEventHandler(object sender, DropdownItemSelectedEventArgs e);
    public event DropdownItemSelectedEventHandler DropdownItemSelected;

    protected override void OnDropDown(EventArgs e)
    {
        // Install wrapper
        base.OnDropDown(e);
        // Retrieve handle to dropdown list
        COMBOBOXINFO info = new COMBOBOXINFO();
        info.cbSize = Marshal.SizeOf(info);
        SendMessageCb(this.Handle, 0x164, IntPtr.Zero, out info);
        mDropdown = new DropdownWindow(this);
        mDropdown.AssignHandle(info.hwndList);
    }
    protected override void OnDropDownClosed(EventArgs e)
    {
        // Remove wrapper
        mDropdown.ReleaseHandle();
        mDropdown = null;
        base.OnDropDownClosed(e);
        OnSelect(-1, Rectangle.Empty, true);
    }
    internal void OnSelect(int item, Rectangle pos, bool scroll)
    {
        if (this.DropdownItemSelected != null)
        {
            pos = this.RectangleToClient(pos);
            DropdownItemSelected(this, new DropdownItemSelectedEventArgs(item, pos, scroll));
        }
    }
    // Event handler arguments
    public class DropdownItemSelectedEventArgs : EventArgs
    {
        private int mItem;
        private Rectangle mPos;
        private bool mScroll;
        public DropdownItemSelectedEventArgs(int item, Rectangle pos, bool scroll) { mItem = item; mPos = pos; mScroll = scroll; }
        public int SelectedItem { get { return mItem; } }
        public Rectangle Bounds { get { return mPos; } }
        public bool Scrolled { get { return mScroll; } }
    }

    // Wrapper for combobox dropdown list
    private class DropdownWindow : NativeWindow
    {
        private ComboBoxTooltip mParent;
        private int mItem;
        public DropdownWindow(ComboBoxTooltip parent)
        {
            mParent = parent;
            mItem = -1;
        }
        protected override void WndProc(ref Message m)
        {
            // All we're getting here is WM_MOUSEMOVE, ask list for current selection for LB_GETCURSEL
            Console.WriteLine(m.ToString());
            base.WndProc(ref m);
            if (m.Msg == 0x200)
            {
                int item = (int)SendMessage(this.Handle, 0x188, IntPtr.Zero, IntPtr.Zero);
                if (item != mItem)
                {
                    mItem = item;
                    OnSelect(false);
                }
            }
            if (m.Msg == 0x115)
            {
                // List scrolled, item position would change
                OnSelect(true);
            }
        }
        private void OnSelect(bool scroll)
        {
            RECT rc = new RECT();
            SendMessageRc(this.Handle, 0x198, (IntPtr)mItem, out rc);
            MapWindowPoints(this.Handle, IntPtr.Zero, ref rc, 2);
            mParent.OnSelect(mItem, Rectangle.FromLTRB(rc.Left, rc.Top, rc.Right, rc.Bottom), scroll);
        }
    }
    // P/Invoke declarations
    private struct COMBOBOXINFO
    {
        public Int32 cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
    [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);
    [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessageRc(IntPtr hWnd, int msg, IntPtr wp, out RECT lp);
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    [DllImport("user32.dll")]
    private static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref RECT rc, int points);
}

【问题讨论】:

  • 这是 WPF 还是(好看的)WinForm?
  • @keyboardP 对不起,我忘了评论那个大细节,是一个 WindowsForm
  • @ElektroHacker - 这似乎是一个显而易见的功能,但没有简单的属性可以处理。我还没有尝试过 David 链接中的方法,但如果它们不令人满意,NoBugz 发布了一个扩展的 ComboBox,它支持工具提示 here。 TripleDuck 和 frog297 也有更简单的建议,您可以先尝试一下。
  • @david 解决方案非常适合列表框,但有一条指令我无法为组合框重现 (Me.ListBox1.IndexFromPoint),工具提示也显示为全闪烁(工具提示每秒重新显示...)是一种令人作呕的视觉效果。我会试试你说的keyboardP

标签: .net vb.net tooltip balloon-tip


【解决方案1】:

你可以试试这个:

    Dim LastSelectedItem As Int32 = -1

Private Sub ComboBoxTooltip_DropdownItemSelected(sender As Object, e As ComboBoxTooltip.DropdownItemSelectedEventArgs) _
Handles ComboBoxTooltip1.DropdownItemSelected

    Dim SelectedItem As Int32 = e.SelectedItem

    If SelectedItem <> LastSelectedItem Then
        ToolTip1.Hide(sender)
        LastSelectedItem = -1
    End If

    If SelectedItem < 0 OrElse e.Scrolled Then
        ToolTip1.Hide(sender)
        LastSelectedItem = -1
    Else
        If sender.Items(e.SelectedItem).Length > CInt(sender.CreateGraphics.MeasureString(0, sender.Font).Width) + 8 Then
            LastSelectedItem = SelectedItem
            ToolTip1.Show(sender.Items(SelectedItem).ToString(), sender, e.Bounds.Location)
        End If
    End If

End Sub

以及用户控制代码:

    using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class ComboBoxTooltip : ComboBox
{
    private DropdownWindow mDropdown;
    public delegate void DropdownItemSelectedEventHandler(object sender, DropdownItemSelectedEventArgs e);
    public event DropdownItemSelectedEventHandler DropdownItemSelected;

    protected override void OnDropDown(EventArgs e)
    {
        // Install wrapper
        base.OnDropDown(e);
        // Retrieve handle to dropdown list
        COMBOBOXINFO info = new COMBOBOXINFO();
        info.cbSize = Marshal.SizeOf(info);
        SendMessageCb(this.Handle, 0x164, IntPtr.Zero, out info);
        mDropdown = new DropdownWindow(this);
        mDropdown.AssignHandle(info.hwndList);
    }
    protected override void OnDropDownClosed(EventArgs e)
    {
        // Remove wrapper
        mDropdown.ReleaseHandle();
        mDropdown = null;
        base.OnDropDownClosed(e);
        OnSelect(-1, Rectangle.Empty, true);
    }
    internal void OnSelect(int item, Rectangle pos, bool scroll)
    {
        if (this.DropdownItemSelected != null)
        {
            pos = this.RectangleToClient(pos);
            DropdownItemSelected(this, new DropdownItemSelectedEventArgs(item, pos, scroll));
        }
    }
    // Event handler arguments
    public class DropdownItemSelectedEventArgs : EventArgs
    {
        private int mItem;
        private Rectangle mPos;
        private bool mScroll;
        public DropdownItemSelectedEventArgs(int item, Rectangle pos, bool scroll) { mItem = item; mPos = pos; mScroll = scroll; }
        public int SelectedItem { get { return mItem; } }
        public Rectangle Bounds { get { return mPos; } }
        public bool Scrolled { get { return mScroll; } }
    }

    // Wrapper for combobox dropdown list
    private class DropdownWindow : NativeWindow
    {
        private ComboBoxTooltip mParent;
        private int mItem;
        public DropdownWindow(ComboBoxTooltip parent)
        {
            mParent = parent;
            mItem = -1;
        }
        protected override void WndProc(ref Message m)
        {
            // All we're getting here is WM_MOUSEMOVE, ask list for current selection for LB_GETCURSEL
            Console.WriteLine(m.ToString());
            base.WndProc(ref m);
            if (m.Msg == 0x200)
            {
                int item = (int)SendMessage(this.Handle, 0x188, IntPtr.Zero, IntPtr.Zero);
                if (item != mItem)
                {
                    mItem = item;
                    OnSelect(false);
                }
            }
            if (m.Msg == 0x115)
            {
                // List scrolled, item position would change
                OnSelect(true);
            }
        }
        private void OnSelect(bool scroll)
        {
            RECT rc = new RECT();
            SendMessageRc(this.Handle, 0x198, (IntPtr)mItem, out rc);
            MapWindowPoints(this.Handle, IntPtr.Zero, ref rc, 2);
            mParent.OnSelect(mItem, Rectangle.FromLTRB(rc.Left, rc.Top, rc.Right, rc.Bottom), scroll);
        }
    }
    // P/Invoke declarations
    private struct COMBOBOXINFO
    {
        public Int32 cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
    [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);
    [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessageRc(IntPtr hWnd, int msg, IntPtr wp, out RECT lp);
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    [DllImport("user32.dll")]
    private static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref RECT rc, int points);
}

【讨论】:

    【解决方案2】:

    试试这个:

    ' Get the longest element
    For Each elem As String In CBox1.Items
        If elem.Length > auxCad.Length Then auxCad = elem
    Next
    
    ' Get the size
    iSize = CInt(CBox1.CreateGraphics.MeasureString(auxCad, CBox1.Font).Width) + 20
    
    If iSize > CBox1.Width Then
        'Show tooltip
    End If
    

    【讨论】:

    • 使用那段代码我无法实现的许多事情,在“'显示工具提示”注释中我想首先我需要知道鼠标指针是否在该项目上,其次我如何可以在该坐标中准确显示工具提示吗?
    • 好吧,那么我建议你检查一下鼠标指针在 ComboBox 的 OnEnter 事件上的坐标,并在使用我的代码的帮助后,显示带有 ToolTip1.Show() 的工具提示。它有很多实现可以在你想要的地方展示它:msdn.microsoft.com/en-us/library/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 2019-06-05
    • 2013-02-20
    • 1970-01-01
    • 2014-06-16
    相关资源
    最近更新 更多