Tuesday, March 25, 2014

Using ScrollViewer in Windows Store Apps - Determining Scrollable Contents

In this post, we will be trying to discuss options available to determine whether ScrollViewer's contents are partially hidden. The options would be looked at from a Windows Store App perspective. But they are quite generic and should be easily usable in other XAML based platforms including WPF and Silverlight and Windows Phone applications.

Let's first add an image to our project. We would be using pan and zoom with this image to understand how we can determine whether we have any horizontal and vertical scrollable content. Here we are setting the Build Action as 'Content'. This would allow us to use ms-appx:// Uri syntax to reference the image. In those cases, we would be showing some content in the application. They would be just TextBlocks showing details of available scrollable contents.

ComputedHorizontalScrollbarVisibility and ComputedVerticalScrollbarVisibility
As we know we can use HorizontalScrollbarVisibility and VerticalScrollbarVisibility to set whether and when scrollbars should be available. They are both of type ScrollbarVisibility. The possible values are Auto, Disabled, Visible and Hidden. The values are irrespective of whether the scrollbars are currently displayed or not.

ScrollViewer provides another set of properties to determine whether scroll bars are currently displayed or not. It must be remembered that this is irrespective of whether we have any available scrollable regions. They are read-only properties, and named as ComputedHorizontalScrollbarVisibility and ComputedVerticalScrollbarVisibility. They are useful when the options to set the visibility are set as Auto.

In the following example we are adding an image to ScrollViewer. Since we need Panning and Zooming, we have enabled the ZoomMode for the ScrollViewer. Here we are using ms-appx:// Uri syntax to reference the image we just added. You can see that we are adding two TextBlocks, one on the top and other on the left side. We are binding the visibility of the Borders containing these TextBlocks to ComputedHorizontalScrollBarVisibility and ComputedVerticalScrollbarVisibility. This would show them only when their corresponding scrollbars are visible.

Now let's run the App and see how these controls are displayed as the horizontal and vertical scrollbars are shown.

Using Extent, Viewport & Scrollable Height and Width Properties
The computed properties to determine the visibility of scrollbars work great but they we need to set the Visibilty of scrollbars to Auto for them to work. In most of the touch scenarios, we don't have this luxury available. For such cases, we can directly use other properties to determine if we have any scrollable contents available. These properties are ExtentWidth, ViewportWidth, ScrollableWidth and the Height counterparts. The values for these properties are calculated as follows:

Now for a WPF application, this can be a lot easier as we have DataTriggers for Style. We can use them to perform certain operations on the screen as ScrollableProperty ( ExtentProperty - ViewPortProperty) > 0. But we can still use IValueConverter to convert and bind them to Visibility for the Border controls used in the previous example. It must be remembered it is not the same WPF interface as Convert and ConvertBack signatures are different. It is also in Windows.UI.Xaml.Data namespace instead of WPF's System.Windows.Data namespace. Let's introduce an IValueConverter. The converter returns Visibility.Visible when the bounded value is greater than zero. It returns Visibility.Collapsed otherwise.

Let' use the converter defined above for our purpose. Notice the xmlns:ns="using:NAMESPACEsyntax for namespace declaration in Windows Store apps compared to xmlns:ns=clr-namespace:NAMESPACE. This is closer to the C# code namespace declarations.

This behaves exactly the same way as in the previous example. If this were a WPF application, we also had some extra options to control the visibility of these Border controls. These options are as follows:
  1. Style Data Triggers: We can set these borders as Collapsed by default. We can then set Visibility to Visible when ScrollableProperty > 0.

  2. Using Blend SDK Conditional Behavior: We can use Blend SDK's Interaction triggers. We can use ScrollChanged event from ScrollViewer. The SDK conditional behaviors allow us to use ConditionalExpression. We can use GreaterThan expression to determine if ExtentProperty is greater than ViewPortProperty. We can the use ChangePropertyAction to set the visibility of these border controls.
Download Code

No comments: