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
有一个public
BringIndexIntoViewPublic
方法,它的工作方式很有魅力,包括基于像素的滚动。您必须将您的
项控件
分为子类,或者使用
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>