Is there an event that is raised when a child is scrolled into view and gives an indication of what child was realized?
Of course there is the ScrollChanged event, b
New Edit: I read your question again and I realized I didn't understand you in first place.
Sorry I thought you mean you wish to be notified what children are inside Viewport of ScrollViewer when you mouse your mouse inside, or set focus betweent first visible or last visible item. That's when RequestBringIntoView comes handy.
Still there are few things which aren't clear to me:
"Is there an event that is raised when a child is scrolled into view which gives an indication of what child was realized ?" - Are you talking about normal panel or VirtualizingStackPanel?
The answer ouflak posted is not bad design at all. It is actually usual WPF.
If you still not happen with our suggestion take a look at source code of 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
Maybe there you will stumble upon an event you could use.
Old Edit: Is this what you looking for.
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.requestbringintoview.aspx
I think you should look at this article which gives a way of telling if a control is visible to the viewer.
If you were to hook up a call to that custom method in your ScrollChanged handler, thus having a checked every time you scrolled, I think that would do the trick. I'll try this out myself....
Edit: It works! Here's the method:
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);
}
And my simple code call:
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
}
}
}
}
Edit: I took a look through the source code of the ScrollViewer from the link that dev hedgehog posted and found this interesting private function:
// 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);
}
This obviously suggests that even the ScrollViewer itself is interested in knowing what's visible and, as I expected, essentially performs the same kind of calculation as in that helper method. It might be worthwhile to download this code and see who calls this method, where, and why.
Edit: Looks like its called by OnKeyDown() and used to determine focus behavior, and that's it. Interesting....
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.