How can I set different hint texts for each item in the list?

I have a list associated with binding to a collection of objects. The list of lists is configured to display the identifier properties for each object. I would like to show a tooltip with information specific to an item in a list that hovers over, rather than a single tooltip for the list as a whole.

I work in WinForms, and thanks to some useful blog posts, a pretty nice solution has been put together that I would like to share.

I would be interested to see if there are other elegant solutions to this problem or how this can be done in WPF.

+22
c # winapi winforms listbox
Oct 10 '08 at 18:37
source share
7 answers

To solve this problem, two main problems must be solved:

  • Determine which item is above
  • Kill the MouseHover event when the user hovers over one element, then moves the cursor to the list and hovers over another element.

The first problem is pretty simple. By calling a method similar to the following in your handler for MouseHover, you can determine which element is above it:

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 return value to set the prompt as needed.

The second problem is that usually the MouseHover event does not fire again until the cursor leaves the client area of ​​the control and then returns.

You can get around this by wrapping up the TrackMouseEvent Win32API call. In the following code, the ResetMouseHover method wraps the API call to get the desired effect: reset the base 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 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 an in-place wrapper, 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 remains within control.

I’m sure that this approach, sticking all the code in the MouseHover handler, should lead to faster MouseHover events than it really needs, but it will do the job. Any improvements are more than welcome.

+19
Oct 10 '08 at 18:58
source share

Using the MouseMove event, you can track the index of the element that is above the mouse and store it in a variable that stores its value between MouseMoves. Each time MouseMove starts up, it checks to see if the index has changed. If so, it disables the tooltip, changes the tooltip text for this control, and then reactivates it.

The following is an example where one property of the Car class is displayed in a ListBox, but then full information is displayed when it hangs over any one row. For this example to work, all you need is a ListBox called lstCars, with the MouseMove event and a TextTip component called tt1 on your WinForm.

Vehicle Class Definition:

  class Car { // Main properties: public string Model { get; set; } public string Make { get; set; } public int InsuranceGroup { get; set; } public string OwnerName { get; set; } // Read only property combining all the other informaiton: public string Info { get { return string.Format("{0} {1}\nOwner: {2}\nInsurance group: {3}", Make, Model, OwnerName, InsuranceGroup); } } } 

Form load event:

  private void Form1_Load(object sender, System.EventArgs e) { // Set up a list of cars: List<Car> allCars = new List<Car>(); allCars.Add(new Car { Make = "Toyota", Model = "Yaris", InsuranceGroup = 6, OwnerName = "Joe Bloggs" }); allCars.Add(new Car { Make = "Mercedes", Model = "AMG", InsuranceGroup = 50, OwnerName = "Mr Rich" }); allCars.Add(new Car { Make = "Ford", Model = "Escort", InsuranceGroup = 10, OwnerName = "Fred Normal" }); // Attach the list of cars to the ListBox: lstCars.DataSource = allCars; lstCars.DisplayMember = "Model"; } 

Tooltip code (including creating a class level variable called hoveredIndex):

  // Class variable to keep track of which row is currently selected: int hoveredIndex = -1; private void lstCars_MouseMove(object sender, MouseEventArgs e) { // See which row is currently under the mouse: int newHoveredIndex = lstCars.IndexFromPoint(e.Location); // If the row has changed since last moving the mouse: if (hoveredIndex != newHoveredIndex) { // Change the variable for the next timw we move the mouse: hoveredIndex = newHoveredIndex; // If over a row showing data (rather than blank space): if (hoveredIndex > -1) { //Set tooltip text for the row now under the mouse: tt1.Active = false; tt1.SetToolTip(lstCars, ((Car)lstCars.Items[hoveredIndex]).Info); tt1.Active = true; } } } 
+13
Jul 27 '11 at 17:14
source share

I think the best option, since your binding of your list to objects will use a sample of data. So you can do something like this:

 <ListBox Width="400" Margin="10" ItemsSource="{Binding Source={StaticResource myTodoList}}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Path=TaskName}" ToolTipService.ToolTip="{Binding Path=TaskName}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> 

Of course, you would replace the ItemsSource binding, whatever your binding source is, and binding the Path parts to any public property of the objects in the list that you really want to display. More information is available at msdn.

+7
May 12, '09 at 15:29
source share

Using the title attribute, we can set a tooltip for each item in the list in the list.

Complete this for all items in the list.

 ListItem li = new ListItem("text","key"); li.Attributes.Add("title","tool tip text"); 

Hope this helps.

0
Aug 14 '09 at 4:00
source share

Here is the style that the RadioButtons group creates using the ListBox. Everything is connected with MVVM-ing. MyClass contains two String properties: MyName and MyToolTip. This will display a list of RadioButtons, including the correct tooltip. Interest in this topic is the Setter for ToolTip next to the bottom, which makes this the whole Xaml solution.

Usage example:

ListBox Style = "{StaticResource radioListBox}" ItemsSource = "{Binding MyClass}" SelectedValue = "{Binding SelectedMyClass}" / ">

Style:

  <Style x:Key="radioListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}"> <Setter Property="BorderThickness" Value="0" /> <Setter Property="Margin" Value="5" /> <Setter Property="Background" Value="{x:Null}" /> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <Grid Background="Transparent"> <RadioButton Focusable="False" IsHitTestVisible="False" IsChecked="{TemplateBinding IsSelected}" Content="{Binding MyName}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="ToolTip" Value="{Binding MyToolTip}" /> </Style> </Setter.Value> </Setter> </Style> 
0
Jun 12 '10 at 15:13
source share

Using onmouseover , you can iterate over each item in the list and display ToolTip

 onmouseover="doTooltipProd(event,''); function doTooltipProd(e,tipObj) { Tooltip.init(); if ( typeof Tooltip == "undefined" || !Tooltip.ready ) { return; } mCounter = 1; for (m=1;m<=document.getElementById('lobProductId').length;m++) { var mCurrent = document.getElementById('lobProductId').options[m]; if(mCurrent != null && mCurrent != "null") { if (mCurrent.selected) { mText = mCurrent.text; Tooltip.show(e, mText); } } } } 
0
Nov 12 '14 at 7:06
source share

You can use this simple code that uses the onMouseMove ListBox event in WinForms:

 private void ListBoxOnMouseMove(object sender, MouseEventArgs mouseEventArgs) { var listbox = sender as ListBox; if (listbox == null) return; // set tool tip for listbox var strTip = string.Empty; var index = listbox.IndexFromPoint(mouseEventArgs.Location); if ((index >= 0) && (index < listbox.Items.Count)) strTip = listbox.Items[index].ToString(); if (_toolTip.GetToolTip(listbox) != strTip) { _toolTip.SetToolTip(listbox, strTip); } } 

Of course, you will need to initialize the ToolTip object in the constructor or some init function:

 _toolTip = new ToolTip { AutoPopDelay = 5000, InitialDelay = 1000, ReshowDelay = 500, ShowAlways = true }; 

Enjoy it!

0
Dec 19 '17 at 11:58 on
source share



All Articles