1

In my app, which is a WF project, I have a combobox with directories:

enter image description here

If a directory is too large the user can't see the entire directory name.

Now, in most applications I see that if a item of a combobox or textbox exceeds the visible limit then a kind of a little tooltip/ballontip is shown exactly at the item/mouse position displaying/expanding the full string (the full directory name I mean)

My question is how I can do the same, I don't know how to do this using the default tooltips.

UPDATE:

I've got this usercontrol, but it turns really slow when opening the dropdownlist and when while overhoving the combobox items, the "navigation" between the items are really slow and like I've said also is slow to open the dropdown list after do a click to expand the list.

I want to improve the speed like the default combobox:

PS: I don't know anything about 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);
}
4

2 回答 2

1

尝试这个:

' 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
于 2013-07-01T12:42:59.510 回答
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);
}
于 2013-07-06T09:51:20.907 回答