2008-10-10 8 views
22

Tengo un listbox que está vinculado a una colección de objetos. El cuadro de lista está configurado para mostrar una propiedad de identificación de cada objeto. Me gustaría mostrar una información sobre herramientas con información específica del elemento dentro del cuadro de lista que se está pasando por alto en lugar de una información sobre herramientas para el cuadro de lista como un todo.¿Cómo puedo establecer diferentes textos de Información sobre herramientas para cada elemento en un cuadro de lista?

Estoy trabajando dentro de WinForms y gracias a algunas publicaciones de blog útiles reuní una solución bastante buena, que quería compartir.

Me gustaría ver si hay otras soluciones elegantes a este problema, o cómo se puede hacer esto en WPF.

Respuesta

19

Hay dos principales sub-problemas hay que resolver con el fin de resolver este problema:

  1. determinar qué elemento está siendo flotaba sobre
  2. Obtener el evento MouseHover para disparar cuando el usuario se ha mantenido más de una elemento, luego movió el cursor dentro del cuadro de lista y se movió sobre otro elemento.

El primer problema es bastante simple de resolver. Al llamar a un método como el siguiente dentro de su manejador para MouseHover, se puede determinar qué elemento se está flotaba sobre: ​​

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

continuación, utilizar el valor devuelto para ajustar la ventana de información, según sea necesario.

El segundo problema es que normalmente el evento MouseHover no se activa nuevamente hasta que el cursor ha salido del área del control y vuelve.

Puede solucionar esto envolviendo la llamada TrackMouseEvent Win32API. En el siguiente código, el método ResetMouseHover ajusta la llamada API para obtener el efecto deseado: restablecer el temporizador subyacente que controla cuando se activa el evento de desplazamiento.

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 el envoltorio en su lugar, sólo tiene que llamar ResetMouseHover (listBox.Handle) al final de su controlador MouseHover y el evento activable se disparará de nuevo, incluso cuando el cursor se mantiene dentro del control de límites.

Estoy seguro de que este enfoque, pegando todo el código en el manejador MouseHover debe dar como resultado más disparos de eventos MouseHover de los realmente necesarios, pero hará el trabajo. Cualquier mejora es más que bienvenida.

+0

Gracias por la sugerencia de revisión, @reformed. Los revisores lo rechazaron, pero hice ajustes al idioma que encontraste confuso. –

6

Creo que la mejor opción, ya que su databinding your listbox a los objetos, sería usar datatemplate. Por lo que podría hacer algo como esto:

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

Por supuesto que le sustituya el ItemsSource vinculante con cualquiera que sea su origen de enlace es, y las partes ruta de enlace con cualquier propiedad pública de los objetos de la lista que realmente quiere monitor. Más detalles disponibles en msdn

+2

La pregunta estaba dirigida a WinForms, no a WPF. –

0

Usando el atributo de título, podemos establecer la información sobre herramientas para cada elemento de la lista en un cuadro de lista.

Pasa esto por todos los elementos en un cuadro de lista.

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

Espero que esto ayude.

+0

Esto muestra una información sobre herramientas solo cuando se abre el Listbox de selección. Irónicamente, eso funciona bien para mí y he usado esta solución, así que muchas gracias. –

-1

Aquí hay un Estilo que crea un grupo de RadioButtons usando un ListBox. Todo está dirigido a MVVM-ing. MyClass contiene dos propiedades de cadena: MyName y MyToolTip. Esto mostrará la lista de RadioButtons, incluida la información sobre herramientas adecuada. De interés para este hilo es Setter para ToolTip near bottom haciendo de esto una solución Xaml.

uso

Ejemplo:

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

Estilo:

<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> 
+1

La pregunta estaba dirigida a WinForms, no a WPF. –

+4

Cuando publiqué esta pregunta, StackOverflow se describía como una wiki organizada en torno a preguntas. De hecho, tengo en mi pregunta "Me interesaría ver si hay otras soluciones elegantes a este problema, o cómo se puede hacer esto en WPF."No puedo evaluar la exactitud de esta solución de WPF, pero no es necesario rechazarla simplemente porque no es WinForms –

13

Con el evento MouseMove, puede realizar un seguimiento del índice del elemento sobre el que está el mouse y almacenarlo en una variable que mantiene su valor entre MouseMoves. Cada vez que se activa MouseMove, comprueba si el índice ha cambiado. Si es así, deshabilita la información sobre herramientas, cambia el texto de información sobre herramientas para este control y luego lo vuelve a activar.

A continuación se muestra un ejemplo en el que se muestra una propiedad única de una clase de automóvil en un ListBox, pero luego se muestra información completa al pasar el ratón sobre una fila. Para que este ejemplo funcione, todo lo que necesita es un ListBox llamado lstCars con un evento MouseMove y un componente de texto de información sobre herramientas llamado tt1 en su WinForm.

Definición de la categoría de coches:

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

Formulario evento de carga:

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

El código de información sobre herramientas (incluyendo la creación de la variable de nivel de clase llamada 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; 
       } 
      } 
     } 
+0

¿Qué significa" .InsuranceGroup "en su código? No puedo encontrar nada al respecto en Internet, y mi Visual Studio tampoco sabe esta palabra. – theoretisch

+1

El grupo de seguros es solo una propiedad de la clase de automóviles que quería mostrar. En el Reino Unido, el grupo de seguros indica si el costo del seguro será barato o costoso. el coche para asegurar, menor es el número, el más caro para asegurar el número más grande. He ampliado mi explicación anterior para hacer de esto un ejemplo completo. – Michael

+0

funcionó perfecto para mí, pero tal vez falta "ToolTip tt1 = new ToolTip(); "como campo de clase en el código? Tuve que agregarlo. – Falco

0

Uso onmouseover puede recorrer cada elemento de la lista y puede mostrar t que 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

Se puede utilizar este código simple que utiliza el evento onMouseMove del cuadro de lista en Windows Forms:

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

Por supuesto que tendrá que inicializar el objeto de información sobre herramientas en el constructor o alguna función init:

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

disfrutar!

Cuestiones relacionadas