Facebook-like 'scroll to top' functionality in a ScrollViewer

Facebook did it some time ago! - Added this nifty button that makes you go to the top whenever you scroll upwards on Windows Phone.
A true UX gem which is almost expected by users because of its simple and intuitive nature. 

Yesterday I saw Tim Heuer tweeting how it's almost standard and something that the users expects.
My immediate thought was let's build it and bring it out there! 

 

Download the source and sample

 

 

 

Behavior and code explained

The solution is pretty straight forward. The solution is created using a custom behavior which injects a button and the necessary functionality into a scrollviewer. 
(Big shoutout to Joost van Schaik and Morten Nielsen for sharing their amazing insights on creating custom behaviors).

Using the behavior

Using the behavior is pretty straight forward. You simply set it on the scrollviewer and set the ScrollToTopButton property on the behavior with the button you want to inject and appear on top of your scrollviewer while scrolling backwards towards the top.

<ScrollViewer
    Grid.Row="1">
    <Interactivity:Interaction.Behaviors>
        <local:ScrollToTopBehavior>
            <local:ScrollToTopBehavior.ScrollToTopButton>
                <Button
                    x:Name="GoToTopButton"
                    Grid.RowSpan="2"
                    VerticalAlignment="Top"
                    HorizontalAlignment="Right"
                    Style="{StaticResource CleanButtonStyle}"
                    Margin="0,20,20,0">
                    <Image
                        x:Name="image"
                        Width="100"
                        Height="100"
                        Source="ms-appx:///Images/ToTop.png"
                        Stretch="None"
                        HorizontalAlignment="Right" />
                </Button>
            </local:ScrollToTopBehavior.ScrollToTopButton>
        </local:ScrollToTopBehavior>
    </Interactivity:Interaction.Behaviors>
</ScrollViewer>

Inside the behavior

To create a behavior we have to implement the IBehavior interface located inside the Behaviors SDK which is an extension shipped with Visual Studio and installed per default. 
The interface gives us an attach (hook into events and functionality) and detach (unhook events and functionality to allow GC) method which automatically is called when the behavior is attacted and detached to the scrollviewer.

In the attach method we hook into on following:

  • ScrollViewer.ViewChanging which is called continously while scrolling. This event is used to get offsets and see if inertia is enabled while scrolling.
  • ScrollViewer.Loaded to find the appropriate container inside the template of the scrollviewer and inject our button into.
public void Attach(DependencyObject associatedObject)
{
    offsets = new List<double>();
    isHidden = true;
    buttonAdded = false;

    if (!DesignMode.DesignModeEnabled)
    {
        _associatedObject = associatedObject;

        scrollviewer = _associatedObject as ScrollViewer;

        if (scrollviewer != null)
        {
            scrollviewer.ViewChanging += Scrollviewer_ViewChanging;
            scrollviewer.Unloaded += Scrollviewer_Unloaded;
        }

        if (ScrollToTopButton != null)
        {
            ScrollToTopButton.Tapped += ScrollToTopButton_Tapped;
            ScrollToTopButton.Visibility = Visibility.Collapsed;
            ScrollToTopButton.Name = "ScrollToTopButton";
        }
    }
}


Injecting the button

To find the right container in the scrollviewer template we create a recursive method to traverse down the visual tree until we find our grid. We create a container from our button and add it to our located grid. The container-grid is added to ensure we can animate the button. (Thanks @danielvistisen for the tip)

private void Scrollviewer_Loaded(object sender, RoutedEventArgs e)
{
    FindParts(scrollviewer);
}

private void FindParts(DependencyObject dp)
{
    if (buttonAdded == false)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dp); i++)
        {
            var control = VisualTreeHelper.GetChild(dp, i);

            if (buttonAdded == false && control is Grid)
            {
                container = new Grid();
                container.Children.Add(ScrollToTopButton);
                container.Visibility = Visibility.Collapsed;

                grid = control as Grid;
                grid.Children.Add(container);
                buttonAdded = true;
                break;
            }
            else
            {
                FindParts(control);
            }
        }
    }
    else
    {
        return;
    }
}

Determine scrolldirection and showing the button

By hooking into the ViewChanging event on the scrollviewer we'll continuously get the new offset as long as the user scrolls vertically and the there's not inertia included.

Inertia will be false while the user scrolls real slow eg. in reading scenarios. We only want to show it if the user actually shows towards the top. no

Each offset is added to a list which is used to determine our direction and our proximity towards the top. 

If the right conditions is meet we'll either show or hide the Button which allows us to scroll to the top.

private void Scrollviewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
    var offset = scrollviewer.VerticalOffset;

    if (e.IsInertial)
    {
        Debug.WriteLine(offset);
        offsets.Add(offset);
        count = count + 1;

        DetermineVisualstateChange();
    }
}

private void DetermineVisualstateChange()
{
    if (count > 4)
    {
        if (offsets[count - 1] > 250.0 && offsets[count - 2] < offsets[count - 3]
            && offsets[count - 1] < offsets[count - 2])
        {
            ShowGoToTopButton();
        }
        else if ((offsets[count - 1] > 250.0 && offsets[count - 3] < offsets[count - 2]
            && offsets[count - 2] < offsets[count - 1]) || offsets[count - 1] < 250.0)
        {
            HideGoTopTopButton();
        }
    }
}

Showing and hiding the button

Hiding and showing the button is done by a combination of using the built in FadeInThemeAnimation and setting visibility on the button.
The themeanimation animates the border inside the button template and when that's done we collapse the button visibility.

Important: If you modify the button template you need to have a control with the Name set to "border" otherwise the behavior wont to work!

private void HideGoTopTopButton()
{
    if (!isHidden)
    {
        isHidden = true;

        var storyboard = new Storyboard();
        var animation = new FadeOutThemeAnimation();
        animation.SetValue(Storyboard.TargetNameProperty, "ScrollToTopButton");
        Storyboard.SetTarget(animation, ScrollToTopButton);
        storyboard.Children.Add(animation);
        storyboard.Completed += (e,a) => { container.Visibility = Visibility.Collapsed; };
        storyboard.Begin();
    }
}

private void ShowGoToTopButton()
{
    if (isHidden)
    {
        container.Visibility = Visibility.Visible;
        isHidden = false;
        var storyboard = new Storyboard();
        var animation = new FadeInThemeAnimation();
        animation.SetValue(Storyboard.TargetNameProperty, "ScrollToTopButton");
        Storyboard.SetTarget(animation, ScrollToTopButton);
        storyboard.Children.Add(animation);
        storyboard.Begin();
    }
}

Scrolling to the top

When the button is visibile and the user invokes the Tapped event we call the ChangeView event which will set the scrollviewers vertical offset to 0 - which is at the top.
Calling the ChangeView method will trigger a offset change but that's not detected by detected by the ViewChanging event.  We call the HideGoToTopButton method to hide the button afterwards.

private void ScrollToTopButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
    scrollviewer.ChangeView(null, 0, null);
    HideGoTopTopButton();
}

 

Final words

This was infact my first custom behavior - ever. I created it after Morten Nielsen pointed out that my inspirational textbox styles implementation could have been done using behaviors.
After a few code iterations based on input from @skendrot, @dotmorten and @scottisafool I think the result is rather great. 

The final finish could be added by using @xyzzer's WinRT toolkit and have the scrolling animate all the way to the top using his scrollviewer extension but I omitted that part to avoid 3rd party dependencies. 

I hope you joined the post and find it useful. Feel free to share your thoughts, comments and maybe even improvements. I'd love to hear from you.

Happy coding!

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.

Implementing Image blur and gaussian blur in a Windows universal app

Image blur and gaussian blur can give a nice visual effect if used right . Blurred images for backgrounds can give a nice subtle effect and for the same reason it has been used by designers for years. However it's not supported out of the box on Windows Universal apps platform. Luckily there's atleast  two different nuget packages which can help us get started. 
I tried both the Lumia Imaging SDK 2.0 and Win2D. 

Between the two I see Win2D as the winner because the rendering quality, speed and amount of documentation. The downside is  that you don't have the same layout flexibility out of the box as with the Lumia Image SDK. 
Below you'll find source for both implementations.

Win2D Gaussian Blur

Tap to see the Gaussian Blur rendering. The rendering quality way better than the Lumia Image SDK.

Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering. It makes use of the Direct2D engine and utilizes the GPU to create the new blur effect. 
Win2D comes with a great documentation that gets you started in seconds and it works on both Windows Phone and Windows Store. 
The rendering is smooth, crisp and it's really really fast. Only downside is that you have to work the with the Win2D CanvasControl and CanvasBitmap rather than a regular Bitmap or WriteableBitmap.

 

Download demo and sample source

 

Lumia Image SDK 2.0

Tap to see the Blur rendering. The rendering quality not quite as good as the Win2D rendering.

The Lumia Image SDK is a Nokia and now Microsoft owned SDK created to provide easy-to-use filtering and image manipulation capabilities. The Image SDK is known for being fast - especially when working with live camera feeds and it has been a part of the platform for a while. I assume it makes use of the GPU but didn't find any material on the core of the Lumia Image SDK.

The rendering is not nearly as smooth as the Win2D though. If you compare the images it looks like the Lumia Image SDK makes use of a blur algorithm that blurs in squares. Not nearly is neat and good looking as Win2D but on the upside you get to work directly with Bitmaps and Writeable bitmaps with offers layout flexibility compared to Win2D where you have to some of the calculations yourself to get the image in the right size. 

Download source and sample

 

Wrapup

Win2D is the clear winner here though the Lumia SDK is a bit easier to implement but offers less flexibility and quality. A huge shout out to Rodrigo Diaz (@r2d2rigo) for introducing me to Win2D! He's a absolutely brilliant and clever guy. One to follow for sure! 

If you prefer not downloading the source you can find it below this section.

Feel free to contact contact me or write in the comments below if you have any questions or problems getting the sample to work.

P.S Make sure you follow me on twitter @deanihansen for more news, design tips, articles and how-to's.


Code snippets for Lumia Image SDK blur and Win2D Gaussian Blur

Lumia Image SDK implementation:

1) This code snippet is a part of a templated control. The template contains an Image named "imagePresenter":

private async Task RenderImage()
{
    if (!string.IsNullOrWhiteSpace(ImageUrl))
    {
        var imageControl = GetTemplateChild("imagePresenter") as Image;

        if (imageControl != null)
        {
            var client = new HttpClient();

            // or get stream locally
            var stream = await client.GetStreamAsync(ImageUrl);

            var source = new Lumia.Imaging.StreamImageSource(stream, ImageFormat.Undefined);

            var source = new Lumia.Imaging.StreamImageSource(stream, ImageFormat.Undefined);

            var info = await source.GetInfoAsync();

            double displayScaling = DisplayInformation.GetForCurrentView().LogicalDpi / 96.0;


            double pixelWidth = Window.Current.Bounds.Width * displayScaling;

            var scalefactor = pixelWidth / info.ImageSize.Width;

            using (var filterEffect = new FilterEffect(source))
            {
                WriteableBitmap target = new WriteableBitmap((int)pixelWidth, (int)(info.ImageSize.Height * scalefactor));

                var filter = new BlurFilter(45);

                filterEffect.Filters = new IFilter[] { filter };

                using (var renderer = new WriteableBitmapRenderer(filterEffect, target, OutputOption.PreserveAspectRatio))
                {
                    await renderer.RenderAsync();

                    imageControl.Source = target;
                }
            }
        }
    }
}

Win2D Gaussian blur implementation

1) Create the CanvasControl which will host our Image and hook into the two events. Finally set the CanvasControl as Content on a contentpresenter or control.

control = new CanvasControl();
control.Draw += OnDraw;
control.CreateResources += OnCreateResources;
contentPresenter.Content = control;

2) Create and load the image from a URL

private async void OnCreateResources(CanvasControl sender, object args)
{
    if (!string.IsNullOrWhiteSpace(ImageUrl))
    {
        scaleEffect = new ScaleEffect();
        blurEffect = new GaussianBlurEffect();

        image = await CanvasBitmap.LoadAsync(sender.Device,
          new Uri(ImageUrl));

        imageLoaded = true;

        sender.Invalidate();
    }
}

3) On Draw we take the draw session and apply a scale effect to resize to the appropriate siize for the screen and DPI. Afterwards the blur effect is applied and finally the draw is called to render the image onto the Canvas.

private void OnDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
    if (imageLoaded)
    {
        using (var session = args.DrawingSession)
        {
            session.Units = CanvasUnits.Pixels;

            double displayScaling = DisplayInformation.GetForCurrentView().LogicalDpi / 96.0;

            double pixelWidth = sender.ActualWidth * displayScaling;

            var scalefactor = pixelWidth / image.Size.Width;

            scaleEffect.Source = this.image;
            scaleEffect.Scale = new Vector2()
            {
                X = (float)scalefactor,
                Y = (float)scalefactor
            };

            blurEffect.Source = scaleEffect;
            blurEffect.BlurAmount = Blur;

            session.DrawImage(blurEffect, 0.0f, 0.0f);
        }
    }
}

Custom Page Template for Windows apps

In almost all Windows 8 or phone project involved in we use a create custom page class which derives from the Page class. Doing this allows you centralize functionality such as:

  • Providing a global appbar across your entire app
  • Centralized isloading indicator management
  • Setting different visual states depending on screen resolution

It's very simple to do. Almost too simple. You create a class in your solution inheriting the Page class:

namespace MyApp.Base
{
public abstract class PageBase : Page
{
 protected PageBase()
 {
 // Create global appbar 
 // Hook into this.SizeChanged to set visual states depending on screen width // Create centralized loading mechanism
 }
}
}

To use the new class called PageBase you need to inherit and use that on your xaml.cs and xaml pages. Replace your code with this;

<base:PageBase
xmlns:base="using:MyApp.Base"
....

and in you code behind you should have this instead;

public sealed partial class MainPage : PageBase

That's all it takes guys! - Using his simple maneuver you can now extend the functionality as much as you like and save your self a ton of duplicated code across pages inside your app and it only takes 5 minutes to set up. 

Pro tip: You can do the exact same thing for UserControl's!

Feel free to let me know if you have any issues with the implementation.
 

Extra: More on OTF and TTF fonts in Windows Phone and Store apps

Working with custom fonts in Windows Store and Windows Phone projects requires you to know the 'full font name'. Make sure you get the name completely right, otherwise it wont work! Most of times the name in the character map tool built in to Windows works. - How ever sometimes it does not display the right one, depending on how you have installed the font.

To aviod having a TTF font not working or a OTF font not working in Windows Store or Windows Phone, I recommend using this tool http://us.fontviewer.de/download.html to the right full font name.

In case everything else fails: Remember you always enable 'copy always' for the files, and set the build properties to Content

In xaml you can create font families and reference them on your page:

<Page.Resources>
<FontFamily
x:Key="FontBook">ms-appx:////Styles/Fonts/FuturaStd-Book.otf#Futura Std</FontFamily>
<FontFamily
x:Key="FontHeavy">ms-appx:////Styles/Fonts/FuturaStd-Heavy.otf#Futura Std</FontFamily></Page.Resources>

If you are facing any problems working with fonts - let me know on twitter, and I'll help you out.