I am trying to set the tool tip text for some of my subitems in my listview control. I am unable to get the tool tip to show up.
Anyone have any suggestions?
ObjectListView (an open source wrapper around .NET WinForms ListView) has builtin support for cell tooltips (and, yes, it does work with VB). You listen for a CellToolTip
event and you can do things like this (which is admittedly excessive):
If you don't want to use ObjectListView, you need to subclass ListView, listen for WM_NOTIFY
messages, and then within those, respond to TTN_GETDISPINFO
notifications, in a manner similar to this:
case TTN_GETDISPINFO:
ListViewHitTestInfo info = this.HitTest(this.PointToClient(Cursor.Position));
if (info.Item != null && info.SubItem != null) {
// Call some method of your own to get the tooltip you want
String tip = this.GetCellToolTip(info.Item, info.SubItem);
if (!String.IsNullOrEmpty(tip)) {
NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT)m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT));
ttt.lpszText = tip;
if (this.RightToLeft == RightToLeft.Yes)
ttt.uFlags |= 4;
Marshal.StructureToPtr(ttt, m.LParam, false);
return; // do not do normal processing
}
}
break;
Obviously, this is C#, not VB, but you get the idea.
Assuming .NET 2.0 or later, you can also set ListView.ShowItemToolTips to true
. If you need to customize the tooltip text for a given item, set ListViewItem.ToolTipText to the string you want displayed.
If you set ShowItemTooltips for the ListView control in "details" mode and do nothing else, the ListView control will automatically provide tooltips for items and subitems that exceed their column's widths. This turns out to work even if the FullRowSelect property is set to true. If ToolTipText has been set for a ListViewItem and FullRowSelect is true, then the tooltip will appear for the whole row; that's the case where tooltips won't be displayed for subitems.
You can use the MouseMove
event:
private void listview1_MouseMove(object sender, MouseEventargs e)
{
ListViewItem item = listview1.GetItemAt(e.X, e.Y);
ListViewHitTestInfo info = listview1.HitTest(e.X, e.Y);
if((item != null) && (info.SubItem != null))
{
toolTip1.SetToolTip(listview1, info.SubItem.Text);
}
else
{
toolTip1.SetToolTip(listview1, "");
}
}
The original code in the question does not work since it creates a New ToolTip
inside OnMouseMove
. I guess that the ToolTip.Show
method is asynchronous and thus the function exits immediately after invoking it, destroying the temporary ToolTip
. When Show
gets to execute, the object does not exist anymore.
The solution would be to create a persistent ToolTip
object, by:
ToolTip
control on the form; orToolTip
class field (disposed in the Finalize
or Dispose
method of the class); orStatic
object inside the function.Also, there is no need to GetItemAt()
since ListViewHitTestInfo
already contains both the item and subitem references.
Improving Colin's answer, here is my code:
Private Sub ListView_MouseMove(sender As Object, e As MouseEventArgs) _
Handles MyList1.MouseMove
Static prevMousePos As Point = New Point(-1, -1)
Dim lv As ListView = TryCast(sender, ListView)
If lv Is Nothing Then _
Exit Sub
If prevMousePos = MousePosition Then _
Exit Sub ' to avoid annoying flickering
With lv.HitTest(lv.PointToClient(MousePosition))
If .SubItem IsNot Nothing AndAlso Not String.IsNullOrEmpty(.SubItem.Text) Then
'AndAlso .Item.SubItems.IndexOf(.SubItem) = 1
'...when a specific Column is needed
Static t As ToolTip = toolTip1 ' using a form's control
'Static t As New ToolTip() ' using a private variable
t.ShowAlways = True
t.UseFading = True
' To display at exact mouse position:
t.Show(.SubItem.Tag, .Item.ListView, _
.Item.ListView.PointToClient(MousePosition), 2000)
' To display beneath the list subitem:
t.Show(.SubItem.Tag, .Item.ListView, _
.SubItem.Bounds.Location + New Size(7, .SubItem.Bounds.Height + 1), 2000)
' To display beneath mouse cursor, as Windows does:
' (size is hardcoded in ugly manner because there is no easy way to find it)
t.Show(.SubItem.Tag, .Item.ListView, _
.Item.ListView.PointToClient(Cursor.Position + New Size(1, 20)), 2000)
End If
prevMousePos = MousePosition
End With
End Sub
I've made the code as general as possible so that the function could be assigned to multiple ListView
s.