Alessandro Del Sole's Blog

/* A programming space about Microsoft® .NET® */
posts - 143, comments - 0, trackbacks - 0

My Links

News

Your host

This is me! This space is about Microsoft® .NET® and Microsoft® Visual Basic development. Enjoy! :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.

Microsoft MVP

My MVP Profile

I'm a VB!

Watch my interview in Seattle

My new book on VB 2012

Order my book about VB 2012 on Amazon My book "Visual Basic 2012 Unleashed" is available. Click the cover!

My new book on LightSwitch!

Visual Studio LightSwitch Unleashed My book "Visual Studio LightSwitch Unleashed" is available. Click the cover!

Your visits

campusMVP.NET - Tutored online training for Microsoft developers

Follow me on Twitter!

Messenger me!


CyberInstaller Beta Tester

Download CIS 2008!!

CodePlex download Download my open-source projects from CodePlex!

Search the blog



Article Categories

Archives

Post Categories

.NET Framework

Blogroll

Help Authoring

Microsoft & MSDN

Setup & Deployment

Visual Basic 2005/2008/2010

Creating an RSS feed reader for Windows Phone 7 with Visual Basic 2010

Today is a great day for Visual Basic developers: the RTW version of developer tools for Windows Phone 7 and Visual Basic 2010 is finally available! (download links here). You can definitely develop your applications with this release and submit them to the market Place. In this blog post I'm going to describe a more complex example: creating a news reader (RSS) with VB 2010. We will take advantage of the Panorama control and of data-binding on the XAML side.

Once Visual Studio 2010 is running, create a new project and select the Windows Phone Panorama Application template from the folder named Silverlight For Windows Phone:

The template name recalls the Panorama control which allows displaying multiple contents with a single touch, going over the phisical limitations of the display in both ways. Once the project is ready, Visual Studio 2010 looks familiar if you already tried the tools for Visual C# or the VB CTP: 

On the left side you get the designer and on the right side you get the XAML code editor. By default a Panorama control is added; this is made of several PanoramaItem controls, each representing a page of the container and that is enabled when you touch the display. The project template populates the control with some data exposed by an architecture based on the MVVM pattern. This is evident if you browse the solution: you can find a folder named SampleData which exposes design-time data and another folder named ViewModels, which exposes "bridges" between the UI and the data:

Using this pattern requires a good knowledge of the pattern itself, so we don't use it in this post (not necessarily all the readers know about MVVM). So the goal is creating a news reader based on RSS feeds. The application will aggregate information exposed by the Visual Basic Developer Center from Microsoft and will display the following categories:

  • latest news
  • latest technical articles
  • latest "How-do-I" videos
  • latest threads in the VB IDE Forum

To accomplish this we will use LINQ to XML and asynchronous downloading techniques. First, we need to add a reference to the System.Xml.Linq assembly (in any kind of Silverlight-based project this is not available by default); next, we need a class that maps a single news exposed by the feeds. We can call this class FeedItem and the following is its code:

Public Class FeedItem
 
    'Content's title
    Public Property Title As String
    'Date of publication
    Public Property pubDate As String
    'Page link
    Public Property Link As Uri
 
End Class

Before discussing the UI and the data-binding, it's a good idea writing the code that is responsible for downloading feed contents. As it usually happens in Silverlight applications, this must be done in an asynchronous fashion via the WebClient class. In the MainPage.xaml.vb code-behind file let's write the following method:

    'Reads RSS contents from the given Uri and assigns the result to the specified PanoramaItem
    Private Sub ReadContentsRss(ByVal rssUri As UriByVal panoramaElement As PanoramaItem)
        'The WebClient class allows async downloading
        Dim wclient As New WebClient()
 
 
        'The statement lambda is a replacement for AddressOf
        AddHandler wclient.OpenReadCompleted, Sub(sender As Object, e As OpenReadCompletedEventArgs)
                                                  If e.Error IsNot Nothing Then
                                                      MessageBox.Show(e.Error.Message)
                                                      Exit Sub
                                                  End If
 
                                                  Dim str As IO.Stream = e.Result
                                                  Dim xdoc As XDocument = XDocument.Load(str)
 
                                                  ' take results from each item element in the XML feed
                                                  Dim rssFeedsItems = (From item In xdoc...<item>
                                                                       Let published = CStr(CDate(item.<pubDate>.Value).ToLocalTime)
                                                                       Let title = item.<title>.Value
                                                                       Let postLink = item.<link>.Value
                                                                       Select New FeedItem With {.Link = New Uri(postLink, UriKind.Absolute),
                                                                                                 .pubDate = published,
                                                                                                 .Title = title}).ToList
 
                                                  ' close
                                                  str.Close()
                                                  ' add results to listbox
                                                  panoramaElement.DataContext = rssFeedsItems
                                              End Sub
        wclient.OpenReadAsync(rssUri)
    End Sub

Notice how the code uses a statement lambda instead of an AddressOf clause. Basically the code invokes the WebClient.OpenReadAsync method; when the OpenReadCompleted event is raised, the code first check if any error occurred. In no error was found, it loads the feed into a stream (which is stored inside e.Result). Next, the LINQ query retrieves all the elements named item, each representing a news post, and stores its information in istances of the FeedItem class, for each item got. Once done this, we can load the feed when the MainPage.Loaded event is raised:

    Private Sub MainPage_Loaded(ByVal sender As ObjectByVal e As RoutedEventArgsHandles Me.Loaded
        'VB Highlights feed
        ReadContentsRss(New Uri("http://services.social.microsoft.com/feeds/feed/VB_featured_resources"),
                        ContentsItem)
        'How-do-I videos feed
        ReadContentsRss(New Uri("http://www.microsoft.com/feeds/msdn/en-us/vbasic/HDI-vbasic.xml"),
                        VideosItem)
        'Articles feed
        ReadContentsRss(New Uri("http://services.community.microsoft.com/feeds/feed/query/tag/article/eq/tag/visual%20basic/eq/and/locale/en-us/eq/and/namespace/F76D3EB9-73E1-4810-8C2D-0B42FEC930E7/eq/and"),
                        ArticlesItem)
        'VB IDE Forum feed
        ReadContentsRss(New Uri("http://social.msdn.microsoft.com/Forums/en-US/vbide/threads?outputAs=rss"),
                        ForumsItem)
    End Sub

Now let's switch to the User Interface in the MainPage.xaml file. First, let's edit the title in the Panorama control:

        <!--Panorama control-->
        <controls:Panorama Title="VB Dev Center">

Secondly let's remove all the <controls:PanoramaItem/> elements added by default. Next we can add our first custom PanoramaItem, which points to the VB Highlights:

            <controls:PanoramaItem Header="VB Highlights" x:Name="ContentsItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <!-- ListBox template for each item-->
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}" 
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>

How does it work? By taking advantage of default styles, the first TextBlock gets the news' title and binds it to the Text property, whereas the Tag property is bound to the link of the Web page containing the post (this will be used in a few moments). The second TextBlock is data-bound to the publication date. The collection of news is assigned at runtime via the ReadContentRss method shown before. Once you understand this mechanism, adding new PanoramaItem controls is easy:

            <controls:PanoramaItem Header="'How do I' videos" x:Name="VideosItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}" 
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>
            <controls:PanoramaItem Header="Articles" x:Name="ArticlesItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}" 
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>
            <controls:PanoramaItem Header="VB IDE Forum" x:Name="ForumsItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}"
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>

Now imagine that our user must have the possibility of displaying a preview of contents, inside a WebBroser control. Since we used TextBlock controls, when the user touches the screen a MouseLeftButtonUp event is raised. It is handled as follows:

    Private Sub TextBlock_MouseLeftButtonUp(ByVal sender As System.Object,
                                            ByVal e As System.Windows.Input.MouseButtonEventArgs)
        Dim senderButton = CType(sender, TextBlock)
        NavigationService.Navigate(New Uri("/BrowserPage.xaml?Content=" + senderButton.Tag.ToString,
                                           UriKind.Relative))
    End Sub

The code simply converts the sender into a TextBlock, and then it builds a query string that invokes a new page named BrowserPage.xaml (it will be created later); the query string is built adding the content of the Tag property from the TextBlock (as I mentioned before, this stores the link to the content). The NavigationService.Navigate method opens the page by taking advantage of the Navigation Framework. Now let's add (Project|Add New Item) a new page of type Windows Phone Portrait Page. We can replace the SupportedOrientations property in the XAML code like this:

SupportedOrientations="PortraitOrLandscape"

At this point we can change the default title and add a new WebBrowser control by dragging it from the toolbox onto the designer:

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="PageTitle" Text="Anteprima" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <phone:WebBrowser Name="Web1" />
        </Grid>

At the code-behind level, we need to retrieve the value of the query string passed by the main page, then obtain the link and pass this to the WebBrowser. Here is some code:

    Private Sub BrowserPage_Loaded(ByVal sender As Object,
                                   ByVal e As System.Windows.RoutedEventArgsHandles MyBase.Loaded
        Try
            Dim contentSource As String = ""
            NavigationContext.QueryString.TryGetValue("Content", contentSource)
            Web1.Navigate(New Uri(contentSource))
 
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    End Sub

The NavigationContext class exposes the QueryString.TryGetValue method which allows reading the parameter of the string and its value; the latter is converted into an Uri and passed to the WebBrowser control. If we now try to run the application we will get the following result:

Then we can try to open up some contents and see how they are displayed in the preview page.

Download the code

You can download the code for the sample application here.

Final considerations

Don't forget to read the guide lines to build the UI in Windows Phone 7 apps or documentation about other important topics like the tombstoning. There is a number of possible improvements to the sample application described here, such as using the application bar (e.g. for implementing a button that invokes a contents refresh). You can find complete and more complex examples in a page from Microsoft that contains lots of examples in both Visual Basic and C#.

Enjoy this RTW!

Alessandro

Print | posted on lunedì 29 novembre 2010 20:20 |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 8 and 6 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET