2009-07-11 9 views
2

J'essaie d'implémenter le modèle MVVM dans WPF. J'ai suivi le Very simple MVVM demo application de Jeremy Alles. J'ai un ListBox qui a une liaison à un ObservableCollection:La barre de défilement Listbox ne suit pas l'élément sélectionné (avec ICollectionView)

<ListBox 
    Name="myListBox" 
    IsSynchronizedWithCurrentItem="True" 
    ItemsSource="{Binding Persons}"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <views:PersonsView /> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

J'ai ajouté une ICollectionView pour gérer l'élément sélectionné sur la zone de liste. Cela me permet également d'avoir deux boutons qui me permettent de sélectionner les éléments précédents et suivants dans la liste.

private void GoToPrevious() 
{ 
    this.collectionView.MoveCurrentToPrevious(); 
} 
private void GoToNext() 
{ 
    this.collectionView.MoveCurrentToNext(); 
} 

Tout fonctionne très bien, cependant, lorsque l'élément sélectionné est en dessous de la zone affichée de la zone de liste, la barre de défilement de la zone de liste ne se déplace pas en conséquence.

Comment synchroniser la barre de défilement/la zone d'affichage de la ListBox avec l'élément sélectionné?

Répondre

6

J'ai trouvé la réponse. je devais utiliser

myListBoxItem.BringIntoView(); 

Le problème était que je ne voulais ajouter un code-behind, comme je suis MVVM la mise en œuvre. La solution utilise les comportements attachés. Josh Smith a un excellent article à ce sujet: Introduction to Attached Behaviors in WPF.

J'adedd un Setter au style des éléments dans la liste:

<ListBox.ItemContainerStyle> 
    <Style TargetType="{x:Type ListBoxItem}"> 
     <Setter 
      Property="custom:ListBoxItemBehavior.IsBroughtIntoViewWhenSelected" 
      Value="True" /> 
    </Style> 
</ListBox.ItemContainerStyle> 

et a ajouté la classe suivante (seulement changé TreeView de l'article de Josh à ListBox):

public static class ListBoxItemBehavior 
{ 
    #region IsBroughtIntoViewWhenSelected 

    public static bool GetIsBroughtIntoViewWhenSelected(ListBoxItem listBoxItem) 
    { 
     return (bool)listBoxItem.GetValue(IsBroughtIntoViewWhenSelectedProperty); 
    } 

    public static void SetIsBroughtIntoViewWhenSelected(
     ListBoxItem listBoxItem, bool value) 
    { 
     listBoxItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value); 
    } 

    public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty = 
     DependencyProperty.RegisterAttached(
     "IsBroughtIntoViewWhenSelected", 
     typeof(bool), 
     typeof(ListBoxItemBehavior), 
     new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged)); 

    static void OnIsBroughtIntoViewWhenSelectedChanged(
     DependencyObject depObj, DependencyPropertyChangedEventArgs e) 
    { 
     ListBoxItem item = depObj as ListBoxItem; 
     if (item == null) 
      return; 

     if (e.NewValue is bool == false) 
      return; 

     if ((bool)e.NewValue) 
      item.Selected += OnListBoxItemSelected; 
     else 
      item.Selected -= OnListBoxItemSelected; 
    } 

    static void OnListBoxItemSelected(object sender, RoutedEventArgs e) 
    { 
     // Only react to the Selected event raised by the ListBoxItem 
     // whose IsSelected property was modified. Ignore all ancestors 
     // who are merely reporting that a descendant's Selected fired. 
     if (!Object.ReferenceEquals(sender, e.OriginalSource)) 
      return; 

     ListBoxItem item = e.OriginalSource as ListBoxItem; 
     if (item != null) 
      item.BringIntoView(); 
    } 

    #endregion // IsBroughtIntoViewWhenSelected 
} 

Il fonctionne !!

Questions connexes