2010-02-18 13 views
23

Tengo un gran ListBox con desplazamiento vertical habilitado, mi MVVM tiene New y Edit ICommands. Estoy agregando un nuevo elemento al final de la colección, pero quiero que la barra de desplazamiento también se posicione automáticamente al final cuando llamo a mi MVVM-AddCommand. También estoy haciendo un elemento editable (llamando a EditCommand con un elemento de fila particular) desde alguna otra parte de la aplicación para que mi ListBoxItem entre en el modo de edición usando DataTrigger, pero ¿cómo voy a traer esa fila en particular (ListBoxItem) al ver ajustando la posición de desplazamiento.Cómo controlar la posición de desplazamiento de un ListBox en una aplicación MVVM WPF

Si lo estoy haciendo en el lado de Vista, puedo llamar a listBox.ScrollInToView (lstBoxItem). Pero, ¿cuál es la mejor manera de resolver este problema de desplazamiento común desde una perspectiva de MVVM.

+0

El uso del evento ListBox SelectionChanged y el método ScrollIntoView no rompe MVVM. Esto es completamente una funcionalidad de vista y debe ser manejado por la vista. El modelo de vista ni siquiera debería saber que ListBox existe o tiene control sobre dónde se encuentra un objeto en la vista. Lo único que debe hacer el modelo de vista es cambiar el elemento seleccionado, que debe ser una propiedad vinculada de ListBox al modelo de vista. – Tim

Respuesta

27

Normalmente establezco IsSynchronizedWithCurrentItem="True" en el ListBox. Luego añadir un controlador de SelectionChanged y siempre llevar el elemento seleccionado a la vista, con el código de la siguiente manera:

private void BringSelectionIntoView(object sender, SelectionChangedEventArgs e) 
    { 
     Selector selector = sender as Selector; 
     if (selector is ListBox) 
     { 
      (selector as ListBox).ScrollIntoView(selector.SelectedItem); 
     } 
    } 

Desde mi VM puedo obtener la vista colección predeterminada y utilizar uno de los métodos MoveCurrent*() para asegurar que el elemento que se editado es el elemento actual.

CollectionViewSource.GetDefaultView(_myCollection).MoveCurrentTo(thisItem); 

NOTA: Editado utilizar ListBox.ScrollIntoView() para dar cabida a la virtualización

+0

Sí, mi ListView está virtualizado, gracias por la respuesta Dr. –

7

El uso de este en MVVM se puede lograr fácilmente a través de un comportamiento adjunto de este modo:

using System.Windows.Controls; 
using System.Windows.Interactivity; 

namespace Jarloo.Sojurn.Behaviors 
{ 
    public sealed class ScrollIntoViewBehavior : Behavior<ListBox> 
    { 
     protected override void OnAttached() 
     { 
      base.OnAttached(); 
      AssociatedObject.SelectionChanged += ScrollIntoView; 
     } 

     protected override void OnDetaching() 
     { 
      AssociatedObject.SelectionChanged -= ScrollIntoView; 
      base.OnDetaching(); 
     } 

     private void ScrollIntoView(object o, SelectionChangedEventArgs e) 
     { 
      ListBox b = (ListBox) o; 
      if (b == null) 
       return; 
      if (b.SelectedItem == null) 
       return; 

      ListBoxItem item = (ListBoxItem) ((ListBox) o).ItemContainerGenerator.ContainerFromItem(((ListBox) o).SelectedItem); 
      if (item != null) item.BringIntoView(); 
     } 
    } 
} 

A continuación, en la vista de anuncio esta referencia en la parte superior:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

Y haga esto:

<ListBox ItemsSource="{Binding MyData}" SelectedItem="{Binding MySelectedItem}"> 
     <i:Interaction.Behaviors> 
      <behaviors:ScrollIntoViewBehavior /> 
     </i:Interaction.Behaviors> 
</ListBox> 

Ahora cuando SelectedItem cambia el comportamiento, hará la llamada BringIntoView() por usted.

1

Si el código anterior no funciona para usted, le daría prueba

public class ListBoxExtenders : DependencyObject 
{ 
    public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); 

    public static bool GetAutoScrollToCurrentItem(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(AutoScrollToSelectedItemProperty); 
    } 

    public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) 
    { 
     obj.SetValue(AutoScrollToSelectedItemProperty, value); 
    } 

    public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) 
    { 
     var listBox = s as ListBox; 
     if (listBox != null) 
     { 
      var listBoxItems = listBox.Items; 
      if (listBoxItems != null) 
      { 
       var newValue = (bool)e.NewValue; 

       var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition)); 

       if (newValue) 
        listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker; 
       else 
        listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker; 
      } 
     } 
    } 

    public static void OnAutoScrollToCurrentItem(ListBox listBox, int index) 
    { 
     if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0) 
      listBox.ScrollIntoView(listBox.Items[index]); 
    } 

} 

uso en XAML

<ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../> 
3

Esta es la forma de propiedad adjunta la respuesta aceptada:

using System.Windows; 
using System.Windows.Controls; 

namespace CommonBehaviors 
{ 
    public static class ScrollCurrentItemIntoViewBehavior 
    { 
     public static readonly DependencyProperty AutoScrollToCurrentItemProperty = 
      DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", 
       typeof(bool), typeof(ScrollCurrentItemIntoViewBehavior), 
       new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); 

     public static bool GetAutoScrollToCurrentItem(DependencyObject obj) 
     { 
      return (bool)obj.GetValue(AutoScrollToCurrentItemProperty); 
     } 

     public static void OnAutoScrollToCurrentItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
     { 
      var listBox = obj as ListBox; 
      if (listBox == null) return; 

      var newValue = (bool)e.NewValue; 
      if (newValue) 
       listBox.SelectionChanged += listBoxSelectionChanged; 
      else 
       listBox.SelectionChanged -= listBoxSelectionChanged; 
     } 

     static void listBoxSelectionChanged(object sender, SelectionChangedEventArgs e) 
     { 
      var listBox = sender as ListBox; 
      if (listBox == null || listBox.SelectedItem == null || listBox.Items == null) return; 

      listBox.Items.MoveCurrentTo(listBox.SelectedItem); 
      listBox.ScrollIntoView(listBox.SelectedItem); 
     } 

     public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) 
     { 
      obj.SetValue(AutoScrollToCurrentItemProperty, value); 
     } 
    } 
} 

Uso:

<ListBox ItemsSource="{Binding}" 
      IsSynchronizedWithCurrentItem="True" 
      behaviors:ScrollCurrentItemIntoViewBehavior.AutoScrollToCurrentItem="True"> 
Cuestiones relacionadas