I have a listbox that is databound to a Collection of objects. The listbox is configured to display an identifier property of each object. I would like to show a tooltip with information specific to the item within the listbox that is being hovered over rather than one tooltip for the listbox as a whole.
I am working within WinForms and thanks to some helpful blog posts put together a pretty nice solution, which I wanted to share.
I'd be interested in seeing if there's any other elegant solutions to this problem, or how this may be done in WPF.
Alternatively known as a balloon, help balloon, flyover help, or ScreenTip, a Tooltip is a text description near an object. The tooltip is displayed when the user hovers the mouse cursor over the object.
Add your tooltip by selecting the Tooltip option on the Marks shelf. Navigate to the dashboard where you want the tooltip and place the visual that you created in this blog post over the appropriate textbox. You should now have a textbox with a tooltip that appears when you hover over it.
In Visual Studio, add a ToolTip component to the form. Select the control that will display the ToolTip, or add it to the form. In the Properties window, set the ToolTip on ToolTip1 value to an appropriate string of text.
There are two main sub-problems one must solve in order to solve this problem:
The first problem is rather simple to solve. By calling a method like the following within your handler for MouseHover, you can determine which item is being hovered over:
private ITypeOfObjectsBoundToListBox DetermineHoveredItem() { Point screenPosition = ListBox.MousePosition; Point listBoxClientAreaPosition = listBox.PointToClient(screenPosition); int hoveredIndex = listBox.IndexFromPoint(listBoxClientAreaPosition); if (hoveredIndex != -1) { return listBox.Items[hoveredIndex] as ITypeOfObjectsBoundToListBox; } else { return null; } }
Then use the returned value to set the tool-tip as needed.
The second problem is that normally the MouseHover event isn't fired again until the cursor has left the client area of the control and then come back.
You can get around this by wrapping the TrackMouseEvent
Win32API call.
In the following code, the ResetMouseHover
method wraps the API call to get the desired effect: reset the underlying timer that controls when the hover event is fired.
public static class MouseInput { // TME_HOVER // The caller wants hover notification. Notification is delivered as a // WM_MOUSEHOVER message. If the caller requests hover tracking while // hover tracking is already active, the hover timer will be reset. private const int TME_HOVER = 0x1; private struct TRACKMOUSEEVENT { // Size of the structure - calculated in the constructor public int cbSize; // value that we'll set to specify we want to start over Mouse Hover and get // notification when the hover has happened public int dwFlags; // Handle to what's interested in the event public IntPtr hwndTrack; // How long it takes for a hover to occur public int dwHoverTime; // Setting things up specifically for a simple reset public TRACKMOUSEEVENT(IntPtr hWnd) { this.cbSize = Marshal.SizeOf(typeof(TRACKMOUSEEVENT)); this.hwndTrack = hWnd; this.dwHoverTime = SystemInformation.MouseHoverTime; this.dwFlags = TME_HOVER; } } // Declaration of the Win32API function [DllImport("user32")] private static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack); public static void ResetMouseHover(IntPtr windowTrackingMouseHandle) { // Set up the parameter collection for the API call so that the appropriate // control fires the event TRACKMOUSEEVENT parameterBag = new TRACKMOUSEEVENT(windowTrackingMouseHandle); // The actual API call TrackMouseEvent(ref parameterBag); } }
With the wrapper in place, you can simply call ResetMouseHover(listBox.Handle)
at the end of your MouseHover handler and the hover event will fire again even when the cursor stays within the control's bounds.
I'm sure this approach, sticking all the code in the MouseHover handler must result in more MouseHover events firing than are really necessary, but it'll get the job done. Any improvements are more than welcome.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With