滚动到虚拟化ItemsControl的元素

Set*_*gie 10 c# wpf itemscontrol virtualizingstackpanel

我有一个ItemsControl在a中显示其项目ScrollViewer,并进行虚拟化.我试图将其滚动ScrollViewer到它包含的(屏幕外,因此虚拟化)项目.但是,由于项目是虚拟化的,因此屏幕上并不存在并且没有位置(IIUC).

我已尝试BringIntoView过子元素,但它不会滚动到视图中.我也尝试手动用做它TransformToAncestor,TransformBoundsScrollToVerticalOffset,但TransformToAncestor不会返回(我猜是因为虚拟化的同时,因为它没有位置,但我没有这方面的证明)和代码它从未执行后.

是否可以滚动到虚拟化的项目ItemsControl?如果是这样,怎么样?

小智 16

我一直在寻找带有VirtualizingStackPanel的ItemsControl来滚动到一个项目一段时间,并一直找到"使用ListBox"的答案.我不想,所以我找到了办法.首先,您需要为其中包含ScrollViewer的ItemsControl设置一个控件模板(如果您使用的是项目控件,则可能已经拥有该模板).我的基本模板如下所示(包含在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>
Run Code Online (Sandbox Code Playgroud)

所以我基本上有一个带有滚动查看器的边框,它将包含我的内容.
我的ItemsControl定义为:

<ItemsControl x:Name="myItemsControl" [..snip..] Style="{DynamicResource TheItemsControlStyle}"  ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True">
Run Code Online (Sandbox Code Playgroud)

好的,现在好玩的部分.我已经创建了一个扩展方法来附加到任何ItemsControl以使其滚动到给定项目:

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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

所以使用扩展方法就可以像ListBox的伴随方法一样使用它:

myItemsControl.VirtualizedScrollIntoView(someItemInTheList);
Run Code Online (Sandbox Code Playgroud)

效果很好!

请注意,您还可以调用sv.ScrollToEnd()和其他常用的滚动方法来绕过您的项目.


H.B*_*.B. 7

浏览.NET源代码,我建议你使用a ListBox及其ScrollIntoView方法.此方法的实现依赖于一些internal方法,这些方法VirtualizingPanel.BringIndexIntoView强制在该索引处创建项目并滚动到该索引.许多机制都是内部的这一事实意味着,如果你试图自己做这件事,那你将度过一段美好的时光.

(为了使它带来的选择不可见,你可以重新模仿ListBoxItems)


Pet*_*ore 7

我知道这是一个旧线程,但万一其他人(如我)遇到它,我认为值得更新我刚刚发现的答案。

从 .NET Framework 4.5 开始,VirtualizingPanel有一个公共BringIndexIntoViewPublic方法,它的作用就像一个魅力,包括基于像素的滚动。你必须任何一个子类的ItemsControl,或者使用VisualTreeHelper发现其子VirtualizingPanel,但无论哪种方式,它现在很容易迫使你ItemsControl精确滚动到一个特定的项目/索引。