C# 滚动到虚拟化项控件的元素
我有一个C# 滚动到虚拟化项控件的元素,c#,wpf,itemscontrol,virtualizingstackpanel,C#,Wpf,Itemscontrol,Virtualizingstackpanel,我有一个ItemsControl,它在ScrollViewer中显示其项目,并进行虚拟化。我正在尝试将ScrollViewer滚动到它包含的(屏幕外,因此是虚拟化的)项目。但是,由于项目是虚拟化的,因此它实际上不存在于屏幕上,并且没有位置(IIUC) 我在子元素上尝试了BringIntoView,但它没有滚动到视图中。我也尝试过用TransformToAncestor、TransformBounds和ScrollToVerticalOffset手动执行,但是TransformToAncestor
ItemsControl
,它在ScrollViewer
中显示其项目,并进行虚拟化。我正在尝试将ScrollViewer
滚动到它包含的(屏幕外,因此是虚拟化的)项目。但是,由于项目是虚拟化的,因此它实际上不存在于屏幕上,并且没有位置(IIUC)
我在子元素上尝试了BringIntoView
,但它没有滚动到视图中。我也尝试过用TransformToAncestor
、TransformBounds
和ScrollToVerticalOffset
手动执行,但是TransformToAncestor
永远不会返回(我猜也是因为虚拟化,因为它没有位置,但我没有证据),而且它之后的代码永远不会执行
是否可以滚动到具有虚拟化
项目控件的项目?如果是这样,怎么做?仔细查看.NET源代码,我建议您使用列表框及其方法。此方法的实现依赖于一些内部
方法,如virtualizangpanel.BringIndexIntoView
,它强制在该索引处创建项并滚动到该项。事实上,这些机制中的许多都是内部的,这意味着如果你试图自己去做这件事,你会过得很糟糕
(要使选择不可见,您可以重新放置ListBoxItems
)我一直在寻找一个带有VirtualzingStackPanel的Items控件以滚动到某个项目,现在已经有一段时间了,并且一直在寻找“使用ListBox”的答案。我不想,所以我找到了一个方法。首先,您需要为ItemsControl设置一个控件模板,该模板中有一个ScrollViewer(如果您使用的是items控件,则可能已经有了ScrollViewer)。我的基本模板如下所示(包含在ItemsControl的便捷样式中)
因此,有了扩展方法,您可以像ListBox的配套方法一样使用它:
myItemsControl.VirtualizedScrollIntoView(someItemInTheList);
很好
请注意,您也可以调用sv.ScrollToEnd()和其他常用的滚动方法来处理您的项目。我知道我参加聚会已经很晚了,但希望这可以帮助其他人寻找解决方案
int index = myItemsControl.Items.IndexOf(*your item*).FirstOrDefault();
int rowHeight = *height of your rows*;
myScrollView.ScrollToVerticalOffset(index*rowHeight);
//this will bring the given item to the top of the scrollViewer window
。。。我的XAML是这样设置的
<ScrollViewer x:Name="myScrollView">
<ItemsControl x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<!-- data here -->
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
使用@AaronCook示例,创建了一个适用于我的VirtualzingItemsControl的行为。以下是代码:
public class ItemsControlScrollToSelectedBehavior : Behavior<ItemsControl>
{
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(ItemsControlScrollToSelectedBehavior),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ItemsControlScrollToSelectedBehavior target = (ItemsControlScrollToSelectedBehavior)d;
object oldSelectedItems = e.OldValue;
object newSelectedItems = target.SelectedItem;
target.OnSelectedItemsChanged(oldSelectedItems, newSelectedItems);
}
protected virtual void OnSelectedItemsChanged(object oldSelectedItems, object newSelectedItems)
{
try
{
var sv = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(AssociatedObject, 0), 0) as ScrollViewer;
// now get the index of the item your passing in
int index = AssociatedObject.Items.IndexOf(newSelectedItems);
if (index != -1)
{
sv?.ScrollToVerticalOffset(index);
}
}
catch
{
// Ignore
}
}
}
public类项controlScrollToSelectedBehavior:Behavior
{
公共静态只读从属属性SelectedProperty=
DependencyProperty.Register(“SelectedItem”、typeof(object)、typeof(ItemsControlScrollToSelectedBehavior),
新的FrameworkPropertyMetadata(空,
新房地产变更回拨(OnSelectedItemsChanged));
公共对象SelectedItem
{
get=>GetValue(SelectedItemProperty);
set=>SetValue(SelectedItemProperty,value);
}
SelectedItemsChanged上的私有静态无效(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
ItemsControlScrollToSelectedBehavior目标=(ItemsControlScrollToSelectedBehavior)d;
对象oldSelectedItems=e.OldValue;
对象newSelectedItems=target.SelectedItem;
target.OnSelectedItemsChanged(oldSelectedItems、newSelectedItems);
}
SelectedItemsChanged上受保护的虚拟无效(对象oldSelectedItems、对象newSelectedItems)
{
尝试
{
var sv=VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(AssociatedObject,0),0)作为ScrollViewer;
//现在获取您传入的项目的索引
int index=AssociatedObject.Items.IndexOf(newSelectedItems);
如果(索引!=-1)
{
sv?.ScrollToVerticalOffset(索引);
}
}
抓住
{
//忽略
}
}
}
用途如下:
<ItemsControl Style="{StaticResource VirtualizingItemsControl}"
ItemsSource="{Binding BoundItems}">
<i:Interaction.Behaviors>
<behaviors:ItemsControlScrollToSelectedBehavior SelectedItem="{Binding SelectedItem}" />
</i:Interaction.Behaviors>
</ItemsControl>
对那些喜欢行为和干净XAML的人很有帮助,没有代码隐藏。我知道这是一个旧线程,但如果其他人(像我一样)遇到它,我想这值得我刚刚发现的更新答案
从.NET Framework 4.5开始,VirtualizingPanel
有一个publicBringIndexIntoViewPublic
方法,它的工作方式很有魅力,包括基于像素的滚动。您必须将您的项控件
分为子类,或者使用VisualTreeHelper
查找其子项虚拟化面板
,但无论哪种方式,现在都很容易强制您的项控件
精确滚动到特定项/索引。我希望避免这样做,因为我不需要“选择一个项目列表框的功能。知道为什么ItemsControl
没有ScrollIntoView
吗?@Seth:就像我说的,你可以隐藏选择,谁在乎它是否在那里?它没有滚动功能,因为它是这样设计的,ItemsControl
是最基本的items控件,这样的基类不需要滚动功能。现在了解如何使列表框
在单击时退出将项目完全滚动到视图中…是的,明白了,您只需处理ListBoxItem
的MouseDown
事件,并将MouseButtonEventArgs.Handled
设置为true
,即可。谢谢。不幸的是,我确实使用了基于像素的滚动,所以这对我不起作用,但我相信这将在将来帮助其他人,+1。如果您使用基于像素的滚动,那么您可以在ItemsControl中获得单个项目的大小(如果是固定大小,则很容易,但也可以在ItemControls ItemTemplate上进行枚举以获得单个项目的大小)然后
<ScrollViewer x:Name="myScrollView">
<ItemsControl x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<!-- data here -->
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
public class ItemsControlScrollToSelectedBehavior : Behavior<ItemsControl>
{
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(ItemsControlScrollToSelectedBehavior),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ItemsControlScrollToSelectedBehavior target = (ItemsControlScrollToSelectedBehavior)d;
object oldSelectedItems = e.OldValue;
object newSelectedItems = target.SelectedItem;
target.OnSelectedItemsChanged(oldSelectedItems, newSelectedItems);
}
protected virtual void OnSelectedItemsChanged(object oldSelectedItems, object newSelectedItems)
{
try
{
var sv = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(AssociatedObject, 0), 0) as ScrollViewer;
// now get the index of the item your passing in
int index = AssociatedObject.Items.IndexOf(newSelectedItems);
if (index != -1)
{
sv?.ScrollToVerticalOffset(index);
}
}
catch
{
// Ignore
}
}
}
<ItemsControl Style="{StaticResource VirtualizingItemsControl}"
ItemsSource="{Binding BoundItems}">
<i:Interaction.Behaviors>
<behaviors:ItemsControlScrollToSelectedBehavior SelectedItem="{Binding SelectedItem}" />
</i:Interaction.Behaviors>
</ItemsControl>