Come posso impostare un testo Tooltip diverso per ogni elemento in una listbox?

Ho una listbox che è un database per una collezione di oggetti. La listbox è configurata per visualizzare una proprietà identificatore di ciascun object. Vorrei mostrare un suggerimento con informazioni specifiche per l’elemento all’interno della casella di riepilogo che viene spostato sopra piuttosto che un suggerimento per la listbox nel suo complesso.

Sto lavorando all’interno di WinForms e grazie ad alcuni utili post sul blog abbiamo messo insieme una soluzione carina, che volevo condividere.

Sarei interessato a vedere se ci sono altre soluzioni eleganti a questo problema, o come questo possa essere fatto in WPF.

Esistono due sotto-problemi principali che è necessario risolvere per risolvere questo problema:

  1. Determina su quale object si passa il mouse
  2. Scarica l’evento MouseHover quando l’utente ha passato il mouse su un elemento, quindi sposta il cursore all’interno della casella di riepilogo e passa con il mouse su un altro elemento.

Il primo problema è piuttosto semplice da risolvere. Chiamando un metodo come il seguente all’interno del gestore per MouseHover, puoi determinare su quale object si passa il mouse sopra:

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; } 

Quindi utilizzare il valore restituito per impostare il suggerimento secondo necessità.

Il secondo problema è che normalmente l’evento MouseHover non viene generato nuovamente finché il cursore non ha lasciato l’area client del controllo e quindi torna indietro.

È ansible aggirare questo avvolgendo la chiamata Win32API TrackMouseEvent. Nel codice seguente, il metodo ResetMouseHover esegue il wrapping della chiamata API per ottenere l’effetto desiderato: reimpostare il timer sottostante che controlla quando viene triggersto l’evento hover.

 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); } 

}

Con il wrapper in atto, puoi semplicemente chiamare ResetMouseHover (listBox.Handle) alla fine del tuo gestore MouseHover e l’evento hover si triggers nuovamente anche quando il cursore rimane entro i limiti del controllo.

Sono sicuro che questo approccio, che attacca tutto il codice nel gestore MouseHover, deve comportare un numero maggiore di eventi di MouseHover che si triggersno rispetto a quelli realmente necessari, ma il lavoro verrà completato. Qualsiasi miglioramento è più che benvenuto.

Utilizzando l’evento MouseMove, puoi tenere traccia dell’indice dell’elemento su cui si trova il mouse e memorizzarlo in una variabile che mantiene il suo valore tra MouseMoves. Ogni volta che viene triggersto MouseMove, controlla se l’indice è cambiato. In tal caso, disabilita il suggerimento, cambia il testo del tooltip per questo controllo, quindi lo ritriggers.

Di seguito è riportato un esempio in cui una singola proprietà di una class Car viene mostrata in un controllo ListBox, ma successivamente vengono visualizzate le informazioni complete quando si passa con il mouse su una riga qualsiasi. Per far funzionare questo esempio, tutto ciò che serve è un ListBox chiamato lstCars con un evento MouseMove e un componente di testo ToolTip chiamato tt1 sul tuo WinForm.

Definizione della class dell’auto:

  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); } } } 

Evento di caricamento del modulo:

  private void Form1_Load(object sender, System.EventArgs e) { // Set up a list of cars: List allCars = new List(); 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"; } 

Il codice del suggerimento (compresa la creazione della variabile di livello di class chiamata 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 time 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; } } } 

Penso che l’opzione migliore, dal momento che la tua associazione al database di oggetti, sarebbe di utilizzare un datatemplate. Quindi potresti fare qualcosa del genere:

        

Ovviamente si sostituirà il binding di ItemsSource con qualunque sia l’origine di binding e il percorso di binding parte con qualsiasi proprietà pubblica degli oggetti nell’elenco che si desidera effettivamente visualizzare. Maggiori dettagli disponibili su msdn

Ecco uno stile che crea un gruppo di RadioButton usando un ListBox. Tutto è destinato a MVVM-ing. MyClass contiene due proprietà String: MyName e MyToolTip. Questo mostrerà l’elenco di RadioButton incluso il corretto ToolTip-ing. Di interesse per questo thread è il Setter per ToolTip in basso, rendendo questa una soluzione tutta Xaml.

Esempio di utilizzo:

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

Stile:

      

Usando l’attributo title, possiamo impostare il tool tip per ogni elemento della lista in una lista.

Loop questo per tutti gli elementi in una casella di riepilogo.

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

Spero che questo ti aiuti.

Usando onmouseover puoi scorrere ogni voce dell’elenco e mostrare la 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); } } } } 

È ansible utilizzare questo semplice codice che utilizza l’evento onMouseMove di ListBox 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); } } 

Ovviamente dovrete avviare l'object ToolTip nel costruttore o qualche funzione init:

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

Godere!