In my app, which is a WF project, I have a combobox with directories:
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);
}
Well yo can try this:
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
And the user control codes:
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);
}