2

当孩子滚动到视图中并指示实现了哪个孩子时,是否会引发事件?

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

编辑 :

我尝试连接到 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 会为我做这件事吗?

4

3 回答 3

7

我认为你应该看看这篇文章,它提供了一种判断控件是否对查看者可见的方法。

如果您要在 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
            }
        }
    }
}

编辑:我从开发刺猬发布的链接中查看了 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() 调用并用于确定焦点行为,仅此而已。有趣的....

于 2013-10-16T08:35:22.447 回答
2

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

抱歉,我以为您的意思是当您将鼠标悬停在 ScrollViewer 的视口内时,或者在第一个可见项或最后一个可见项之间设置焦点时,您希望通知哪些子项在 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/系统/Windows/Controls/ScrollViewer@cs/2/ScrollViewer@cs

也许在那里你会偶然发现一个你可以使用的事件。

旧编辑:这是你要找的。

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

于 2013-10-16T07:50:13.733 回答
1

Ok, another answer along a different vein, but based on dev hedgehog's suggestion. Basically the idea is that an item's BringIntoView method is always called when it is actually in view. How this is determined is a bit mysterious, and what happens if say two items are scrolled into view is unknown. However some sample code that should capture all calls of 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);

And an example handler:

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

A bit of tweeking here might just get this to capture all of the BringIntoView events, which should provide a solution for the original question as this handler does have the item passed into the sender.

于 2013-10-16T11:28:13.950 回答