2010-02-18 15 views
23

Ho un grande ListBox con lo scorrimento verticale abilitato, il mio MVVM ha Nuovo e Modifica ICommands. Sto aggiungendo un nuovo elemento alla fine della raccolta ma voglio anche che la barra di scorrimento si posizioni automaticamente fino alla fine quando chiamo MVVM-AddCommand. Sto anche rendendo un elemento modificabile (chiamando EditCommand con una particolare riga) da qualche altra parte dell'applicazione in modo che il mio ListBoxItem entri in modalità di modifica usando DataTrigger, ma come posso portare quella particolare riga (ListBoxItem) al vista regolando la posizione di scorrimento.Come controllare la posizione di scorrimento di un ListBox in un'app MVVM WPF

Se lo sto facendo nel lato Vista posso chiamare listBox.ScrollInToView (lstBoxItem). Ma qual è il modo migliore per risolvere questo problema di Scroll comune da una prospettiva MVVM.

+0

L'utilizzo dell'evento ListBox SelectionChanged e del metodo ScrollIntoView non interrompe MVVM. Questa è interamente vista funzionalità e dovrebbe essere gestita dalla vista. Il modello di visualizzazione non dovrebbe nemmeno sapere che il ListBox esiste o ha il controllo su dove si trova un oggetto nella vista. L'unica cosa che il modello di visualizzazione dovrebbe fare è modificare l'oggetto SelectedItem, che dovrebbe essere una proprietà associata di ListBox al modello di vista. – Tim

risposta

27

Generalmente ho impostato IsSynchronizedWithCurrentItem="True" su ListBox. Poi aggiungo un gestore SelectionChanged e sempre portare l'elemento selezionato in vista, con il codice come questo:

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

Dal mio VM posso ottenere la vista raccolta predefinita e utilizzare uno dei MoveCurrent*() metodi per assicurare che la voce di essere modificato è l'elemento corrente.

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

NOTA: A cura di utilizzare ListBox.ScrollIntoView() per accogliere la virtualizzazione

+0

Sì il mio ListView è virtualizzato, grazie per la risposta Dr. –

7

Usando questo in MVVM può essere facilmente realizzata mediante un comportamento attaccato in questo 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(); 
     } 
    } 
} 

Poi nel View annuncio questo riferimento a la parte superiore:

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

E fare questo:

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

Ora, quando l'oggetto SelectedItem cambia, il comportamento farà la chiamata a BringIntoView().

1

Se il codice di cui sopra non funziona per voi, dare una prova

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

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

Questa è la forma di proprietà allegato della risposta accettata:

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

Utilizzo:

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