He estado buscando en conseguir un ItemsControl con una VirtualizingStackPanel para desplazarse a un elemento desde hace un tiempo, y se mantiene la búsqueda de la "utilizar un ListBox" respuesta. No quería, así que encontré la manera de hacerlo. Primero necesita configurar una plantilla de control para su ItemsControl que tenga un ScrollViewer (que probablemente ya tenga si usa un control de elementos). Mi plantilla básica se parece a la siguiente (contenida en un estilo práctico para ItemsControl)
<Style x:Key="TheItemsControlStyle" TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False" HorizontalScrollBarVisibility="Auto">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
así que he básicamente tiene una frontera con un visor de desplazamiento thats de ir a contener mi contenido.
Mi ItemsControl se define con:
<ItemsControl x:Name="myItemsControl" [..snip..] Style="{DynamicResource TheItemsControlStyle}" ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True">
Ok, ahora viene la parte divertida. He creado un método de extensión para conectar a cualquier ItemsControl conseguirlo para desplazarse hasta el elemento dado:
public static void VirtualizedScrollIntoView(this ItemsControl control, object item) {
try {
// this is basically getting a reference to the ScrollViewer defined in the ItemsControl's style (identified above).
// you *could* enumerate over the ItemsControl's children until you hit a scroll viewer, but this is quick and
// dirty!
// First 0 in the GetChild returns the Border from the ControlTemplate, and the second 0 gets the ScrollViewer from
// the Border.
ScrollViewer sv = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild((DependencyObject)control, 0), 0) as ScrollViewer;
// now get the index of the item your passing in
int index = control.Items.IndexOf(item);
if(index != -1) {
// since the scroll viewer is using content scrolling not pixel based scrolling we just tell it to scroll to the index of the item
// and viola! we scroll there!
sv.ScrollToVerticalOffset(index);
}
} catch(Exception ex) {
Debug.WriteLine("What the..." + ex.Message);
}
}
Así que con el método de extensión en el lugar que lo utilizaría como método compañero de ListBox:
myItemsControl.VirtualizedScrollIntoView(someItemInTheList);
Funciona muy bien!
Tenga en cuenta que también puede llamar a sv.ScrollToEnd() y a los demás métodos de desplazamiento habituales para desplazarse por sus elementos.
Me gustaría evitar hacer esto porque no necesito la capacidad de "seleccionar un elemento" de 'ListBox'.¿Alguna idea de por qué 'ItemsControl' no tiene' ScrollIntoView'? –
@Seth: Como he dicho, puede ocultar la selección, ¿a quién le importa si está allí? No tiene desplazamiento porque fue diseñado de esa manera, 'ItemsControl' es el más básico de los controles de elementos, la funcionalidad de desplazamiento no es necesaria para dicha clase base. –
Ahora, para averiguar cómo hacer que 'ListBox' deje de desplazarse un elemento completamente a la vista al hacer clic ... –