WPF虚拟化配置面板所选项目更改原因滚动

WPF虚拟化配置面板所选项目更改原因滚动,wpf,scroll,virtualization,selectionchanged,Wpf,Scroll,Virtualization,Selectionchanged,我已经实现了一个工作非常好的虚拟化面板,该面板实现了IScrollInfo。我正在使用键盘让它工作。该面板在ListView中用作ListView.ItemsPanel。当用户按下所选项目时,所选项目将正确更改,但不会出现滚动,当您到达最后一个可见项目时,您将无法再按下。我试图弄清楚如何让面板知道所选的项目并适当地滚动。我试着四处搜索,但似乎找不到任何与我想做的事情相关的东西。如果你能指出正确的方向,我将不胜感激 编辑:是虚拟化面板的代码,因此我找到了一个效果相当好的解决方案,我不确定这是否是最

我已经实现了一个工作非常好的虚拟化面板,该面板实现了IScrollInfo。我正在使用键盘让它工作。该面板在ListView中用作ListView.ItemsPanel。当用户按下所选项目时,所选项目将正确更改,但不会出现滚动,当您到达最后一个可见项目时,您将无法再按下。我试图弄清楚如何让面板知道所选的项目并适当地滚动。我试着四处搜索,但似乎找不到任何与我想做的事情相关的东西。如果你能指出正确的方向,我将不胜感激


编辑:是虚拟化面板的代码

,因此我找到了一个效果相当好的解决方案,我不确定这是否是最好的选择,但似乎效果不错

在ArrangeOverride方法中,在子对象上循环时,我执行以下操作

var listViewItem = child as ListViewItem;
if (listViewItem != null)
{
    listViewItem.Selected -= ListViewItemSelected;
    listViewItem.Selected += ListViewItemSelected;
}
在MeasureOverride中,我在这里调用CleanUpItems,我们需要取消订阅所选活动

var listViewItem = children[i] as ListViewItem;
if (listViewItem != null)
{
    listViewItem.Selected -= ListViewItemSelected;
}
我还添加了以下三个函数

private void ListViewItemSelected(object sender, RoutedEventArgs e)
{
    var listViewItem = sender as ListViewItem;
    if(listViewItem == null) return;

    var content = listViewItem.Content as CollectionViewGroup;
    if(content != null) return; //item is a group header dont click

    var items = ItemContainerGenerator as ItemContainerGenerator;
    if(items == null) return;
    BringIndexIntoView(items.IndexFromContainer(listViewItem));
    listViewItem.Focus();
}

protected override void BringIndexIntoView(int index)
{
    var offset = GetOffsetForFirstVisibleIndex(index);
    SetVerticalOffset(offset.Height);
}

private Size GetOffsetForFirstVisibleIndex(int index)
{
    int childrenPerRow = CalculateChildrenPerRow(_extent);
    var actualYOffset = ((index / childrenPerRow) * ChildDimension.Height) - ((ViewportHeight - ChildDimension.Height) / 2);
    if (actualYOffset < 0)
    {
        actualYOffset = 0;
    }
    Size offset = new Size(_offset.X, actualYOffset);
    return offset;
}
private void ListViewItemSelected(对象发送方,路由目标)
{
var listViewItem=作为listViewItem的发件人;
if(listViewItem==null)返回;
var content=listViewItem.content作为CollectionViewGroup;
如果(content!=null)return;//项是组头,请不要单击
var items=ItemContainerGenerator作为ItemContainerGenerator;
如果(items==null)返回;
BringIndexIntoView(items.IndexFromContainer(listViewItem));
listViewItem.Focus();
}
受保护的覆盖无效BringIndexIntoView(int索引)
{
var offset=GetOffsetForFirstVisibleIndex(索引);
设置垂直偏移(偏移高度);
}
私有大小GetOffsetForFirstVisibleIndex(整数索引)
{
int childrenPerRow=计算childrenPerRow(_范围);
变量实际偏移=((索引/childrenPerRow)*ChildDimension.Height)-(视口高度-ChildDimension.Height)/2);
如果(实际偏移<0)
{
实际偏移=0;
}
尺寸偏移=新尺寸(_offset.X,实际偏移);
返回偏移量;
}

GetOffsetForFirstVisibleIndex函数可能会根据您的实现而有所不同,但如果其他人在提出解决方案时遇到问题,这应该足够了。

我们可以看看您的
自定义面板的一些代码吗?@RohitVats我添加了一个指向面板源代码的链接。我现在也遇到了同样的问题。有什么消息吗?@LonelyPixel我刚刚提交了一个解决问题的方案。您的实现可能会有所不同,所以考虑到这一点。当我尝试时,所选的项目总是正好在视图的中间。选定内容在边框出现之前不会向上或向下移动,只有在边框出现时才会滚动,但它始终会滚动。此外,Up/PageUp和Down/PageDown始终将所选内容移动一行。因此,这似乎不是一个很好或完全解决原始问题的方法。@LonelyPixel它的滚动方式正是我决定实现它的方式。我想要一些不同的东西,你可以通过修改GetOffsetForFirstVisibleIndex让它以你想要的方式工作。该方法只返回视图将滚动到的偏移量。就PageUp和PageDown而言,我没有使用这些控件(我的用户使用的是遥控器,所以键是有限的),所以我没有对它们的实现做任何事情。我只是发布了这段代码,以便其他人可以看到如何连接面板以接收所选项目更改的通知。