Creating a Parallax Scrolling Effect on Windows Phone

tl;dr: Here’s a commented sample project if you’re in a hurry

Windows Phone’s scrollviews have a property where if you scroll past the content area, the content is squished. Nokia’s App Social has a cool but subtle effect that takes advantage of this. If you’re looking at a recommendation list within the app and try pulling the list down, you’ll notice that the header image doesn’t scroll with the rest of the list. Instead, it scrolls slower than everything else, as well as expands to fill the space made by the squished scrollview.

If you’ve ever played with an iOS device, you should know this effect well, as it’s present in so many apps on that platform. My favorite example is probably within Day One, an awesome diary app, where within your diary entries you can set a header photo that behaves similarly.

Creating this effect on Windows Phone is certainly tricky for a variety of reasons—in fact, if you play around with App Social’s implementation, you might even catch a few glitches—but with a bit of work you can hack together something like this:

Let’s get to work! Open up VS and create a new blank WP8 project. This might work in WP7 (or even WP8.1!) but I haven’t tested it.

Ordering Voice Commands on the “Did You Know?” Page

Just a small tip for a small problem I ran into.

If your WP app has voice commands defined, you’ll get a “Did You Know?” page where the user can see examples of all of the voice commands your app has. If you have a lot of voice commands available, it might get a bit messy.

What’s more confusing is that the order of the commands on this page probably won’t match up with the order you defined them in your VCD file.

It turns out that they’re displayed in alphabetical order, with the key being the Name attribute of your Command object, which means that you can adjust your names to order them how you want, perhaps by prepending them with letters or numbers.

In my particular case, that means that my commands would go from

1
2
3
4
<Command Name="SearchPokemon" />
<Command Name="SearchMoves" />
<Command Name="SearchTypes" />
<Command Name="SearchDualTypes" />

to the following with numbered prefixes

1
2
3
4
<Command Name="00_SearchPokemon" />
<Command Name="01_SearchMoves" />
<Command Name="02_SearchTypes" />
<Command Name="03_SearchDualTypes" />

If you plan on inserting more commands later on, you’ll have to rename older ones to get the order you want, so I’d recommend using string.Contains("base-name-of-command") rather than equality checks in your code so that even if the prefix changes, your code will still work without having to do a million find-and-replaces.

Fixing the Long List Selector’s Scrollbar Spacing Issue

Windows Phone’s Long List Selector has an annoying problem. Its scrollbar takes up actual horizontal space on the layout grid, pushing your list items to the left. Now this normally isn’t a problem; you can just offset your list items accordingly. The problem though is that if your list ever has too few elements such that scrolling is not necessary, the scrollbar’s Visibility will be set to Collapsed and no longer take up any horizontal space, meaning that your offsets will cause your items to stick too far off of the right edge.

Here is a simple Style that you can add to a Resources file (or just a phone:PhoneApplicationPage.Resources block) that will solve this problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<Style x:Key="LLSFloatingScrollbarStyle"
               TargetType="phone:LongListSelector">
    <Setter Property="Background"
            Value="Transparent" />
    <Setter Property="Foreground"
            Value="{StaticResource PhoneForegroundBrush}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="phone:LongListSelector">
                <Grid Background="{TemplateBinding Background}"
                      d:DesignWidth="480"
                      d:DesignHeight="800">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="ScrollStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="00:00:00.5" />
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Scrolling">
                                <Storyboard>
                                    <DoubleAnimation Duration="0"
                                                     To="1"
                                                     Storyboard.TargetProperty="Opacity"
                                                     Storyboard.TargetName="VerticalScrollBar" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="NotScrolling" />
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid Margin="{TemplateBinding Padding}">

                        <ViewportControl x:Name="ViewportControl"
                                         HorizontalContentAlignment="Stretch"
                                         VerticalAlignment="Top" />

                        <ScrollBar x:Name="VerticalScrollBar"
                                   Margin="4,0,-12,0"
                                   Opacity="0"
                                   HorizontalAlignment="Right"
                                   Orientation="Vertical" />
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- then on your long list selector... -->

<phone:LongListSelector Style="{StaticResource LLSFloatingScrollbarStyle}" />

What this will do is cause the scrollbar to float on top of the long list selector instead of taking up horizontal space, which means that it will never push the list’s content to the left. A note though: take heed of the Margin attribute of the Scrollbar tag. You may need to adjust this to get the scrollbar to go to where you want. I personally like my scrollbars flush against the right side of the screen.

Rendering Image Sources With the Nokia Imaging SDK

Edit: After some searching, I’ve found the Nokia-Certified™ method of rendering to a BitmapImage/ImageSource.

First of all, Image controls can surprisingly take a WriteableBitmap as their source. You can do this:

1
BindedImageSource = new WriteableBitmap(1024,768);

and everything will be fine.

Second, remember how IEditingSession has a RenderToWriteableBitmap() method?

1
2
3
4
5
6
7
var myWBP = new WriteableBitmap(1024, 768);

await _session.RenderToWriteableBitmapAsync(myWBP);

myWBP.Invalidate(); // forces a redraw

BindedImageSource = myWBP;

An interesting side-note is that RenderToWriteableBitmap appears to be shorthand for

1
2
var bitmap = myWBP.AsBitmap();
await _session.RenderToBitmapAsync(bitmap);

bitmap in this example is not a typical Bitmap, but a mysterious object that wraps a WriteableBitmap’s pixel data. That means that changes to this bitmap affect the original WriteableBitmap. Now extracting it isn’t very helpful in this small example, but if you check out the code for Nokia’s Filter Effects sample (spefically checkout the AbstractFilter class) you can do some pretty neat things without having to create a new WriteableBitmap each time.

I’ll keep the original post below for context.


So I was working on integrating the Nokia Imaging SDK for my journal app, Sofa, for the Nokia Create contest, essentially letting the user crop and enhance their photos before attaching them to an entry.

I found myself using the SDK in my ViewModel and wondering how I was supposed to get the rendered image into my View. The SDK itself provides methods for rendering to JPEG, raw Bitmaps, WriteableBitmaps, and even Image controls. The Image controls option (renderToImageAsync()) sounded like the way to go, but I realized that in the VM I don’t have access to the Image control in my view. Instead, I have access to a BitmapImage that binds to the Source property of the Image control. So how do you get a BitmapImage out of the editing session?

Well after some searching I found that you can coax a BitmapImage out of a WriteableBitmap. Here’s an extension method that lets you do exactly that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static async Task<BitmapImage> RenderToBitmapImageAsync(this IEditingSession session, int width, int height, OutputOption outputOption = OutputOption.PreserveAspectRatio)
{
  var bi = new BitmapImage();

  var wbp = new WriteableBitmap(width, height);
  await session.RenderToWriteableBitmapAsync(wbp, outputOption);

  using (var ms = new MemoryStream())
  {
    wbp.SaveJpeg(ms, width, height, 0, 100);
    bi.SetSource(ms);
  }

  // not super necessary
  wbp = null;

  return bi;
}

Just put this into a static class (perhaps an Extensions.cs?) and then call

1
myBindedImage = await _myEditSession.RenderToBitmapImageAsync(someWidth, someHeight, outputOptionIfYouWant);

Note that there may be memory issues with WriteableBitmaps, so be wary of calling this often, especially with high widths and heights. You may need to force garbage collection after a few calls.

Because of that, I’m definitely not 100% keen on this method. It definitely works (wait for Sofa v1.1 to prove that) but I’ll be on the lookout for less memory-intensive options.

Sofa Is Done!

It’s been almost two months now, but Sofa, my journal application for Windows Phone 8, is finally done!

First of all, the last time I referred to Sofa I called it ‘Quill’. That was its work-in-progress name, but I had to change it because I couldn’t find a nice quill vector to use as the main logo. No problem, Sofa was actually how I referred to the app within the code anyway, and it’s still a nice title. Maybe I’ll use Quill for something else…

This is easily the biggest programming project (hell, maybe even general project) that I’ve worked on, and I’ve learned a ton since I started. I now have a much better grip on Git, and by that I mean I know more than git commit -am "Did stuff" and git push origin master. I learned more about branching, merging, stashing (ran into a nasty problem with that), and am currently learning about rebasing because I have way too many one-line-changed commits.

I’ve also learned a lot more about the tools and libraries I’ve been using in my small WP dev ‘career’. From the Windows Phone Toolkit (which I ended up having to fork to fix a bug), to MVVMLight (minimal code-behind, I promise!), to new tools like SQLite and Microsoft’s LiveSDK (for SkyDrive integration, need those bullet-point features).

I think I’ve also learned to finally accept a v1 product—there are tons of features that I want for Sofa, but I needed to realize that if I spend way too much time trying to perfect it, I’ll fall into my trap of getting frustrated, getting bored, and then giving up on it altogether (I have enough half-finished VS solutions to show the effects of this).

The last week and a half have been spent with some awesome beta testers who have been extremely helpful in finding bugs, little quirks, and feature suggestions. I am definitely having beta periods for all of my serious apps from now on.

Sofa has been in the approval process for the last two days and you wouldn’t believe how nervous I am. Every morning I wake up, scramble to my phone, and falter, looking at that mail icon with a number next to it. I’ve gotten “Certification Failed” for my other apps before (Dammit Pinned, get along with the budget phones!), and I really don’t want that to happen now. There’s two weeks until I have to move back to Southern California for college, and I have no idea what kind of free time I’ll have once that happens.

Wish me luck!

My Multiline Textbox Configuration

I’m still working on Quill, but I figured this construct was useful enough for me to save in its purest form, before I uglify it with orientation hacks.

This is an implementation of a properly-behaving multiline TextBox for Windows Phone. The original implementation was sourced from KlingDigital. I just made extremely minor modifications, mostly refactoring to make it easier to modify (adding an input scope, supporting landscape, etc).

Here is the XAML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<phone:PhoneApplicationPage
    x:Class="Autocomplete.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True"
    Loaded="PhoneApplicationPage_Loaded">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <ScrollViewer x:Name="Scroller"
                      Background="#1ba1e2"
                      Grid.Row="0">
            <StackPanel VerticalAlignment="Top">
                <TextBox x:Name="ScrollerInput"
                         TextWrapping="Wrap"
                         AcceptsReturn="True"
                         TextChanged="ScrollerInput_TextChanged"
                         GotFocus="ScrollerInput_GotFocus"
                         LostFocus="ScrollerInput_LostFocus"
                         Tap="ScrollerInput_Tap"/>
            </StackPanel>
        </ScrollViewer>

        <Grid x:Name="KeyboardPlaceholder"
              Grid.Row="1"
              Visibility="Collapsed" />
    </Grid>

</phone:PhoneApplicationPage>

and here is the codebehind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using Microsoft.Phone.Controls;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Autocomplete
{
  public partial class MainPage : PhoneApplicationPage
  {
    private const int PortraitHeight = 336;

    private double _inputHeight = 0;
    private double _tapHeight = 0;

    // Constructor
    public MainPage()
    {
      InitializeComponent();
    }

    private void SizePlaceholder()
    {
      KeyboardPlaceholder.Height = PortraitHeight;
    }

    private void ForceLayout()
    {
      App.RootFrame.RenderTransform = new CompositeTransform();

      LayoutRoot.UpdateLayout();
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {

    }

    private void ScrollerInput_TextChanged(object sender, TextChangedEventArgs e)
    {
      // updates seem to be late if we don't invoke them with the dispatcher
      Dispatcher.BeginInvoke(() =>
      {
        double currentInputHeight = ScrollerInput.ActualHeight;

        if (currentInputHeight > _inputHeight)
        {
          // scroll up by the difference between the current box size and the old box size
          Scroller.ScrollToVerticalOffset(Scroller.VerticalOffset + currentInputHeight - _inputHeight);
        }

        _inputHeight = currentInputHeight;
      });
    }

    private void ScrollerInput_GotFocus(object sender, RoutedEventArgs e)
    {
      KeyboardPlaceholder.Visibility = Visibility.Visible;
      SizePlaceholder();

      ForceLayout();

      Scroller.ScrollToVerticalOffset(_tapHeight);
    }

    private void ScrollerInput_LostFocus(object sender, RoutedEventArgs e)
    {
      KeyboardPlaceholder.Visibility = Visibility.Collapsed;
    }

    private void ScrollerInput_Tap(object sender, System.Windows.Input.GestureEventArgs e)
    {
      _tapHeight = e.GetPosition(ScrollerInput).Y - 120;
    }
  }
}

Hopefully this template will prove useful in the future.

Fixing Alignment Issues With DataTemplateSelector

I’m currently working on a journaling application for WP8. While designing the page for browsing through past entries, I found that I needed to be able to switch between data templates for the LongListSelector housed in the page. Some journal entries would have just text, some would have only a header photo, and some would have both, and I needed to display the entries differently for all three cases.

I found this article describing how to recreate WPF’s DataTemplateSelector class for Windows Phone. It worked fine—after remaking the base abstract class and extending it for my specific case, all you need to do is specify the different data templates in your xaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<DataTemplate x:Key="EntryListItemTemplate">
  <sel:EntryItemTemplateSelector>
    <sel:EntryItemTemplateSelector.Both>
      <DataTemplate> <!-- content here --> </DataTemplate>
    </sel:EntryItemTemplateSelector.Both>

    <sel:EntryItemTemplateSelector.OnlyImage>
      <DataTemplate> <!-- content here --> </DataTemplate>
    </sel:EntryItemTemplateSelector.OnlyImage>

    <sel:EntryItemTemplateSelector.OnlyText>
      <DataTemplate> <!-- content here --> </DataTemplate>
    </sel:EntryItemTemplateSelector.OnlyText>
  </sel:EntryItemTemplateSelector>
</DataTemplate>

A bit verbose, but it got the job done thankfully. The LongListSelector would now switch between templates as required.

There was a bit of a problem though.

Mordecai has the right idea.

All of the elements in the LongListSelector were no longer listening to the standard alignment and sizing rules, instead being only as wide as their content and staying put in the center.

I tried adjusting margins, hardcoding widths, HorizontalAlignment=Stretch everything, and either it wouldn’t work, or it would make things even worse.

After a lot of vague googling (what exactly would you call this problem? I honestly had no idea), I arrove at the correct solution. My EntryItemTemplateSelector, inheriting from ContentControl, has a new property named HorizontalContentAlignment, which sets the alignment of the control’s content rather than the control itself. So all you have to do then is

1
<sel:EntryItemTemplateSelector HorizontalContentAlignment="Stretch">

which gets you

Nothing satisfies Mordecai though.

Testing Code Highlighting

Let’s see how syntax-highlighting works with this.

Here is some Lua code for a Love2D-based game:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
-- mapper.lua
-- loads the maps and draws them to the screen

-- requires
local tileLoader = require("libs.advtileloader.Loader")

-- constants
local TOTAL_LEVELS = 1

-- local vars
local function buildMapName(index)
  return "level_" .. index .. ".tmx"
end

-- module
local Mapper =
{
  currentLevelIndex = 1,
  maps = {}
}

function Mapper.draw()
  love.graphics.setColor(255,255,255) -- apparently we need to do this to prevent tint
  Mapper.maps[Mapper.currentLevelIndex]:draw()
end

function Mapper.getCurrentMap()
  return maps[currentLevelIndex]
end

-- init and return
local function init()
  tileLoader.path = "maps/"

  for i=1, TOTAL_LEVELS do
    local map = tileLoader.load(buildMapName(i))

    map:setDrawRange(0,0,love.graphics.getWidth(), love.graphics.getHeight())
    map.drawObjects = false

    Mapper.maps[i] = map
  end

  return Mapper
end

return init()

And that’s it.

This Is a Beginning

Let us see where it brings us. Right now the site theme is pretty ehh, and I have to configure my actual domain to point to this.