Hello there developer who was just asked to incorporate the PullToRefresh behavior into one of your ListViews for the UWP app you are building. And of course you are looking to steal code on the internet. No blaming, I do it all the time. I actually want you to use this.
In case you just crawled out of your cave and aren’t really sure what PullToRefresh is here you go.
After evaluating the few solutions that were available on the internet I realized none of them fulfilled my requirements perfectly so I had to make my own. To be exact I had to steal code and was hoping that after some tweaking and refactoring I might be able to make it work.
After shamelessly stealing great code from @dotMorten ‘s blog here RefreshBox for Windows Phone 7 as well as some interesting ideas from an otherwise mediocre Microsoft sample here XamlPullToRefresh I ended up with the code below.
P.S Scroll to the bottom for some interesting details about this solution.
The first most interesting and important detail of this solution is the trick the Microsoft sample used to bypass the classic problem of putting a ListView inside a ScollViewer which effectively kills Virtualization. Since the PullToRefresh indicator needs to be inside and above the ScrollViewer that hosts the list the same problem applies here. To overcome this
the Microsoft sample used two custom panels (an Inner and Outer) that know each other and can “talk” to each other. The reason virtualization is killed is because when a ListView is hosted inside a ScrollViewer then it thinks it has infinite vertical space because that’s what the ScrollViewer is telling it. The solution above works around this problem by allowing the Inner panel to figure out the Outer panel’s actual visible height and thus correctly calculate it’s own actual visible height which it can get by simply subtracting the size of the PullToRefresh indicator from the Outer panel’s height.
The second most interesting and important detail was figuring out if the user released their finger when the ScrollViewer was over-stretched. The limitation of the sample is that the Refresh is scheduled to be triggered once the user overstretches even if the user decides to cancel by slowly unstretching the ScrollViewer back to the original position.
The original attempts to solve this failed because ScrollViewer only exposes the DirectManipulationStarted and DirectManipulationCompleted events and the second one fires too late. Loong after the user has released their finger. The DirectManipulationPointerReleased event was not available unfortunately.
The trick to solve this turned out to be the use of the ViewChanging event (see line 114) and figuring out the exact moment the user released his/her finger from the screen.
When this occurs at the desired time then :
- The event would be Inertial.
- The ScrollViewer would be overstretched so VerticalOffset should be zero and
- The direction of the inertial movement should be upwards towards the non-stretched state.
This allowed users to change their minds if they didn’t really want to refresh.