Pull to refresh for WinRT

'Pull to refresh' has been a standard UX pattern which is expected in all apps, on all platforms and I've made it available for Windows Phone 8.1 and Windows 8.1 (Universal) apps. 

The solution has been wrapped in a single control based on a ScrollViewer which is ready to use right out of the box.

<controls:PullToRefreshScrollViewer Grid.Row="1">
 ..Content goes here..
</controls:PullToRefreshScrollViewer>

Download and try the source

Pro tip: The sample works for Windows 8.1 apps as well! 

Should you need Pull to refresh for Listviews and gridviews it modifying this implementation should be fairly easy. So don't hesitate giving it a shot! 



 

Pull to refresh implementation explained

<controls:PullToRefreshScrollViewer
Grid.Row="1"
RefreshContent="PullToRefreshScrollViewer_RefreshContent"
RefreshCommand="{Binding RefreshCommand}"
ArrowColor="#004050"
RefreshText="Release to refresh"
PullText="Pull to refresh" />

This is the available properties in the control (PullToRefreshScrollViewer.cs)

  • RefreshContent (event)
  • RefreshCommand (Command)
  • ArrowColor (Brush)
  • RefreshText (string)
  • PullText (string)

The Command and event is invoked when the user pulls down and releases appropriately. 
ArrowColor is the brush of the arrow that animates once the user pulls down. During the pull there's two different texts - RefreshText and PullText
 

Deep dive

As far as I know there isn't a smooth and easy way to learn from the scrollviewer if the scrollviewer offset is negative.
After endless amounts of googling I couldn't figure out a way to do it so I decided to tweak the scrollviewer using two timers, negative margin, composite translate transform and some code to figure out whether the scrolling has a negative offset and hereby means to trigger the pull to refresh.
This solution is far from ideal (bit of a hack really) but get's the job done.


Loading the control and the timers

The control requires some timers in order to check whether the user scrolls into negative space or not. 
Note: The timers will only run as long as the scrolling offset is 0.

private void PullToRefreshScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
    timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(100);
    timer.Tick += Timer_Tick;

    compressionTimer = new DispatcherTimer();
    compressionTimer.Interval = TimeSpan.FromSeconds(1);
    compressionTimer.Tick += CompressionTimer_Tick;

    timer.Start();
}


Working with the offset

Most of the magic comes from the ScrollViewers ViewChanged event which is invoked when the user scrolls. The event is fired continuously, multiple times in the second, and it tells us about the current offset. This is event looks whether we're at the top (offset 0) or not.
If we're at the top we start some timers used to determine if the users decides to pull to refresh.

private void ScrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
    if (e.NextView.VerticalOffset == 0)
    {
        timer.Start();
    }
    else
    {
        if (timer != null)
        {
            timer.Stop();
        }

        if (compressionTimer != null)
        {
            compressionTimer.Stop();
        }

        isCompressionTimerRunning = false;
        isCompressedEnough = false;
        isReadyToRefresh = false;

        VisualStateManager.GoToState(this, VisualStateNormal, true);
    }
}

Determining pull to refresh by using the timers

The timer will tick every 100 ms and will use the TransformToVisual method to detect it's bound and compare it with the expected offset. If the user scrolls far enough and stays there for 1 second - which is checked by using another timer (compression timer) - a boolean property, IsReadyToRefresh, is set to true.
Once the offset goes back to 0 we check IsReadyToRefresh is true and invoke the refresh events to the world. 

private void Timer_Tick(object sender, object e)
{
    if (containerGrid != null)
    {
        Rect elementBounds = pullToRefreshIndicator.TransformToVisual(containerGrid).TransformBounds(new Rect(0.0, 0.0, pullToRefreshIndicator.Height, RefreshHeaderHeight));

        var compressionOffset = elementBounds.Bottom;
        Debug.WriteLine(compressionOffset);

        if (compressionOffset > offsetTreshhold)
        {
            if (isCompressionTimerRunning == false)
            {
                isCompressionTimerRunning = true;
                compressionTimer.Start();
            }

            isCompressedEnough = true;
        }
        else if (compressionOffset == 0 && isReadyToRefresh == true)
        {
            InvokeRefresh();
        }
        else
        {
            isCompressedEnough = false;
            isCompressionTimerRunning = false;
        }
    }
}

We use a visual state to change the text and rotate the arrow. If we after a second stayed long enough within the right bounds, we've set isReadyToRefresh to true and show the "release ready" visual state telling the user it will refresh once released.

private void CompressionTimer_Tick(object sender, object e)
{
    if (isCompressedEnough)
    {
        VisualStateManager.GoToState(this, VisualStateReadyToRefresh, true);
        isReadyToRefresh = true;
    }
    else
    {
        isCompressedEnough = false;
        compressionTimer.Stop();
    }
}


Telling the page and ViewModel about the refresh

The final part is telling the world that it should refresh the content. The world outside the control can listen by using an event or a command. If either is available they'll be raised once the InvokeRefresh is called and the control returns to it's original state.

private void InvokeRefresh()
{
    isReadyToRefresh = false;
    VisualStateManager.GoToState(this, VisualStateNormal, true);

    if (RefreshContent != null)
    {
        RefreshContent(this, EventArgs.Empty);
    }

    if (RefreshCommand != null && RefreshCommand.CanExecute(null) == true)
    {
        RefreshCommand.Execute(null);
    }
}

Final words

This is a quite hacky way to do it, but I couldn't find any other way when working with a scrollviewer. The solution is plug an play and works right out of the box. It get's the job done and hopefully this will be a lot simpler with Windows 10.

I'd love to hear your thoughts, improvement and feedback in the comments below - so don't hesitate writing something! 

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

Video: Increase your XAML Productivity with Blend for Visual Studio (1h 27 min)

The 26th of March I held a webtalk online on increasing the XAML productivity with Blend for Visual Studio.

Per request the webtalk was recorded and is now available.

The slides and the source code is available through this link http://sdrv.ms/ZgFMoL

The talk was hosted by the Windows Developers in Denmark usergroup.

Thanks for reading.
I hope you’ll enjoy the video. If you have any comments or feedback feel free to reach out to me in comments or on twitter @deanihansen.

Create a Resource in Expression Blend

Heres a small guide to how you get started creating resources inside Expression Blend and place it any given resource dictionary.

1.  Select a given Framework Element in the Objects and Timeline tab. In this example we will Select a TextBlock.

 

 

2. Select the Foreground property and change the color in the Color Editor inside the the Property tab.

 

 

 

 

3. Click the white square and choose: Convert to new resource.

Tip: The white square means the property have a value set locally on the page or in the control and that it's different from the default template. 

Tip: You can reset to the default template anytime by clicking the square and then click Reset.

4. Name (unique key) the resource, choose in what resource dictionary you want to define the new resource in and then click OK.

 

 

5. DONE! You have now created a new resource and applied it to your textblock.

You can see it has been added to your TextBlock by the white square you clicked before, is now green.

If you look in the XAML - the resource is now created, and your TextBlock is actually already using it by referencing it using the StaticResource.

Pro tip:
In Blend you have a tab called resources - check it out. It gives you a complete overview of all the defined resources inside your project. You can copy, delete, edit and move the resources as you like. Most people don't know about it but it offers a great overview that I use offen when ever I work with resources.. 

Pro tip 2:
The tutorial above actually works in Visual Studio's properties panel when using the designer as well.

 

Feel free to reach out to me if you have any comments or difficulties with resource creation.

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

Improve your app design

Do you want your app to be fun and cool to play around with? Get improved ratings and reviews mentioning your amazing design? Then this post is your you.

What I’ve recently noticed is that most of the apps available either on the Windows Store or Windows Phone marketplace are visually dull.
Most of the apps have great ideas with exciting and cool content but they doesn’t deviate the Visual Studio provided templates. While I've helped quite a few developers out there improving their apps and increasing their user engangement. 
While you can use the design review program offered here on the site you can also do something your self. Here's a few bullets from my list of thing people should try to avoid or do:

  • Do not use the boring black background with white text.
  • Make your app colorful
  • Use animations and transitions. Atleast those which is offered platform
  • Spend some extra time working on great store icons with the right look and feel
  • Don't just use the default templates found in Visual Studio. Make something up your self.
  • Keep your UI simple and clean, and yet tasteful.
  • Use livetiles
  • Use a splashscreen and indicators while loading content
  • Keep your app fluid and responsive
  • Use the design guidelines as guidelines at design.windows.com - not as a list of rules
  • Be creative.

Pro tip: Look at other apps you think looks great and try do what they do, but watch out. You have to own way and style. Nobody want’s a rip-off.

Creating a great design and UI is not an always easy task. It takes time.. Lots of time. Feel free to reach out if you want to discuss how you can take your design to the next level or if you have additional things you'd like to see in the post.

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