2009-01-27 11 views

Respuesta

9

Este código de ejemplo debería ser de ayuda. He hecho esto muchas veces con un TextBox, pero me tomó un tiempo encontrarlo en un ListBox

Obviamente, es solo un Formulario con un Botón y un ListBox. Modificar para adaptarse a sus necesidades:

private void button1_Click(object sender, EventArgs e) 
{ 
    listBox1.Items.Add("Some Text " + listBox1.Items.Count.ToString()); 

    //The max number of items that the listbox can display at a time 
    int NumberOfItems = listBox1.ClientSize.Height/listBox1.ItemHeight; 

    if (listBox1.TopIndex == listBox1.Items.Count - NumberOfItems - 1) 
    { 
     //The item at the top when you can just see the bottom item 
     listBox1.TopIndex = listBox1.Items.Count - NumberOfItems + 1; 
    } 
} 
+0

que añade buttonMultipleAdds_Click para el caso de tener que añadir/eliminar cualquier número de líneas. – huha

0

He resuelto este problema utilizando un método similar al colithium, excepto que luego se dio cuenta que había un error con actualizaciones simultáneas. Entonces, hay un miembro de la clase llamado m_previousCount que almacena el número de elementos en el ListBox antes de que ocurriera esta actualización.

Lo hice con un ListView, pero debería funcionar igual para un ListBox.

En este caso, mi listView1 está vinculada al contenido de listViewModel1.Entries.

private void EventMessageViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
{ 
    listView1.Dispatcher.BeginInvoke(DispatcherPriority.Background, new ScrollToLastItemDelegate(ScrollToLastItem)); 
} 

/// <summary> 
/// Scroll to last item, unless you have scrolled up the list 
/// </summary> 
private void ScrollToLastItem() 
{ 
    // Get scrollviewer 
    Decorator border = System.Windows.Media.VisualTreeHelper.GetChild(listView1, 0) as Decorator; 
    ScrollViewer scrollViewer = border.Child as ScrollViewer; 

    double vo = scrollViewer.VerticalOffset; 

    // assume they are all the same height 
    ListBoxItem lbi = listView1.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem; 

    //The max number of items that the listbox can display at a time 
    double NumberOfItemsOnScreen = listView1.ActualHeight/lbi.ActualHeight; 

    // use previousCount in case we get multiple updates in one go 
    if (m_previousCount > NumberOfItemsOnScreen) // scrollbar should be active 
    { 
     if (vo < (m_previousCount - NumberOfItemsOnScreen)) // you're not at the bottom 
     { 
      return; // don't scroll to the last item 
     } 
    } 

    m_previousCount = listView1.Items.Count; 

    // scroll to the last item 
    listView1.SelectedItem = listView1.Items.GetItemAt(listViewModel1.Entries.Count - 1); 

    listView1.ScrollIntoView(listView1.SelectedItem); 
} 
1

Se me ocurrió la siguiente solución que también funcionará para las cajas de lista dibujadas por el propietario con elementos de altura variable.

La idea básica es que se dispare si se desplaza hacia abajo utilizando el método IndexToPoint y un punto en la parte inferior del cuadro de lista para ver si el último elemento está en esa posición. Esto tiene un pequeño defecto en que si el último elemento está en la parte inferior pero no es completamente visible, seguirá creyendo que está desplazado hacia abajo.

Utiliza la propiedad TopIndex para desplazarse por el cuadro de lista. Tenga en cuenta que al configurar TopIndex para que sea el último elemento en el cuadro de lista, no lo colocará en la parte superior si hay espacio suficiente para otros elementos. En ese caso, lo pondrá en la parte inferior, que es lo que quieres.

También tiene un código adicional para mantener el número de elementos en la lista a un monto máximo (definido en otra parte por la constante MAX_LISTBOX_ITEMS) eliminando el superior después de que se llene. Cuando lo hace, funciona donde tendrá que desplazarse por la lista para mantener los mismos elementos que se muestran incluso después de eliminar uno. Si no te importa controlar la cantidad de elementos agregados al cuadro de lista, deberías poder eliminar la cláusula if media del código y cualquier mención de la variable scrollToIndex.

private void AddItemToListBox(ListBox listBox, object newItem) 
    { 
     int scrollToIndex = -1; 
     bool scrollToEnd = false; 

     //Work out if we should scroll to the end after adding a new item 
     int lastIndex = listBox.IndexFromPoint(4, listBox.ClientSize.Height - 4); 
     if ((lastIndex < 0) || (lastIndex == listBox.Items.Count - 1)) 
     { 
      scrollToEnd = true; 
     } 

     //To prevent listbox jumping about as we delete and scroll 
     listBox.BeginUpdate(); 

     //Work out if we have too many items and have to delete 
     if (listBox.Items.Count >= MAX_LISTBOX_ITEMS) 
     { 
      //If deleting an item, and not scrolling to the end when new item added anyway, 
      //then we will need to scroll to keep in the same place, work out new scroll index 
      if (!scrollToEnd) 
      { 
       scrollToIndex = listBox.TopIndex - 1; 
       if (scrollToIndex < 0) 
       { 
        scrollToIndex = 0; 
       } 
      } 

      //Remove top item 
      listBox.Items.Remove(listBox.Items[0]); 
     } 

     //Add new item 
     listBox.Items.Add(newItem); 

     //Scroll if necessary 
     if (scrollToEnd) 
     { 
      listBox.TopIndex = listBox.Items.Count - 1; 
     } 
     else if (scrollToIndex >= 0) 
     { 
      listBox.TopIndex = scrollToIndex; 
     } 

     listBox.EndUpdate(); 
    } 
10

Puede a modo de prueba:

listBox1.SelectedIndex = listBox1.Items.Count - 1;  
listBox1.SelectedIndex = -1; 
+1

Esta es obviamente la mejor solución. –

+0

Si bien esto no responde perfectamente la pregunta, que requiere que el comportamiento de desplazamiento ocurra "solo si la barra de desplazamiento está en la parte inferior antes de agregar el elemento", esta es una excelente solución y me ayudó a lograr lo que estaba buscando. ¡Gracias! –

Cuestiones relacionadas