How I made the ‘myChannel9′ wp7 app (source code included) – Part 1
‘myChannel9′ is a Windows Phone 7 app for streaming of Channel9 content. It offers features like the ability to favorite a show, browse by tags or search by keyword and, of course, the ability to watch videos right on your device (in portrait and landscape mode). I participated in the Norwegian WP7 developer contest with ‘myChannel9′ and I was one of three lucky winners! The ‘myChannel9′ app is now available as a free app in the WP7 Marketplace, and the source code is available for download from codeplex (mychannel9.codeplex.com). I hope that you take a look at the code and helps making this a better Channel9-app!
Go to the bottom off this article to find a link to the source code.
The user experience
The main user experience is build up around the wp7 panorama control and consists of these 5 “blocks”:
● news
● shows
● tags (not visible in the wp7 marketplace version)
● favorites
● search
The ‘MainPage’ xaml looks like this:
<Grid x:Name="LayoutRoot" Background="Transparent">
<controls:Panorama Title=" my channel 9">
<controls:Panorama.Background>
<ImageBrush ImageSource="PanoramaBackground.png"/>
</controls:Panorama.Background>
<controls:PanoramaItem Header="news">
<Grid>
<local:Control_News x:Name="controlNews" />
<Image x:Name="btnSettings" Source="Images/logo.png" HorizontalAlignment="Left" VerticalAlignment="Top" Width="150" Height="150" Margin="-20,-290,0,0" MouseLeftButtonDown="btnSettings_MouseLeftButtonDown" />
<Image x:Name="btnRefresh" Source="Images/appbar_refresh.png" HorizontalAlignment="Right" VerticalAlignment="Top" Width="48" Height="48" Margin="0,-85,20,0" MouseLeftButtonDown="btnRefresh_MouseLeftButtonDown" />
</Grid>
</controls:PanoramaItem>
<controls:PanoramaItem Header="shows" >
<local:Control_Show x:Name="controlShow" />
</controls:PanoramaItem>
<controls:PanoramaItem Header="tags" >
<local:Control_Tags x:Name="controlTags" />
</controls:PanoramaItem>
<controls:PanoramaItem Header="favorites" >
<local:Control_Favorites x:Name="controlFavorites" />
</controls:PanoramaItem>
<controls:PanoramaItem Header="search" >
<local:Control_Search x:Name="controlSearch" />
</controls:PanoramaItem>
</controls:Panorama>
</Grid>
As you can see I use ‘user controls’ inside the panorama “blocks” to avoid to much code & xaml in the MainPage. I think this works great, except that it “destroys the “navigation-route”. If you navigate like this: this.NavigationService.Navigate(new Uri(“/Show.xaml”, UriKind.Relative)); inside a ‘user control’ then the ‘back’-button wont work correct. To fix this i use a event like this:
public event EventHandler goToVideoInfo'
inside the ‘user control’ and raise it like this:
goToVideoInfo(video, new EventArgs());
In the ‘MainPage’ I hook up to the event inside the ‘user control’ like this:
public MainPage()
{
InitializeComponent();
controlShow.goToShow += new EventHandler(controlShow_goToShow);
...
}
and the method looks like this:
void controlShow_goToShow(object sender, EventArgs e)
{
this.NavigationService.Navigate(new Uri(/VideoInfo.xaml, UriKind.Relative));
}
The ‘News’ page

The news page is basically just a RSS reader that is showing the main Channel9-feed. I use linq to “convert” the feed to ‘VideoObjects’, add them to a list, and then bind the list to a listbox like this:
public void loadNews()
{
lblLoading.Visibility = Visibility.Visible;
lboNews.Visibility = Visibility.Collapsed;
WebClient client = new WebClient();
client.DownloadStringCompleted += ReadNews;
string url = "http://channel9.msdn.com/Feeds/RSS/";
client.DownloadStringAsync(new Uri((url)));
}
private void ReadNews(object Sender, DownloadStringCompletedEventArgs e)
{
List<VideoObject> videoList = new List<VideoObject>();
try
{
if (!e.Cancelled)
{
XDocument xDoc = XDocument.Parse(e.Result);
XElement xml = XElement.Parse(e.Result);
List<VideoObject> lst = new List<VideoObject>();
var queue = from item in xDoc.Descendants("item")
select new VideoObject
{
Heading = item.Element("title").Value,
Url = item.Element("link").Value,
Description = Helper.removeHtml(item.Element("description").Value),
Posted = item.Element("pubDate").Value,
videoList = (from img in item.Elements(item.GetNamespaceOfPrefix("media") + "group").Elements(item.GetNamespaceOfPrefix("media") + "content")
select img.Attribute("url").Value).ToList(),
imageList = (from img in item.Elements(item.GetNamespaceOfPrefix("media") + "thumbnail")
select img.Attribute("url").Value).ToList(),
Lenght = "",
LenghtDetails = "",
PostedDate = Helper.getPostedDate(item.Element("pubDate").Value),
PostedDetails = Helper.getPostedDetails(item.Element("pubDate").Value),
Tags = "",
Type = "",
Favorite = false
};
videoList = queue.ToList<VideoObject>();
List<VideoObject> channel9List = new List<VideoObject>();
if (IsolatedStorageSettings.ApplicationSettings.Contains("channel9List"))
channel9List = IsolatedStorageSettings.ApplicationSettings["channel9List"] as List<VideoObject>;
else
IsolatedStorageSettings.ApplicationSettings.Add("channel9List", channel9List);
foreach (VideoObject video in videoList)
{
if (Helper.isUnique(video.Url, channel9List))
{
channel9List.Add(video);
}
}
IsolatedStorageSettings.ApplicationSettings["channel9List"] = channel9List;
}
lboNews.ItemsSource = videoList;
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
lboNews.Visibility = Visibility.Visible;
lblLoading.Visibility = Visibility.Collapsed;
}
It’s worth to mention that I save every unique videoObjects to a VideoObject-list, named ‘channel9List’, in the isolated storage. This list is among others used by the search-component (because the Channel9-site don’t offer a search-webservice). More about that late.
The ‘News’ page xaml looks like this:
<Grid>
<ListBox x:Name="lboNews" HorizontalAlignment="Left" Width="400" SelectionChanged="lboNews_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border Background="White" Margin="0,0,0,30" Width="400" Opacity="0.5" />
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Margin="0,0,20,0">
<Image Source="{ Binding getImage }" Width="180" Margin="0,0,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" />
<TextBlock Text="{ Binding PublishedDate}" Margin="10,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap" Style="{StaticResource PanoramaTextBlock}" />
</StackPanel>
<TextBlock Text="{ Binding Heading}" TextWrapping="Wrap" Margin="20,0,25,40" Style="{StaticResource PanoramaTextBlock}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock x:Name="lblLoading" Text="loading..." FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,-50,0,0" />
</Grid>
When the user selects an item in the listbox control the method ‘lboNews_SelectionChanged’ will be called. You can see the method below and what it does is to save the selected videoObject to isolated storage. Afterwards I raise the ‘goToVideoInfo’ event to navigate to the Video-page. I have chosen this way of moving data between pages, insted of the “url-parameters way”, because by using the isolated storage I can “move” a lot of data between pages easier.
private void lboNews_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
VideoObject video = lboNews.SelectedItem as VideoObject;
if (video != null)
{
if (IsolatedStorageSettings.ApplicationSettings.Contains("Video"))
IsolatedStorageSettings.ApplicationSettings["Video"] = video;
else
IsolatedStorageSettings.ApplicationSettings.Add("Video", video);
goToVideoInfo(video, new EventArgs());
lboNews.SelectedIndex = -1;
}
}
This page is quite easy to understand, it just reads a VideoObject from Isolated storage and bindes the information to a couple off textblocks and a image control like this:
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
if (IsolatedStorageSettings.ApplicationSettings.Contains("Video"))
{
video = IsolatedStorageSettings.ApplicationSettings["Video"] as VideoObject;
}
if (video != null && video.Image != "")
{
lblDate.Text = video.PostedDetails;
lblHeading.Text = video.Heading;
lblDescription.Text = video.Description;
if (video.LenghtDetails != null && video.LenghtDetails != "")
{
lblLenght.Visibility = Visibility.Visible;
lblLenght.Text = "Lenght: " + video.LenghtDetails;
}
Uri uri = new Uri(video.getImage, UriKind.Absolute);
ImageSource imgSource = new BitmapImage(uri);
img.Source = imgSource;
showCorrectFavoriteIcon();
}
}
One of the features on this page is the ability to add a video as a ‘favorite’ by clicking on the star-icon. The code looks like this:
private void btnFavorite_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
List<VideoObject> channel9List = null;
if (IsolatedStorageSettings.ApplicationSettings.Contains("channel9List"))
{
channel9List = IsolatedStorageSettings.ApplicationSettings["channel9List"] as List<VideoObject>;
if (channel9List != null)
{
if (video.Favorite)
{
channel9List.Remove(video);
video.Favorite = false;
channel9List.Add(video);
}
else
{
channel9List.Remove(video);
video.Favorite = true;
channel9List.Add(video);
}
showCorrectFavoriteIcon();
}
IsolatedStorageSettings.ApplicationSettings["channel9List"] = channel9List;
}
}
To stream the video you have to click on the play-icon in the top right corner, and the app will send you to the ‘Video’-page.
The ‘VideoInfo’ page xaml looks like this:
<Grid x:Name="LayoutRoot" Background="Black">
<Grid x:Name="InfoView">
<Image x:Name="imgBackground" Source="Images/bg.png" Height="800" Width="500" VerticalAlignment="Top" HorizontalAlignment="Left" />
<Border Background="White" Margin="0,85,0,0" Width="450" Opacity="0.5" />
<ScrollViewer Margin="15,85,15,0">
<StackPanel Orientation="Vertical" >
<Image x:Name="img" Width="450" HorizontalAlignment="Center" VerticalAlignment="Top" />
<TextBlock x:Name="lblDate" Margin="20,0,20,0" TextWrapping="Wrap" Style="{StaticResource PanoramaTextBlock}" />
<TextBlock x:Name="lblHeading" FontWeight="Bold" Margin="20,20,20,0" TextWrapping="Wrap" Style="{StaticResource PanoramaTextBlock}" />
<TextBlock x:Name="lblLenght" Visibility="Collapsed" Margin="20,20,20,0" TextWrapping="Wrap" Style="{StaticResource PanoramaTextBlock}" />
<TextBlock x:Name="lblDescription" Margin="20,20,20,0" TextWrapping="Wrap" Style="{StaticResource PanoramaTextBlock}" />
</StackPanel>
</ScrollViewer>
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="15,15,0,0" >
<Image Source="Images/imgStar.png" x:Name="btnFavorite" Width="48" Height="48" Margin="10,0,0,0" MouseLeftButtonDown="btnFavorite_MouseLeftButtonDown" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="15,15,15,0" >
<Image x:Name="btnPlay" Source="Images/imgPlay.png" Width="48" Height="48" MouseLeftButtonDown="btnPlay_MouseLeftButtonDown" />
</StackPanel>
</Grid>
This page is used to stream videos and support both portrait and landscape mode.
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
OrientationChanged += new EventHandler<OrientationChangedEventArgs>(Video_OrientationChanged);
if (IsolatedStorageSettings.ApplicationSettings.Contains("Video"))
{
video = IsolatedStorageSettings.ApplicationSettings["Video"] as VideoObject;
}
if (video != null)
{
media.Loaded += new RoutedEventHandler(media_Loaded);
media.CurrentStateChanged += new RoutedEventHandler(media_Loaded);
media.Source = new Uri(video.getVideo);
play();
showCorrectFavoriteIcon();
}
}
void Video_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
if (Orientation == PageOrientation.Landscape || Orientation == PageOrientation.LandscapeLeft || Orientation == PageOrientation.LandscapeRight)
{
mediaControl.Width = 70;
media.Margin = new Thickness(70, 0, 0, 0);
btnFavorite.Visibility = Visibility.Collapsed;
btnPlay.Visibility = Visibility.Collapsed;
}
else
{
mediaControl.Width = 0;
media.Margin = new Thickness(0, 0, 0, 0);
btnFavorite.Visibility = Visibility.Visible;
btnPlay.Visibility = Visibility.Visible;
}
showCorrectFavoriteIcon();
}
The ‘Video’ page xaml looks like this:
<Grid x:Name="LayoutRoot" Background="Black">
<MediaElement Visibility="Visible" x:Name="media" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="15,15,0,0" >
<Image Source="Images/imgStar.png" x:Name="btnFavorite" Width="48" Height="48" Margin="10,0,0,0" MouseLeftButtonDown="btnFavorite_MouseLeftButtonDown" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="15,15,15,0" >
<Image x:Name="btnPlay" Source="Images/imgPlay.png" Width="48" Height="48" MouseLeftButtonDown="btnPlay_MouseLeftButtonDown" />
</StackPanel>
<StackPanel x:Name="mediaControl" Background="Black" Width="0" VerticalAlignment="Center" HorizontalAlignment="Left" Orientation="Vertical" Margin="0,0,0,0">
<Image Source="Images/imgStar.png" x:Name="btnFavorite2" Width="48" Height="48" Margin="0,15,0,0" MouseLeftButtonDown="btnFavorite_MouseLeftButtonDown" />
<Image x:Name="btnPlay2" Source="Images/imgPlay.png" Width="48" Height="48" Margin="0,15,0,0" MouseLeftButtonDown="btnPlay_MouseLeftButtonDown" />
</StackPanel>
<TextBlock x:Name="lblLoading" Text="loading..." FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,-50,0,0" />
</Grid>
Next post
In the next post, I will go through details on how I built the ‘Show’, ‘Tags’ and ‘Search’ features. I will also list some of the coming changes and new features.
- Media « Sigurd Snørteland pingbacked on 2 years, 6 months ago
- Tweets that mention How I made the ‘myChannel9′ wp7 app (source code included) – Part 1 « Sigurd Snørteland -- Topsy.com pingbacked on 2 years, 6 months ago
- Windows Phone 7 Developer Roundup #2 for 11/05/2010 « "A" Developer`s Life pingbacked on 2 years, 6 months ago
- 1st post after migrating to WordPress~ | CactuarJ's NotePad pingbacked on 2 years, 6 months ago
- WP7 Marketplace downloads and sales data « Sigurd Snørteland pingbacked on 2 years, 5 months ago
- WP7 Marketplace downloads and sales data – one month later « Sigurd Snørteland pingbacked on 2 years, 4 months ago
- myChannel9 | WP7COMP pingbacked on 2 years, 2 months ago












Thanks! It’s a nice app.
| Posted 2 years, 6 months ago