Silverlight BringIntoView() extension method (with OnGotFocus behavior)

It all started because I couldn't find a way to automatically scroll any element into view in Silverlight (a feature
that exists in WPF).  I take that back, I could get the job done with a ListBox's ScrollIntoView(ListBoxItem item)
method, but I hardly wanted everything on my screen to be wrapped as a ListBoxItem; it feels as dirty as it sounds.  

Anyways, here is the code.

/* Extension Methods */
public static class FrameworkElementExtensions
{
    private const int ScrollPadding = 10;

    public static void BringIntoView(this FrameworkElement frameworkElement)
    {
        var parent = VisualTreeHelper.GetParent(frameworkElement);
        while(parent != null)
        {
            parent = VisualTreeHelper.GetParent(parent);
            var scrollViewer = parent as ScrollViewer;
            if(scrollViewer != null)
            {
                frameworkElement.BringIntoViewForScrollViewer(scrollViewer);
                break;
            }
        }
    }

    public static void BringIntoViewForScrollViewer(this FrameworkElement frameworkElement, ScrollViewer scrollViewer)
    {
        var transform = frameworkElement.TransformToVisual(scrollViewer);
        var positionInScrollViewer = transform.Transform(new Point(0, 0));

        if (positionInScrollViewer.Y < 0 || positionInScrollViewer.Y > scrollViewer.ViewportHeight)
            scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + positionInScrollViewer.Y - ScrollPadding);
    }
} 

 

Bonus Behavior! (Behaviors?!? Here is an Introduction).
This behavior was created ensure that as a user tabs through the screen, scrolling automatically takes place.

Note: If you want to use behaviors in Silverlight, install the Blend 3 SDK and reference
Microsoft.Expression.Interactions.dll

/* Behavior class  */
public class BringIntoViewOnFocusBehavior : Behavior<FrameworkElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.GotFocus += OnGotFocus;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.GotFocus -= OnGotFocus;
    }

    private void OnGotFocus(object sender, RoutedEventArgs e1)
    {
        AssociatedObject.BringIntoView();
    }        
}

 

<!-- XAML usage of custom behavior -->
<UserControl 
    xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    xmlns:behaviors="clr-namespace:YourNamespaceToBehaviors.Behaviors">        
    <StackPanel>
        <TextBox>
            <interactivity:Interaction.Behaviors>
                <behaviors:BringIntoViewOnFocusBehavior />
            </interactivity:Interaction.Behaviors>
        </TextBox>
    </StackPanel>    
</UserControl>
Published Thursday, November 05, 2009 9:23 AM by dotjosh
Filed under: , , ,

Comments

# re: Silverlight BringIntoView() extension method (with OnGotFocus behavior)

hi Josh, do you know of a way to do this for version 2.0 of SL?

Thursday, January 14, 2010 1:19 PM by Voodoo

# re: Silverlight BringIntoView() extension method (with OnGotFocus behavior)

Great code! ive made a slight enhancement to the BringIntoViewForScrollViewer method so that if a  framework element is partially cut off the screen at the bottom, it will show it.

       public static void BringIntoViewForScrollViewer(this FrameworkElement frameworkElement, ScrollViewer scrollViewer)

       {

           var transform = frameworkElement.TransformToVisual(scrollViewer);

           Point topPositionInScrollViewer = transform.Transform(new Point(0, 0));

           Point bottomPositionInScrollViewer = transform.Transform(new Point(0, frameworkElement.ActualHeight));

           if(topPositionInScrollViewer.Y < 0)

               scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + topPositionInScrollViewer.Y - ScrollPadding);

           else if (bottomPositionInScrollViewer.Y > scrollViewer.ViewportHeight)

               scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + bottomPositionInScrollViewer.Y - scrollViewer.ViewportHeight + ScrollPadding);

       }

Friday, July 16, 2010 2:13 AM by Patrock

# re: Silverlight BringIntoView() extension method (with OnGotFocus behavior)

Hi,

I'm having some trouble using your extension. ViewportHeight is always 1, ViewportWidth is always infinity, and "positionInScrollViewer" is always the same (e.g. {5,2} or {1,2}).

The ActualWidth and ActualHeight values appear valid (e.g. 28 and 431 respectively).

Some background: The TextBox I'm testing on is inside a DataTemplate of an ItemTemplate.

Any ideas?

Thursday, November 04, 2010 12:07 PM by Peet Brits

Leave a Comment

(required) 
(required) 
(optional)
(required)