2011-09-09 10 views
10

wersja skróconaListBox ScrollIntoView podczas korzystania CollectionViewSource z GroupDescriptions (tj IsGrouping == true)

chciałbym przewinąć element ListBox polu widzenia, gdy wybór jest zmieniany.

Długa wersja

Mam ListBox z ItemsSource związanego z CollectionViewSource z GroupDescription, jak na poniższym przykładzie.

<Window.Resources> 
    <CollectionViewSource x:Key="AnimalsView" Source="{Binding Source={StaticResource Animals}, Path=AnimalList}"> 
     <CollectionViewSource.GroupDescriptions> 
      <PropertyGroupDescription PropertyName="Category"/> 
     </CollectionViewSource.GroupDescriptions> 
    </CollectionViewSource> 
</Window.Resources> 

<ListBox x:Name="AnimalsListBox"ItemsSource="{Binding Source={StaticResource AnimalsView}}" ItemTemplate="{StaticResource AnimalTemplate}" SelectionChanged="ListBox_SelectionChanged"> 
    <ListBox.GroupStyle> 
     <GroupStyle HeaderTemplate="{StaticResource CategoryTemplate}" /> 
    </ListBox.GroupStyle> 
</ListBox> 

Istnieje zdarzenie SelectionChanged w pliku z kodem.

public List<Animal> Animals { get; set; } 

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    ListBox control = (ListBox)sender; 
    control.ScrollIntoView(control.SelectedItem); 
} 

Teraz. Jeśli ustawię AnimalsListBox.SelectedItem na element, który obecnie nie jest widoczny, chciałbym, aby był przewinięty w widoku. W tym przypadku robi się to trudne, ponieważjest grupami (właściwość IsGrouped to true), a niepowodzenie wywołania ScrollIntoView.

System.Windows.Controls.ListBox przez Reflektor. Uwaga: base.IsGrouping w OnBringItemIntoView.

public void ScrollIntoView(object item) 
{ 
    if (base.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
    { 
     this.OnBringItemIntoView(item); 
    } 
    else 
    { 
     base.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnBringItemIntoView), item); 
    } 
} 

private object OnBringItemIntoView(object arg) 
{ 
    FrameworkElement element = base.ItemContainerGenerator.ContainerFromItem(arg) as FrameworkElement; 
    if (element != null) 
    { 
     element.BringIntoView(); 
    } 
    else if (!base.IsGrouping && base.Items.Contains(arg)) 
    { 
     VirtualizingPanel itemsHost = base.ItemsHost as VirtualizingPanel; 
     if (itemsHost != null) 
     { 
      itemsHost.BringIndexIntoView(base.Items.IndexOf(arg)); 
     } 
    } 
    return null; 
} 

Pytania

  1. Może ktoś wyjaśnić, dlaczego to robi nie pracę przy użyciuzgrupowanie?
    • Zawsze zwraca zawsze zwraca null, mimo że jego stan informuje o wygenerowaniu wszystkich kontenerów.
  2. Jak mogę uzyskać przewijanie widoku, gdy przy użyciu grupy?

Odpowiedz

8

Znalazłem rozwiązanie mojego problemu. Byłem pewien, że nie byłem pierwszą osobą, która trafiła w ten problem, więc nadal szukałem rozwiązań StackOverflow i natknąłem się na tę odpowiedź Davida about how ItemContainerGenerator works with a grouped list.

Rozwiązaniem Davida było opóźnienie dostępu do procesu renderowania ItemContainerGenerator do po.

Mam zaimplementowane to rozwiązanie, z kilkoma zmianami, które szczegółowo opiszę po.

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    ListBox control = (ListBox)sender; 

    if (control.IsGrouping) 
    { 
     if (control.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
       Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView)); 
     else 
       control.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; 
    } 
    else 
     control.ScrollIntoView(control.SelectedItem); 
} 

private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) 
{ 
    if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) 
     return; 

    ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; 
    Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView)); 
} 

private void DelayedBringIntoView() 
{ 
    var item = ItemContainerGenerator.ContainerFromItem(SelectedItem) as ListBoxItem; 
    if (item != null) 
     item.BringIntoView(); 
} 

Zmiany:

  • używa tylko podejście ItemContainerGenerator gdy IsGrouping jest true, inaczej nadal używać domyślnego ScrollIntoView.
  • Sprawdź, czy ItemContainerGenerator jest gotowy, jeśli tak, wywołaj akcję, w przeciwnym razie sprawdź status ItemContainerGenerator, aby go zmienić. Jest to ważne, ponieważ jest gotowe, a wydarzenie StatusChanged nigdy nie zostanie wywołane.
+0

Powinieneś zmienić swoją odpowiedź na poprawną, a nie powyższą. – Valentein

+0

@ Valentein: Zmieniłem zaznaczoną odpowiedź. JEDNAK, jak porady [crazyarabian] (http://stackoverflow.com/a/7375646/73025) pomogły mi w zdiagnozowaniu problemu, dobrze byłoby oddać głos ** obie ** odpowiedzi, jeśli skorzystasz z mojego ostatecznego rozwiązania. – Dennis

+0

Przy użyciu .NET 4.5.1 i MVVM można użyć zachowania, aby to zrobić. Zachowanie działa w obu scenariuszach, ponieważ jest opóźniony. – Kelly

3
  1. wyjęciu z pudełka VirtualizingStackPanel nie obsługuje wirtualizacji pogrupowane widoki zbiórki. Gdy zgrupowana kolekcja jest renderowana w ItemControl, każda grupa jako całość jest pozycją w przeciwieństwie do każdego elementu w kolekcji, co powoduje "szarpanie" przewijania do nagłówka każdej grupy, a nie do każdego elementu.

  2. Prawdopodobnie będziesz musiał przetasować własny VirtualizingStackPanel lub ItemContainerGenerator, aby śledzić pojemniki wyświetlane w grupie. Brzmi to niedorzecznie, ale domyślna wirtualizacja z grupowaniem w WPF jest najmniejsza.

+0

Tak właśnie myślałem, ale miałem nadzieję, że nie będę musiał pisać, że panele wirtualizacyjne mogą być trudne. ... Ponieważ każda grupa jako całość jest pozycją, to właśnie dlatego 'ItemContainerGenerator' zawsze zwraca' null' po przejściu 'SelectedItem'? – Dennis

+1

Wierzę, że tak. Powinieneś sprawdzić blog Bea Stollnitz. Ma wiele dobrych postów dotyczących grupowania i wirtualizacji: http://www.beacosta.com/ – sellmeadog

+0

Dzięki. Czytałem kilka postów Bea Stollnitz na temat WPF i CollectionViewSource i grupowania - faktycznie użyłem jej przykładów grupowania w moim pytaniu. – Dennis

Powiązane problemy