Set*_*gie 10 c# wpf itemscontrol virtualizingstackpanel
我有一个ItemsControl在a中显示其项目ScrollViewer,并进行虚拟化.我试图将其滚动ScrollViewer到它包含的(屏幕外,因此虚拟化)项目.但是,由于项目是虚拟化的,因此屏幕上并不存在并且没有位置(IIUC).
我已尝试BringIntoView过子元素,但它不会滚动到视图中.我也尝试手动用做它TransformToAncestor,TransformBounds和ScrollToVerticalOffset,但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()和其他常用的滚动方法来绕过您的项目.
浏览.NET源代码,我建议你使用a ListBox及其ScrollIntoView方法.此方法的实现依赖于一些internal方法,这些方法VirtualizingPanel.BringIndexIntoView强制在该索引处创建项目并滚动到该索引.许多机制都是内部的这一事实意味着,如果你试图自己做这件事,那你将度过一段美好的时光.
(为了使它带来的选择不可见,你可以重新模仿ListBoxItems)
我知道这是一个旧线程,但万一其他人(如我)遇到它,我认为值得更新我刚刚发现的答案。
从 .NET Framework 4.5 开始,VirtualizingPanel有一个公共BringIndexIntoViewPublic方法,它的作用就像一个魅力,包括基于像素的滚动。你必须任何一个子类的ItemsControl,或者使用VisualTreeHelper发现其子VirtualizingPanel,但无论哪种方式,它现在很容易迫使你ItemsControl精确滚动到一个特定的项目/索引。