2009-05-13 16 views
11

Mam powiązanie WPF ListBox z ObservableCollection. Gdy elementy zostaną dodane do kolekcji, pozycja przewijania przesuwa się o wielkość dodanych wpisów. Chciałbym móc zachować pozycję przewijania, tak aby pozycje dodawane do listy nie poruszały się. Czy istnieje sposób, aby to osiągnąć?Konserwacja zwiniętej pozycji przewijania ListBox WPF po zmianie kolekcji

+0

Po prostu zrobiłem naprawdę proste wpf do datbinding do listbox i kiedy dodaję przedmioty, które były w widoku pozostały w widoku ... Co jeszcze robisz? – TheCodeMonk

+0

Spróbuj dodać nowe elementy do początku kolekcji. Przedmioty, które przeglądasz, zaczynają się przemieszczać poza obszar wyświetlania, gdy dodawane są nowe elementy. Dodajesz 1 pozycję - rzutnia pozostaje na tej samej pozycji (opartej na indeksie), co oznacza, że ​​1 element wprowadził widok i 1 element opuścił go. – EvAlex

Odpowiedz

0

Nie jestem pewien, czy jest to przesłonięcie, ale wątpię w to.

W razie wątpliwości użyj wiązania ręcznego? : -} Poważnie, domyślne zachowanie wiązania będzie - dobrze - domyślne, więc jeśli potrzebujesz specjalnego zachowania, ręczne wiązanie jest lepszym wyborem. Dzięki ręcznemu powiązaniu można zapisać pozycję przewijania i zresetować ją po dodaniu elementów.

+0

Co prowadzi do oczywistego pytania - jak określić aktualną pozycję przewijania? –

1

Utwórz kolekcjęViewView. Spróbuj tego:

private ObservableCollection<int> m_Values; 
    private CollectionView m_View; 

    private void Bind() 
    { 
     m_Values = new ObservableCollection<int>(); 
     m_View = new CollectionView(m_Values); 
     MyListBox.ItemsSource = m_View; 
    } 

    private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     Debug.WriteLine(m_View.CurrentItem); 
     Debug.WriteLine(m_View.CurrentPosition); 
    } 
+1

Proszę wyjaśnić dalej. Ta odpowiedź nie rozwiązuje problemu - po prostu raportuje aktualny przedmiot i pozycję. Mam ten sam problem, używając ListBox z zestawem ItemsPanel do VirtualizingStackPanel. Problemem, jaki widzę, jest to, że elementy z pola listy nadal się poruszają w miarę dodawania nowych elementów do kolekcji Observable. Chociaż możliwe jest przeniesienie elementów do miejsca, w którym znajdowały się po dodaniu nowego elementu (np. Za pomocą SelectedIndex), powstaje gwałtowny ruch. Potrzebny jest sposób, aby zapobiec zmianie pozycji przewijania w pierwszej kolejności po dodaniu nowych elementów. – CyberMonk

+0

Mam ten sam problem, ale twoje rozwiązanie zakłada, że ​​wybrano coś. Ale co, jeśli użytkownik nie chce niczego wybierać? Po prostu przewiń gdzieś i zostaw to. – EvAlex

0

Czy można kontrolować, kiedy rzeczy są dodawane? Mam na myśli, czy masz jeden lub dwa zdefiniowane punkty, w których dane się zmieniają? Jeśli tak, jednym prostym rozwiązaniem (być może nie eleganckim) byłaby lokalna zmienna int do przechowywania bieżącego obiektu ListBox.SelectedIndex. Tuż przed zmianą danych zapisz selectedIndex na tę zmienną i po dodaniu danych ustaw SelectedIndex listbox na wartość tej zmiennej.

1

Najpierw znajdź bieżącą pozycję według ListBox.SelectedIndex, a następnie użyj ListBox.ScrollIntoView (/ * Bieżący indeks * /). Nawet jeśli nowe pozycje zostaną dodane do listy, twoja obecna pozycja i widok będą takie same.

3

Miałem ten sam problem i jeszcze jedno ograniczenie: użytkownik nie wybiera przedmiotów, a jedynie zwojów. Dlatego metoda ScrollIntoView() jest bezużyteczna. Oto moje rozwiązanie.

Najpierw stworzyłem klasę ScrollPreserver wyprowadzania go z DependencyObject, z dołączonym właściwość zależność PreserveScroll typu bool:

public class ScrollPreserver : DependencyObject 
{ 
    public static readonly DependencyProperty PreserveScrollProperty = 
     DependencyProperty.RegisterAttached("PreserveScroll", 
      typeof(bool), 
      typeof(ScrollPreserver), 
      new PropertyMetadata(new PropertyChangedCallback(OnScrollGroupChanged))); 

    public static bool GetPreserveScroll(DependencyObject invoker) 
    { 
     return (bool)invoker.GetValue(PreserveScrollProperty); 
    } 

    public static void SetPreserveScroll(DependencyObject invoker, bool value) 
    { 
     invoker.SetValue(PreserveScrollProperty, value); 
    } 

    ... 
} 

nieruchomości zmienił zwrotna zakłada, że ​​nieruchomość jest ustalana przez ScrollViewer. Metoda zwrotna dodaje tę ScrollViewer do prywatnego słownika i dodaje ScrollChanged obsługi zdarzeń do niego:

private static Dictionary<ScrollViewer, bool> scrollViewers_States = 
    new Dictionary<ScrollViewer, bool>(); 

private static void OnScrollGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    ScrollViewer scrollViewer = d as ScrollViewer; 
    if (scrollViewer != null && (bool)e.NewValue == true) 
    { 
     if (!scrollViewers_States.ContainsKey(scrollViewer)) 
     { 
      scrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollViewer_ScrollChanged); 
      scrollViewers_States.Add(scrollViewer, false); 
     } 
    } 
} 

Słownik ten będzie posiadać odnośniki do wszystkich ScrollViewers w aplikacji, które korzystają z tej klasy. W moim przypadku przedmioty są dodawane na początku kolekcji. Problem polega na tym, że położenie rzutni się nie zmienia. Jest dobrze, gdy pozycja wynosi 0: pierwszy element w rzutni zawsze będzie pierwszym elementem. Ale gdy pierwszy element w rzutni ma inny indeks niż 0 i dodawane są nowe elementy - wskaźnik tego elementu rośnie, więc przewijany jest w dół. Więc wartość bool wskazuje, czy przesunięcie ScrollViewer na pionowy nie jest 0. Jeśli jest 0, nie ma potrzeby robić nic, a jeśli to nie jest 0, konieczne jest zachowanie jego położenia względem pozycji w rzutni:

static void scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) 
{ 
    if (scrollViewers_States[sender as ScrollViewer]) 
     (sender as ScrollViewer).ScrollToVerticalOffset(e.VerticalOffset + e.ExtentHeightChange); 

    scrollViewers_States[sender as ScrollViewer] = e.VerticalOffset != 0; 
} 

ExtentHeightChange służy tutaj do wskazania ilości dodanych elementów. W moim przypadku elementy są dodawane tylko na początku kolekcji, więc wszystko, co musiałem zrobić, to zwiększyć VerticalOffset ScrollViewera o tę wartość. I ostatnia rzecz: użycie. Oto przykład ListBox:

<Listbox ...> 
    <ListBox.Resourses> 
     <Style TargetType="ScrollViewer"> 
      <Setter Property="local:ScrollPreserver.PreserveScroll" Value="True" /> 
     </Style> 
    </ListBox.Resourses> 
</ListBox> 

Ta-da!Działa miło :)

+0

Czy nie wycieka ona z pamięci ze względu na słownik w polu statycznym? – TcKs

+0

DependencyProperties są wszystkie statyczne. Możesz przeczytać o tym na przykład tutaj: http://stackoverflow.com/questions/2989431/why-are-dependency-properties-static – EvAlex

+0

Wiem. Ale pole "scrollViewers_States" nie jest skrótem dla właściwości dependency. Przechowuje wystąpienia programu ScrollViewer. Czy masz jakiś kod, który usuwa stare instancje ze słownika? – TcKs

Powiązane problemy