×
Namespaces

Variants
Actions

How to keep a UI element in view when scrolling a page in Windows Phone

From Nokia Developer Wiki
Jump to: navigation, search
Featured Article
01 Dec
2013

This article explains how to keep a specific UI element in view while we vertically scroll through the content on a Windows Phone page.

See Also

WP Metro Icon UI.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code ExampleTested with
SDK: Windows Phone 8.0 SDK
Devices(s): Nokia Lumia 820
Compatibility
Platform(s):
Windows Phone 8
Article
Created: Depechie (27 Aug 2013)
Last edited: kiran10182 (01 Dec 2013)

Contents

Introduction

Apps which allow the user to scroll through large amounts of content may find it useful to keep the page heading or title visible at the top, rather than allowing it to scroll off page with the rest of the content. This would be common, for example, in a newsreader app.

This article demonstrates how to implement this feature, and also how to animate the heading in the case where the page is "pulled down" to refresh content. To do this we track the scroll movement, and depending on how and where we are in this movement, we will animate another UI element to keep it at a fixed position (or not).

The images below show how this looks (there is also a video below).


Implementation

Launch Visual Studio and create a new Windows Phone application: with this you will get a default MainPage.xaml.

Open the MainPage.xaml and delete everything inside the LayoutRoot, replace it with following code:

<Grid.RowDefinitions> 
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,12">
<TextBlock Text="FIXED TITLE DEMO" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,0,0">
<ScrollViewer Name="ScrollViewer"
Margin="0,0,0,12"
VerticalAlignment="Top"
VerticalScrollBarVisibility="Visible"
ManipulationMode="Control"
MouseMove="ScrollViewer_MouseMove"
MouseLeftButtonUp="ScrollViewer_MouseLeftButtonUp">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
 
<Image Name="Image"
Source="/Assets/Images/bear.jpg"
Grid.Row="0" />
 
<RichTextBox Name="Article"
Grid.Row="1"
Margin="12,12,12,0">
<Paragraph>
<Run FontWeight="Bold" FontSize="22">Never heard before, but a bear was found in the wild!</Run>
</Paragraph>
 
<Paragraph>
<LineBreak />
<Run>
Bears are mammals of the family Ursidae. Bears are classified as caniforms, or doglike carnivorans, with the pinnipeds being their closest living relatives. Although only eight species of bears are extant, they are widespread, appearing in a wide variety of habitats throughout the Northern Hemisphere and partially in the Southern Hemisphere. Bears are found on the continents of North America, Central America, South America, Europe, and Asia.
Common characteristics of modern bears include large bodies with stocky legs, long snouts, shaggy hair, plantigrade paws with five nonretractile claws, and short tails. While the polar bear is mostly carnivorous and the giant panda feeds almost entirely on bamboo, the remaining six species are omnivorous, with varied diets.
With the exceptions of courting individuals and mothers with their young, bears are typically solitary animals. They are generally diurnal, but may be active during the night (nocturnal) or twilight (crepuscular), particularly around humans. Bears are aided by an excellent sense of smell, and despite their heavy build and awkward gait, they can run quickly and are adept climbers and swimmers. In autumn, some bear species forage large amounts of fermented fruits, which affects their behaviour.[1] Bears use shelters, such as caves and burrows, as their dens; most species occupy their dens during the winter for a long period (up to 100 days) of sleep similar to hibernation.[2]
Bears have been hunted since prehistoric times for their meat and fur. With their tremendous physical presence and charisma, they play a prominent role in the arts, mythology, and other cultural aspects of various human societies. In modern times, the bears' existence has been pressured through the encroachment on their habitats and the illegal trade of bears and bear parts, including the Asian bile bear market. The IUCN lists six bear species as vulnerable or endangered, and even least concern species, such as the brown bear, are at risk of extirpation in certain countries. The poaching and international trade of these most threatened populations are prohibited, but still ongoing.
</Run>
</Paragraph>
</RichTextBox>
</Grid>
</ScrollViewer>
 
<Border Name="TitleBorder"
Background="#FF264778"
Margin="0,216,0,0"
Height="{Binding ElementName=TitleText, Path=Height}"
VerticalAlignment="Top">
 
<TextBlock Name="TitleText"
Text="Bear found in the wild!"
Foreground="White"
Margin="12" />
</Border>
</Grid>

The XAML provided above contains an article that has enough text so that the user can scroll through the page. To have this ability we added a ScrollViewer around the article content. We left out the TitleBorder because this is the UI element that we will be animating depending on the scroll position of the page.

Note.pngNote: The XAML assumes that there is an image called bear.jpg available in the assets/images folder of your windows phone project.

An important part of this XAML is that we added the property ManipulationMode="Control" to our ScrollViewer. This was needed because otherwise the UI thread will not be notified with enough ScrollViewer scroll values to get a fluid animation – the normal mode is a performance optimization from Windows Phone that we now need to bypass!

To manipulate the location of the TitleBorder, we need to keep track of the scroll offset of our scrollbar. But the ScrollViewer itself won't give us enough info, the way to get this is by hooking into the ValueChanged event of the VerticalScrollBar that is inside the ScrollViewer.

To get hold of this VerticalScrollBar, we use the VisualTreeHelper on our ScrollViewer and hook onto the ValueChanged event when the MainPage.xaml has loaded.

_vBar = ((FrameworkElement)VisualTreeHelper.GetChild(ScrollViewer, 0)).FindName("VerticalScrollBar") as ScrollBar; 
_vBar.ValueChanged += _vBar_ValueChangedHandler;

Inside our ValueChangedHandler we will animate our TitleBorder, setting it to the corresponding vertical offset of our VerticalScrollBar. But we will stop animating it, when the vertical offset is higher than the value of the top position of our TitleBorder when the page was launched!! Because if our vertical offset has reached that value, this means that the TitleBorder will be positioned at the top of our page.

The top value of the TitleBorder is called _borderTop and is also calculated at when the MainPage.xaml has loaded.

private void _vBar_ValueChangedHandler(object sender, RoutedPropertyChangedEventArgs<double> e) 
{
if (e.NewValue < _borderTop)
{
if (e.NewValue >= 0)
this.TitleBorder.SetVerticalOffset(0 - e.NewValue);
}
else
this.TitleBorder.SetVerticalOffset(0 - _borderTop);
}

SetVerticalOffset() is an extension method on FrameworkElement, that helps positioning any UI control.

public static class Extensions
{
public static void SetVerticalOffset(this FrameworkElement fe, double offset)
{
var translateTransform = fe.RenderTransform as TranslateTransform;
if (translateTransform == null)
{
// create a new transform if one is not alreayd present
var trans = new TranslateTransform()
{
Y = offset
};
fe.RenderTransform = trans;
}
else
{
translateTransform.Y = offset;
}
}
}
 
public struct Offset
{
public double Value { get; set; }
public TranslateTransform Transform { get; set; }
}

With that in place, there is still one thing missing: what to do when the user compresses the ScrollViewer by dragging the content down when the page is at it’s initial position. This behaviour is sometimes used in listboxes to get that ‘pull to refresh effect’. When someone does this on our page, the header title will stay at it’s position while the rest is dragged down … not a nice effect.

This can be solved by hooking into 2 other events of the ScrollViewer MouseMove and MouseLeftButtonUp. MouseMove to detect the compression and MouseLeftButtonUp to know when the user stops the compression.

When the compression occurs, you'll notice a positive TranslateY value on the CompositeTransform of the ScrollViewer content, we will use this to also animate our header title. When the compression is stopped, we check if we had a positive TranslateY value and if so, we reset the position of the header title.

private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
{
UIElement scrollContent = (UIElement)this.ScrollViewer.Content;
CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;
if (ct != null && ct.TranslateY > 0)
this.TitleBorder.SetVerticalOffset(ct.TranslateY);
}
 
private void ScrollViewer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
UIElement scrollContent = (UIElement)this.ScrollViewer.Content;
CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;
if (ct != null)
{
if(ct.TranslateY > 0)
this.TitleBorder.SetVerticalOffset(0);
}
}

So with that in place you'll get a nice animation while scrolling through the content of the article!

Video demo

The media player is loading...

Downloads

A complete working solution can be downloaded here: File:FixedHeaderDemo.zip

This page was last modified on 1 December 2013, at 20:43.
684 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×