Ich habe eine WPF ListBox
an eine ObservableCollection
gebunden. Wenn Objekte zur Sammlung hinzugefügt werden, verschiebt sich die Bildlaufposition ListBox
um die Größe der hinzugefügten Einträge. Ich möchte die Bildlaufposition beibehalten können, so dass die Objekte, die gerade angezeigt werden, nicht verschoben werden, während Dinge zur Liste hinzugefügt werden. Gibt es einen Weg, dies zu erreichen?Speichern einer gebundenen WPF-ListBox-Bildlaufposition, wenn sich die Sammlung ändert
Antwort
Ich bin mir nicht sicher, ob es eine Überschreibung dafür gibt, aber ich bezweifle es.
Im Zweifelsfall manuelle Bindung verwenden? : -} Im Ernst, das Standard-Bindungsverhalten ist - nun - Standard, also, wenn Sie spezielles Verhalten benötigen, ist manuelle Bindung eine bessere Wahl. Mit manueller Bindung können Sie die Bildlaufposition speichern und nach dem Hinzufügen der Elemente zurücksetzen.
Was zu einer offensichtlichen Frage führt - wie ermittle ich die aktuelle Scroll-Position? –
Erstellen Sie eine CollectionView. Versuchen Sie folgendes:
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);
}
Bitte erläutern Sie weiter. Diese Antwort scheint das Problem nicht zu lösen - sie meldet nur den aktuellen Gegenstand und die Position. Ich habe das gleiche Problem, indem ich eine ListBox mit einem ItemsPanel verwende, das auf ein VirtualizingStackPanel gesetzt ist. Das Problem, wie ich es sehe, ist, dass die Listenfeldelemente sich weiter bewegen, wenn neue Objekte zur Observable-Sammlung hinzugefügt werden. Es ist zwar möglich, Elemente nach dem Hinzufügen eines neuen Elements (z. B. mithilfe von SelectedIndex) dorthin zurückzusetzen, wo sie sich befinden. Dies erzeugt jedoch eine ruckartige Bewegung. Was benötigt wird, ist eine Möglichkeit, zu verhindern, dass sich die Bildlaufposition an erster Stelle ändert, wenn neue Elemente hinzugefügt werden. – CyberMonk
Ich habe das gleiche Problem, aber Ihre Lösung geht davon aus, dass etwas ausgewählt ist. Aber was, wenn der Benutzer nichts auswählen möchte? Scrollen Sie einfach irgendwo und lassen Sie es. – EvAlex
Haben Sie steuern, wenn die Dinge hinzugefügt werden? Ich meine, haben Sie einen oder zwei definierte Punkte, an denen Daten geändert werden? Wenn dies der Fall ist, wäre eine einfache Lösung (vielleicht nicht elegant) eine lokale int-Variable, die den aktuellen ListBox.SelectedIndex enthält. Speichern Sie den ausgewähltenIndex unmittelbar vor der Datenänderung in dieser Variablen, und legen Sie nach dem Hinzufügen von Daten den SelectedIndex von listBox auf den Wert dieser Variablen fest.
Zuerst finden Sie die aktuelle Position von ListBox.SelectedIndex und verwenden Sie dann ListBox.ScrollIntoView (/ * Current Index * /). Auch wenn die neuen Elemente in der Liste hinzugefügt werden, ist Ihre aktuelle Position und Ansicht gleich.
Ich sah das gleiche Problem und eine weitere Einschränkung: Benutzer wählt keine Elemente, sondern nur scrollt. Deshalb ist die ScrollIntoView()
Methode nutzlos. Hier ist meine Lösung.
Zuerst habe ich erstellt Klasse ScrollPreserver
es von DependencyObject, mit einer daran befestigten Abhängigkeitseigenschaft ableiten PreserveScroll
vom Typ 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);
}
...
}
Property geändert Rückruf Eigentum übernimmt von der Scroll gesetzt. Die Callback-Methode fügt diese Scroll zum privaten Wörterbuch und fügt ScrollChanged Ereignishandler hinzu:
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);
}
}
}
Dieses Wörterbuch wird Verweis auf alle ScrollViewers in Anwendung halten, die diese Klasse verwenden. In meinem Fall werden Artikel am Anfang der Sammlung hinzugefügt. Das Problem ist, dass sich die Ansichtsfensterposition nicht ändert. Es ist in Ordnung, wenn die Position 0 ist: Das erste Element im Ansichtsfenster ist immer das erste Element. Wenn das erste Element im Ansichtsfenster jedoch einen anderen Index als 0 hat und neue Elemente hinzugefügt werden, wird der Index dieses Elements so vergrößert, dass er nach unten gescrollt wird. So zeigt Bool Wert, ob Scroll des vertikale Versatz ist nicht 0. Wenn es 0 ist, gibt es keine Notwendigkeit, etwas zu tun, und wenn es nicht 0 ist, ist es notwendig, seine Position in Bezug auf Artikel im Ansichtsfenster zu erhalten:
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 wird hier verwendet, um die Anzahl der hinzugefügten Objekte anzugeben. In meinem Fall werden Elemente nur am Anfang der Sammlung hinzugefügt, also musste ich ScrollViewer's VerticalOffset nur um diesen Wert erhöhen. Und das letzte: Nutzung. Hier ist das Beispiel für ListBox:
<Listbox ...>
<ListBox.Resourses>
<Style TargetType="ScrollViewer">
<Setter Property="local:ScrollPreserver.PreserveScroll" Value="True" />
</Style>
</ListBox.Resourses>
</ListBox>
Ta-da!Works nice :)
Speicherlecks wegen Wörterbuch im statischen Feld nicht? – TcKs
DependencyProperties sind alle statisch. Sie können hier zum Beispiel lesen Sie hier: http://stackoverflow.com/questions/2989431/why-are-dependency-properties-static – EvAlex
Ich weiß. Das Feld "scrollViewers_States" ist jedoch keine Verknüpfung für die Abhängigkeitseigenschaft. Es speichert die Instanzen von ScrollViewer. Haben Sie einen Code, der alte Instanzen aus dem Wörterbuch entfernt? – TcKs
Ich habe gerade eine wirklich einfache WPF Databinding zu einer Listbox und wenn ich Elemente hinzufügen die Elemente, die in Sicht waren in Sicht blieb ... Was machst du noch? – TheCodeMonk
Versuchen Sie, neue Elemente zum großen Inhalt der Sammlung hinzuzufügen. Elemente, die Sie anzeigen, werden aus dem Ansichtsfenster verschoben, wenn neue Elemente hinzugefügt werden. Sie fügen 1 Element hinzu - das Ansichtsfenster bleibt an der gleichen Position (indexbasiert), was bedeutet, dass 1 Element in das Ansichtsfenster und 1 Element in das Ansichtsfenster eingegeben wurde. – EvAlex