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.

Slide in menu control for Windows Phone

One of the most used menu navigational patterns right now is the hamburger icon / slide in menu.

The pattern is nothing something I'm particular fond of. Mainly because the icon doesn't look good, the icon takes up space and it's highly overused.
I've always felt the same way about the appbar. It doesn't look good but it's effective.

I've thought for some days how the concept two concepts could be merged together effectively and while having the full benefits of making it look good and maintaining the vertical scroll functionality. 

The solution is a two sided hidden menu wrapped in a easy to use control.
I present to you: 
SlideInMenuContentControl.
It contains one menu for navigation and one for context.

The control is free to use and available for download.

 

Download source and demo


 

 

Download source with buttons

UPDATE - SAMPLE WITH BUTTONS TO TRIGGER MENU
I've added a sample with buttons to slide out the menu's! Important: The trigger buttons need ManipulationMode="All" in order to keep the focus away from the flipvew. If the ManipulationMode isn't set the flipview keeps focus and the SelectedIndex will get reset.


Using the SlideInMenuContentControl

Using the control is simple. Simply replace the ". . ." with your xaml. 

  <local:SlideInMenuContentControl>
        <local:SlideInMenuContentControl.LeftMenuContent>
            <Grid>
                   ...
            </Grid>
        </local:SlideInMenuContentControl.LeftMenuContent>
        <local:SlideInMenuContentControl.RightMenuContent>
            <Grid>
                   ...
            </Grid>
        </local:SlideInMenuContentControl.RightMenuContent>
        <local:SlideInMenuContentControl.Content>
            <Grid>
                   ...
            </Grid>
        </local:SlideInMenuContentControl.Content>
    </local:SlideInMenuContentControl>


SlideInMenuContentControl configuration options:

Menu state (Both, Right, Left): Property to determine which sides of the menu should be visible in. Default is both.
RightSideMenuWidth: Property to determine the width of the slidein. Less than 200 is not recommended. Default is 250.0 px.
LeftSideMenuWidth: Property to determine the width of the slidein. Less than 200 is not recommended. Default is 250.0 px.
GoToMenuState(ActiveState state): Method for forcing the control to go to a specific menu.


A huge shout out to my friend Daniel Vistisen for helping and providing technical insights about building custom controls. He's one of those guys who can built anything in xaml and C#.
He's one to follow for sure! 

Feel free to tell me about your experience using the control and ask if you have any questions. 

P.S Make sure you follow me on twitter @deanihansen for more controls, 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);
        }
    }
}