Otterrete lo stesso effetto per una virtualizzazione non ItemsControl
(ScrollViewer.CanContentScroll="False"
) come per una virtualizzazione se si scorre verso il basso e quindi si seleziona il contenitore superiore visibile con il mouse. Questo può anche essere fatto in codice.
Quando CanContentScroll
è impostato su falso, la virtualizzazione è disattivata in modo che tutti i contenitori vengano generati in qualsiasi momento. Per ottenere il contenitore visibile in alto possiamo iterare i contenitori dall'alto fino a raggiungere lo VerticalOffset
dello ScrollViewer
. Una volta ottenuto, possiamo semplicemente chiamare BringIntoView
su di esso e si allineerà in alto proprio come sarebbe se fosse utilizzata la virtualizzazione.
Esempio
<ListBox ItemsSource="{Binding MyCollection}"
ScrollViewer.CanContentScroll="False"
ScrollViewer.ScrollChanged="listBox_ScrollChanged" >
chiamata BringIntoView
sul contenitore superiore visibile nel gestore di eventi
private void listBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ItemsControl itemsControl = sender as ItemsControl;
ScrollViewer scrollViewer = e.OriginalSource as ScrollViewer;
FrameworkElement lastElement = null;
foreach (object obj in itemsControl.Items)
{
FrameworkElement element = itemsControl.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
if (offset > e.VerticalOffset)
{
if (lastElement != null)
lastElement.BringIntoView();
break;
}
lastElement = element;
}
}
Per solo ottenere questo effetto quando si desidera chiamare PageDown
, in un clic del pulsante, ad esempio, è possibile creare un'estensione me thod for ListBox
chiamato LogicalPageDown
.
listBox.LogicalPageDown();
ListBoxExtensions
public static class ListBoxExtensions
{
public static void LogicalPageDown(this ListBox listBox)
{
ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(listBox);
ScrollChangedEventHandler scrollChangedHandler = null;
scrollChangedHandler = (object sender2, ScrollChangedEventArgs e2) =>
{
scrollViewer.ScrollChanged -= scrollChangedHandler;
FrameworkElement lastElement = null;
foreach (object obj in listBox.Items)
{
FrameworkElement element = listBox.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
if (offset > scrollViewer.VerticalOffset)
{
if (lastElement != null)
lastElement.BringIntoView();
break;
}
lastElement = element;
}
};
scrollViewer.ScrollChanged += scrollChangedHandler;
scrollViewer.PageDown();
}
}
ho notato nella sua domanda che già ottenuto il ScrollViewer
ma io sono l'aggiunta di un'implementazione di GetVisualChild
se qualcun altro si imbatte in questa domanda
public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
@Zmaster: dimenticato che 'Margine' ecc. Può essere coinvolto quando si trova la posizione relativa del contenitore. Aggiornato la mia risposta di conseguenza –
Alla fine ho deciso di utilizzare l'uso ScrollToOffset() calcolando l'offset di destinazione con l'ipotesi che ogni oggetto abbia la stessa altezza (questo è il mio caso). È più efficiente di iterare gli oggetti. La tua soluzione, d'altro canto, funziona anche sull'altezza di diversi oggetti, quindi l'ho accettata come risposta. – Zmaster