Virtual Pivot Control

I am working on an update for Urban Dictionary 7 (one of my Windows Phone apps) and one of the pieces of feedback I got a number of times is that, when people are viewing a word, they expect to be able to slide left and right to see other words in the group they came from. So, for example, if you are looking at recent words, people expect to be able to slide left and right to see words other than the one they clicked on. Makes sense.

I started coding this feature and planned on capturing the swipe gesture, and create the appropriate animation to the “left” and “right” when people swipe, showing the images underneath. Almost started coding and then had another idea – the PivotControl essentially gives the same behavior and is something people are used to. On the other hand, I didn’t want to initialize the Pivot with all the items in the list – that would be inefficient and in some cases I can have lots of words to display (if somebody has a large “favorites” collection for example, or if someone is going through search results.

And so, here’s the idea: Create a PivotTable with 3 panes – one always showing the “current” word, the other two showing the words to the “left” and “right” of that word. Then, when a user starts swiping around the Pivot, we can adjust the other items in the list to show the items that are around our new current item.

This is best explained with an illustration.. Maybe.. The following assumes we have 4 items to display (item 0 to 3) and we always have 3 pivot panes (or PivotItem instances). If the first item we want to show is item 0, here’s what the pivot setup will look like initially:

image

So, Pivot pane 0 is actually the active one – it’s pointing to Item 0. Since that’s the first pivot pane that will show, the user will actually see Item 0. Pivot pane 1 (which visually is to the right of Pivot pane 0) will contain the next item – item 1. However, Pivot pane 2 (which is visually to the left of Pivot pane 0 since the pivot wraps) points to the last item – Item 3 (which, if you think about it, is “before” item 0). The following illustration shows how this looks visually (with Pivot pane 1 in the middle, and the other panes hidden). The arrows are still the same, I just rearranged the bottom row:

image

Now, say somebody swipes from right to left on the pivot – the Pivot functionality will slide Pivot pane 1 to be the visible one. The item on Pivot pane 1 (item 1) will become the visible item. But we now need to adjust so that the next item will actually become item 2 (since it’s the one coming after item 1). After adjustment, this is how the logical connections will look like:

image

Notice – Pivot pane 0 and Pivot pane 1 still point to the same items. But Pivot pane 2 now points to Item 2. At this stage, the pivot actually looks visually the same as that – here’s the same illustration with the “hidden” pivot panes a little transparent:

image

And as you can imagine, if you swipe again, we will re-point Pivot pane 0 to point to Item 3 and Pivot pane 2 will become the “active” Pivot pane.

In this way, we can let the user swish through information without actually having the pivot render all the info from the get go and hopefully make our apps more performant.

The base XAML is trivial – a Pivot control with 3 PivotItem instances in it:

   1: <Grid x:Name="LayoutRoot" Background="Transparent">

   2:     <!--Pivot Control-->

   3:     <controls:Pivot x:Name="pivot" Title="MY APPLICATION" SelectionChanged="Pivot_SelectionChanged">

   4:         <!--Pivot item one-->

   5:         <controls:PivotItem Header="first">

   6:             <!--Double line list with text wrapping-->

   7:         </controls:PivotItem>

   8:  

   9:         <!--Pivot item two-->

  10:         <controls:PivotItem Header="second"> 

  11:             <!--Triple line list no text wrapping-->

  12:         </controls:PivotItem>

  13:         <controls:PivotItem Header="third">

  14:             <!--Triple line list no text wrapping-->

  15:         </controls:PivotItem>

  16:     </controls:Pivot>

  17: </Grid>

The code is rather simple. For the sample code, I use an array of strings that I use to change the header of each PivotItem instance. We also need to keep track of who the last selected PivotItem is (m_lastPivotIndex) and what the current item is (m_index – which string are we showing on the visible PivotItem):

   1: public partial class MainPage : PhoneApplicationPage

   2: {

   3:     string[] items = new string[] 

   4:     {

   5:         "word one",

   6:         "word two",

   7:         "word three",

   8:         "word four",

   9:         "word five",

  10:     };

  11:  

  12:     private int m_index = 0;

  13:     private int m_lastPivotIndex = 0;

Next, we have the adjust method – this guy makes sure the 3 PivotItem instances point at the correct strings:

   1: private void Adjust()

   2: {

   3:     for (int i = -1; i <= 1; i++)

   4:     {

   5:         int wordIndex = (i + m_index + items.Length) % items.Length;

   6:         int pivotIndex = (i + m_lastPivotIndex + pivot.Items.Count) % pivot.Items.Count;

   7:  

   8:         ((PivotItem)pivot.Items[pivotIndex]).Header = items[wordIndex];

   9:     }

  10: }

All this method does is calculate and associate the PivotItem with the relevant string. We go from –1 to 0 to 1 (before current item, current item and next item) and associate that with the relevant Pivot.

Line 5: We wrap the index of the item (m_index)

Line 6: We wrap the index of the PivotItem.

Line 8: We place the correct string on the header.

Next, we need to make sure we transition correctly whenever somebody swipes (which we detect by hooking the pivot selection change):

   1: private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)

   2: {

   3:     if (pivot.SelectedIndex == m_lastPivotIndex)

   4:     {

   5:         return;

   6:     }

   7:     int i = -1;

   8:     if (pivot.SelectedIndex > m_lastPivotIndex)

   9:     {

  10:         i = 1;

  11:     }

  12:  

  13:     if (Math.Abs(pivot.SelectedIndex - m_lastPivotIndex) > 1)

  14:     {

  15:         i *= -1;

  16:     }

  17:  

  18:     m_index = (m_index + i + items.Length) % items.Length;

  19:     m_lastPivotIndex = pivot.SelectedIndex;

  20:     Adjust();

  21: }

First we make sure we really need to do work (lines 3-6). Then, we figure out in which “direction” we need to cycle our words. If i==-1 it means we are cycling back (one item backwards). Of i==1 it means we are cycling forward. Lines 7-11 determines how we advanced by using the current pivot selected item and the previous one we had. We then change direction in lines 13-16 if the pivot actually did a wrap-around.

Finally, lines 18 and 19 make sure our indices are correct and line 20 makes sure we re-adjust our pivot to show the relevant information.

That’s it! Hope this wasn’t too confusing (sure seems that way, I wish I could do a better job of explaining it),

http://cid-f5e0c616077a5eda.office.live.com/embedicon.aspx/BlogPublic/WindowsPhonePivotApplication2.zip

Advertisements
This entry was posted in Dev, Windows Phone and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s