ScrollViewer - 子元素的指示滚动到视图中

时间:2013-10-16 07:43:31

标签: wpf scrollviewer

当孩子滚动到视野中时是否会引发一个事件,并指出孩子被实现了什么?

当然有ScrollChanged事件,但它没有向我提供任何关于滚动到视图中的元素的指示。

提前致谢。

编辑:

Iv'e尝试连接到ScrollViewer的RequestBringIntoView事件,但它永远不会到达。或者,我也在包含这些项目的StackPanel上尝试了相同的操作:

XAML :

     <ScrollViewer RequestBringIntoView="ScrollViewer_RequestBringIntoView" >
        <StackPanel RequestBringIntoView="StackPanel_RequestBringIntoView">
            <Button Content="1" Height="20"/>
            <Button Content="2" Height="20"/>
            <Button Content="3" Height="20"/>
            <Button Content="4" Height="20"/>
            <Button Content="5" Height="20"/>
            <Button Content="6" Height="20"/>
            <Button Content="7" Height="20"/>
            <Button Content="8" Height="20"/>
            <Button Content="9" Height="20"/>
            <Button Content="10" Height="20"/>
            <Button Content="11" Height="20"/>
            <Button Content="12" Height="20"/>
            <Button Content="13" Height="20"/>
            <Button Content="14" Height="20"/>
            <Button Content="15" Height="20"/>
            <Button Content="16" Height="20"/>
            <Button Content="17" Height="20"/>
            <Button Content="18" Height="20"/>
            <Button Content="19" Height="20"/>
            <Button Content="20" Height="20"/>
            <Button Content="21" Height="20"/>
            <Button Content="22" Height="20"/>
            <Button Content="23" Height="20"/>
            <Button Content="24" Height="20"/>
        </StackPanel>
    </ScrollViewer>

他们永远不会到达。据我所知,ScrollViewer在其封装的子元素上调用BringIntoView,并且它们引发了RequestBringIntoView事件,我希望它会在可视化树上传播。我想ScrollViewer会在内部处理该事件。所以我最终遇到了如何在孩子被带入视野时获得通知的相同问题。我可以挂钩他们每个人或者也许一个ItemsControl会为我做那个..?

3 个答案:

答案 0 :(得分:6)

我认为你应该看看this article,它可以告诉观众控件是否对观众可见。

如果您要在ScrollChanged处理程序中挂接对该自定义方法的调用,因此每次滚动时都会检查,我认为这样做会有所帮助。我会自己尝试一下......

编辑:它有效!这是方法:

private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible)
        return false;

    Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

我的简单代码调用:

private void Scroll_Changed(object sender, ScrollChangedEventArgs e)
{
    Object o = sender;
    bool elementIsVisible = false;

    foreach (FrameworkElement child in this.stackPanel1.Children)
    {
        if (child != null)
        {
            elementIsVisible = this.IsUserVisible(child, this.scroller);

            if (elementIsVisible)
            {
                 // Your logic
            }
        }
    }
}

编辑:我从dev hedgehog发布的链接中查看了ScrollViewer的源代码,发现了这个有趣的私有函数:

// Returns true only if element is partly visible in the current viewport
private bool IsInViewport(ScrollContentPresenter scp, DependencyObject element)
{
     Rect viewPortRect = KeyboardNavigation.GetRectangle(scp);
     Rect elementRect = KeyboardNavigation.GetRectangle(element);
     return viewPortRect.IntersectsWith(elementRect);
}

这显然表明,即使ScrollViewer本身也有兴趣知道什么是可见的,并且正如我预期的那样,基本上执行与辅助方法相同的计算。下载此代码并查看调用此方法,其中和原因可能是值得的。

编辑:看起来像是由OnKeyDown()调用并用于确定焦点行为,就是这样。有趣....

答案 1 :(得分:2)

新编辑:我再次阅读了您的问题,我意识到我一开始并不了解您。

抱歉,我认为您的意思是,当您将鼠标放在里面时,或者在第一个可见或最后一个可见项目之间设置焦点时,您希望通知ScrollViewer的视口中的孩子。这就是RequestBringIntoView派上用场的时候。

还有一些事情我不清楚:

“当孩子滚动到视野中时,是否会出现一个事件,表明孩子被实现了什么?” - 你在谈论普通面板还是VirtualizingStackPanel?

答案ouflak张贴的设计都不错。实际上通常是WPF。

如果您仍然没有考虑我们的建议,请查看ScrollViewer的源代码。

http://www.dotnetframework.org/default.aspx/Dotnetfx_Win7_3@5@1/Dotnetfx_Win7_3@5@1/3@5@1/DEVDIV/depot/DevDiv/releases/Orcas/NetFXw7/wpf/src/Framework/System/Windows/Controls/ScrollViewer@cs/2/ScrollViewer@cs

也许你会偶然发现你可以使用的事件。

旧编辑:这就是您要找的。

http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.requestbringintoview.aspx

答案 2 :(得分:1)

好的,另一个答案是不同的,但基于dev hedgehog's的建议。基本上这个想法是项目的BringIntoView方法在实际处于视图中时总是被调用。如何确定这一点有点神秘,如果将两个项目滚动到视图中会发生什么是未知的。但是一些示例代码应该捕获BringIntoView的所有调用:

   string guid = System.Guid.NewGuid().ToString();

   RoutedEvent scrollIntoViewEvent = EventManager.RegisterRoutedEvent(
                guid, 
                RoutingStrategy.Direct, 
                typeof(RequestBringIntoViewEventHandler), 
                typeof(ScrollViewer));

   EventManager.RegisterClassHandler(typeof(ScrollViewer), scrollIntoViewEvent, new RequestBringIntoViewEventHandler(this.RequestBringIntoView_Handler), true);

一个示例处理程序:

   private void RequestBringIntoView_Handler(object sender, RequestBringIntoViewEventArgs e)
   {
       Object o = sender;
   }

这里的一些推文可能只是为了捕获所有BringIntoView事件,这应该为原始问题提供解决方案,因为此处理程序确实将项目传递给发件人。