Share via


Exercise 2: Introduction to Pivot Control

In this exercise we continue to develop the Wazup application. At the end of this exercise the Wazup application will allow us to select a trend from the trends page and see a list of tweets (Twitter messages) relevant to this trend, in addition to what we've accomplished in the previous exercise.

The exercise will include the following tasks:

  • Add Twitter page that uses a Pivot control
  • Implement Twitter page functionality

Task 1 – Adding Twitter Page

  1. In this task we will add a placeholder page for Twitter functionality. The page will contain a pivot control that can display several pages of information. We will use the pivot control on the next task to show a page of tweets for each trend in the trends list.
  2. During this task we will see how to add a new page that contains a pivot control.
  3. Open Microsoft Visual Studio 2010 Express for Windows Phone or Visual Studio 2010.
  4. Open the Begin.sln starter solution from the Source\Ex2-Pivot\Begin folder of this lab. Alternatively, you can continue working on the solution created in previous exercise.
  5. Inside the folder Helpers, add to the LinkLabel folder all the files from the Source\Assets\Helpers\LinkLabel folder of this lab. These should include Link.cs, LinkClickedEventArgs.cs, LinkCollection.cs, LinkLabel.cs and LinkMatchMethod.cs, which we are going to use in this exercise.
  6. Inside the folder Helpers, add to the WrapPanel folder all the files from the Source\Assets\Helpers\WrapPanel folder of this lab. These should include LengthConverter.cs, NumericExtensions.cs, OrientedSize.cs, TypeConverters.cs and WrapPanel.cs.
  7. Open the file TwitterPage.xaml.cs under the folder Views.
  8. Add to the TwitterPage class two public static variables that will be used to pass information between the TrendsPage and TwitterPage.

    C#

    /// <summary> /// Used to transfer the current Trend between the trends page and the twitter page /// </summary> public static Trend Global_CurrentTrend;

  9. Open the file TrendsPage.xaml.cs.
  10. Now we want to open the Twitter page when a trend gets clicked on the trends page.To do that we will go back and implement the MouseLeftButtonUp event handler of the TextBlockTrend control. Our implementation will pass the trends collection and selected trend to the Twitter page by using static variables on the Twitter page. Replace the TextBlockTrend event handler of the MouseLeftButtonUp event:

    C#

    /// <summary> /// Handles the MouseLeftButtonUp event of the TextBlockTrend control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param> private void TextBlockTrend_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Trend trend = (sender as TextBlock).DataContext as Trend;

    this.GoToPage(ApplicationPages.Twitter);
    
    TwitterPage.Global_Trends = Trends;
    TwitterPage.Global_CurrentTrend = trend;
    

    }

  11. Compile and run the application.
  12. Stop the debugging and return to the code. This step concludes the current task.

Task 2 – Implementing Twitter Page Functionality

In this task we will implement the functionality of the Twitter page in the Wazup application. At the end of this task the Wazup application will have a working Twitter page.

During this task we will use the Pivot control to show multiple pages of data, one page for each trend in the trends list. Each page in the pivot control will show a list of tweets relevant to the page trend. We will also see how to handle changes in the pivot selected page. When the pivot selected page will change we will load the current twits for the new page.

  1. Open the file TwitterPage.xaml
  2. Add the following XML namespaces to the phone:PhoneApplicationPage element next to the other XML namespaces definitions:

    XAML

    xmlns:localHelpers="clr-namespace:Wazup.Helpers" xmlns:localWindowsControls="clr-namespace:System.Windows.Controls"

  3. Set the DataContext property of the TwitterPage to be the TwitterPage itself. Do this by adding the following line to the phone:PhoneApplicationPage element:

    XAML

    DataContext="{Binding RelativeSource={RelativeSource Self}}"

  4. Now we will define the UI for the Twitter page. Locate the LayoutRoot grid and replace the content with the code below. The Twitter page contains two controls: a Pivot control that will show a different page for each trend and a ProgressBarWithText control that will show status information while loading tweets. The Pivot control is used to show multiple pages which are easily navigated by flicking or panning horizontally on the page. When you reach the last page of the pivot you can flick once more to return to the first page. The Pivot control items are set via the property ItemsSource which is bound to a collection of Trend items. To transform each Trend object into a valid UI page, we use the ItemTemplate property, which defines the name of the DataTemplate that will perform the transformation. We can also control the look of the page header using the HeaderTemplate property to define the name of the DataTemplate that will perform the transformation between a Trend object and the UI for the header. We will define the DataTemplate in the next step.

    XAML

    <Grid x:Name="LayoutRoot" Background="Transparent"> <controls:Pivot x:Name="PivotControl" Title="Wazup" ItemsSource="{Binding Trends}" HeaderTemplate="{StaticResource TrendHeaderTemplate}" ItemTemplate="{StaticResource TrendTemplate}" SelectionChanged="PivotControl_SelectionChanged" > </controls:Pivot> <localHelpers:ProgressBarWithText Text="Loading Tweets..." ShowProgress="{Binding IsTwitsLoading}" /> </Grid>

  5. Here we define three data templates:
    • TrendHeaderTemplate defines a DataTemplate that transforms a Trend object into a TextBlock that contains the trend name. This template is used as the header for each item in the Pivot control.
    • TrendTemplate defines a DataTemplate that transforms a Trend object into the page UI for each item in the Pivot control. The page UI consists of a single ListBox control that contains the Twitter messages related to the trend. The UI for the ListBox items is defined by the third data template named TwitTemplate.
    • TwitTemplate defines a DataTemplate that transforms a Twit object into a proper UI presentation of a Twitter message that includes an Image for showing the profile image of the tweet author, a LinkLabel control that shows the tweet text with clickable links and two TextBlocks that show the tweet author name and tweet creation time.

    Add the following code into the TwitterPage resources section.

    XAML

    <phone:PhoneApplicationPage.Resources> <DataTemplate x:Key="TrendHeaderTemplate"> <TextBlock Text="{Binding name}" /> </DataTemplate> <DataTemplate x:Key="TwitTemplate"> <Grid Margin="0,0,12,25"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Image Grid.RowSpan="2" Width="60" Height="60" Source="{Binding profile_image_url}" VerticalAlignment="Top" Margin="0,5,7,0" /> <localWindowsControls:LinkLabel Grid.Row="0" Grid.Column="1" Text="{Binding DecodedText}" LinkMatchMethod="ByUriAndLinkPattern" LinkStyle="{StaticResource WrappedHyperlinkButtonStyle}" VerticalAlignment="Top" Foreground="White" Background="Black" FontSize="20" /> <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal"> <TextBlock Text="{Binding from_user}" Foreground="#99FFFFFF" VerticalAlignment="Top" /> <TextBlock Text=", " Foreground="#99FFFFFF" VerticalAlignment="Top" /> <TextBlock Text="{Binding created_at}" Foreground="#99FFFFFF" VerticalAlignment="Top" /> </StackPanel> </Grid> </DataTemplate> <DataTemplate x:Key="TrendTemplate"> <ListBox ItemsSource="{Binding Twits}" ItemTemplate="{StaticResource TwitTemplate}" /> </DataTemplate> </phone:PhoneApplicationPage.Resources>

  6. We now add buttons to the application bar in the Twitter page. After the LayoutRoot grid ends, instead of the commented use of PhoneApplicationPage.ApplicationBar, add the following code:

    XAML

    <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton x:Name="AppbarButtonDigg" IconUri="/Resources/App.Digg.png" Text="Digg" Click="AppbarButtonDigg_Click" /> <shell:ApplicationBarIconButton x:Name="AppbarButtonTwitter" IconUri="/Resources/App.Twitter.png" Text="Twitter" Click="AppbarButtonTwitter_Click" /> <shell:ApplicationBarIconButton x:Name="AppbarButtonBlog" IconUri="/Resources/App.Blog.png" Text="Blog" Click="AppbarButtonBlog_Click" /> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>

  7. Open the file TwitterPage.xaml.cs
  8. Add the following dependency properties to the TwitterPage class:
    • Trends of type ObservableCollection<Trend> - used to save the collection of Twitter trends
    • CurrentTrend of type Trend – used to save the currently selected trend
      Note:
      We implement an OnCurrentTrendChanged function in order to set the PivotControl.SelectedItem property with the current trend. The reason why we don't do this by binding the SelectedItem property to the CurrentTrend property is due to a bug in the current version of the Pivot control for setting a new page that is "far" from the current page (not a neighbor) that might result in an exception. Our code works around this.

  9. IsTwitsLoading of type bool – used to indicate whether Twitter messages loading is currently in progress

    C#

    #region Trends

    /// <summary> /// Trends Dependency Property /// </summary> public static readonly DependencyProperty TrendsProperty = DependencyProperty.Register("Trends", typeof(ObservableCollection<Trend>), typeof(TwitterPage), new PropertyMetadata((ObservableCollection<Trend>)null));

    /// <summary> /// Gets or sets the Trends property. This dependency property /// indicates what are the current twitter trends. /// </summary> public ObservableCollection<Trend> Trends { get { return (ObservableCollection<Trend>)GetValue(TrendsProperty); } set { SetValue(TrendsProperty, value); } }

    #endregion

    #region CurrentTrend

    /// <summary> /// CurrentTrend Dependency Property /// </summary> public static readonly DependencyProperty CurrentTrendProperty = DependencyProperty.Register("CurrentTrend", typeof(Trend), typeof(TwitterPage), new PropertyMetadata((Trend)null, new PropertyChangedCallback(OnCurrentTrendChanged)));

    /// <summary> /// Gets or sets the CurrentTrend property. This dependency property /// indicates what is the current trend. /// </summary> public Trend CurrentTrend { get { return (Trend)GetValue(CurrentTrendProperty); } set { SetValue(CurrentTrendProperty, value); } }

    /// <summary> /// Handles changes to the CurrentTrend property. /// </summary> private static void OnCurrentTrendChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TwitterPage target = (TwitterPage)d; Trend oldCurrentTrend = (Trend)e.OldValue; Trend newCurrentTrend = target.CurrentTrend; target.OnCurrentTrendChanged(oldCurrentTrend, newCurrentTrend); } /// <summary> /// Provides derived classes an opportunity to handle changes to the CurrentTrend property. /// </summary> protected virtual void OnCurrentTrendChanged(Trend oldCurrentTrend, Trend newCurrentTrend) { if (newCurrentTrend != oldCurrentTrend) { Dispatcher.BeginInvoke((Action)delegate { PivotControl.SelectedItem = newCurrentTrend; }); } }

    #endregion

    #region IsTwitsLoading

    /// <summary> /// IsTwitsLoading Dependency Property /// </summary> public static readonly DependencyProperty IsTwitsLoadingProperty = DependencyProperty.Register("IsTwitsLoading", typeof(bool), typeof(TwitterPage), new PropertyMetadata((bool)false));

    /// <summary> /// Gets or sets the IsTwitsLoading property. This dependency property /// indicates whether we are currently loading twits. /// </summary> public bool IsTwitsLoading { get { return (bool)GetValue(IsTwitsLoadingProperty); } set { SetValue(IsTwitsLoadingProperty, value); } }

    #endregion

  10. Add a handler for the SelectionChanged event of the Pivot control. Here we respond to a page change in the Pivot control by searching for Twitter messages relevant to the new selected page. We do this by calling the asynchronous method TwitterService.Search that accepts a search text, a callback function to call when the Twitter search is complete, and a callback function to call if the Twitter search failed, for example, the Twitter service is not available. If the Twitter search completed successfully, we add the tweets to the Twits collection of the current trend, which is bound to the page ListBox.

    C#

    private void PivotControl_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.AddedItems.Count == 0) { return; }

    Trend selectedTrend = e.AddedItems[0] as Trend;
    
    if ((selectedTrend.Twits == null) || (selectedTrend.Twits.Count == 0))
    {
        IsTwitsLoading = true;
    }
    TwitterService.Search(selectedTrend.name,
        delegate(IEnumerable&lt;Twit&gt; twits)
        {
            IsTwitsLoading = false;
    
            selectedTrend.Twits = new ObservableCollection&lt;Twit&gt;();
    
            foreach (Twit twit in twits)
            {
                selectedTrend.Twits.Add(twit);
            }
        },
        delegate(Exception exception)
        {
            IsTwitsLoading = false;
        });
    

    }

    Override the functions OnNavigateTo and OnNavigateFrom of the page. Use the StateManager class to save and load page state when the current page is changing. In the function OnNavigateTo, check whether the Global_Trends variable has a value; if it has, we know that the Twitter page was loaded after clicking a trend in the trends page.

  11. Otherwise, we assume that the Twitter page got loaded due to a return to the application via the Back button, in which case we load the Trends information from the state object.

    C#

    private const string TrendsKey = "TrendsKey"; private const string CurrentTrendKey = "CurrentTrendKey";

    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) { this.SaveState(TrendsKey, Trends); this.SaveState(CurrentTrendKey, CurrentTrend); }

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { if (Global_Trends != null) { // get data from trends page, if we got from trends page Trends = Global_Trends; CurrentTrend = Global_CurrentTrend; } else { // get data from state object Trends = this.LoadState<ObservableCollection<Trend>>(TrendsKey); CurrentTrend = this.LoadState<Trend>(CurrentTrendKey); } }

  12. Add the following event handlers in the TwitterPage class. In this step we handle the application bar buttons. In each event handler we use the Navigation class to go to the correct page. Note that the blog page doesn't exist yet; it will be created later in this lab.

    C#

    #region Appbar handlers

    /// <summary> /// Handles the Click event of the AppbarButtonDigg control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> private void AppbarButtonDigg_Click(object sender, EventArgs e) { this.GoToPage(ApplicationPages.Digg); }

    /// <summary> /// Handles the Click event of the AppbarButtonTwitter control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> private void AppbarButtonTwitter_Click(object sender, EventArgs e) { NavigationService.GoBack(); //this.GoToPage(ApplicationPages.Trends); }

    /// <summary> /// Handles the Click event of the AppbarButtonBlog control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> private void AppbarButtonBlog_Click(object sender, EventArgs e) { this.GoToPage(ApplicationPages.Blog); }

    #endregion

  13. Compile and run the application.

    At this stage the Twitter page should work and look like the following:

    Figure 5

    Twitter page showing tweets for a specific trend

  14. Stop the debugging and return to the code. This step concludes the exercise.

    During this exercise you learned how to use the Pivot control to show multiple pages.

    Note:
    Note: The complete solution for this exercise is provided at the following location: Source\Ex2-Pivot\End.