Visually Located


Creating a behavior to stretch header content when at the top of a scroller

I’ve been playing around a lot with my wife’s new iPhone a lot lately. One feature I love on some of the apps is when you reach the top of a page a header image will stretch out to indicate you are at the top of the page. This is a fun feature that’s super easy to add using a behavior.


The behavior will focus on scaling the image up by a factor but only when the ScrollerViewer is being “stretched”.

public class StretchyHeaderBehavior : Behavior<FrameworkElement>
    private ScrollViewer _scroller;
    public double StretchyFactor
        get { return (double)GetValue(ScaleFactorProperty); }
        set { SetValue(ScaleFactorProperty, value); }
    public static readonly DependencyProperty ScaleFactorProperty = DependencyProperty.Register(
        new PropertyMetadata(0.5));
    protected override void OnAttached()
        AssociatedObject.SizeChanged += OnSizeChanged;
        _scroller = AssociatedObject.GetParentOfType<ScrollViewer>();
        if (_scroller == null)
            AssociatedObject.Loaded += OnLoaded;
    private void OnLoaded(object sender, RoutedEventArgs e)
        _scroller = AssociatedObject.GetParentOfType<ScrollViewer>();
        AssociatedObject.Loaded -= OnLoaded;
    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    private void AssignEffect()
        if (_scroller == null) return;
        CompositionPropertySet scrollerViewerManipulation = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(_scroller);
        var compositor = scrollerViewerManipulation.Compositor;
        // See documentation for Lerp and Clamp: 
        var scaleAnimation = compositor.CreateExpressionAnimation(
             "Lerp(1, 1+Amount, Clamp(ScrollManipulation.Translation.Y/50, 0, 1))");
        scaleAnimation.SetScalarParameter("Amount", (float)StretchyFactor);
        scaleAnimation.SetReferenceParameter("ScrollManipulation", scrollerViewerManipulation);
        var visual = ElementCompositionPreview.GetElementVisual(AssociatedObject);
        var backgroundImageSize = new Vector2((float)AssociatedObject.ActualWidth, (float)AssociatedObject.ActualHeight);
        visual.Size = backgroundImageSize;
        // CenterPoint defaults to the top left (0,0). We want the strecth to occur from the center
        visual.CenterPoint = new Vector3(backgroundImageSize / 2, 1);
        visual.StartAnimation("Scale.X", scaleAnimation);
        visual.StartAnimation("Scale.Y", scaleAnimation);

You can find the behavior on my GitHub repo along with a sample project. The sample gif above was even combined with the ParallaxBehavior to give it a little extra fun!

Thanks to Neil Turner for helping come up with the name of the behavior!

blog comments powered by Disqus