Click here to Skip to main content
Click here to Skip to main content

Using YouTube API 1.9.0.0 with MVVM-Light-Toolkit

By , 28 Feb 2012
Rate this:
Please Sign up or sign in to vote.

Unbenannt-1.jpg

Introduction

Hi, this is my first article for codeproject, hope it will be useful!
It's quite a simple YouTubeViewer, I needed to go my own way coding with YouTubeAPI and MVVM.
Anyway I'm using the MVVM design pattern with MVVM Light Toolkit, so this Article
should support you to understand how MVVM and the YouTube API works.

What is MVVM and why should I use it?

The goal of MVVM is to seperate your UI from Code. So when you're finished and there's absolutly no code behind in your main window, you're doing the right way.
Now you (or the UI Designer) are able to skin the GUI and make modifications without grappeling
with code.

We're only working with bindings to view data and using Commands to add/remove/update/... data.

In WPF there are several ways to store data. For MVVM you should use ObservableCollection<> because as the name implies, we need to "observe" the collection if collection has changed.
Observable provides notifications for adding/removing/updating data, so we can notify the UI
that something hast changed.

Binding/Commands

WPF Controls are able to bind a DataContext, in this example we simply "bind" our ViewModel
to the Main Window.
Collection Controls like ListView's, Listbox, Combobox delievers the "ItemsSource" attribute to bind the ObservableCollection to the Control.
The RaisePropertyChanged is used to notify the UI that data has changed.
RelayCommand and the implemented ICommand are used to bind commands to the UI Controls, many controls supports this.

MVVM Rules/Advanteges

It's quite a while elapsed since learning MVVM, I've googled for "wpf mvvm" and learned it from below article where some funny basic rules are listed. (Glad to bookmarked his article)

Here's the link, hope that the referring compensates my pirate copy.

http://ahaageeks.blogspot.com/2010/03/mvvm-model-view-viewmodel.html

“No code in the code behind.”

“If you have code in your View’s code behind, MVVM Police is going to take your family away.”

There is something called “Code Behind For a Reason”. But what I see the problems is that it is not that easy to test your Views if there are code in your code behind.

“No need for Converters.”

Sometimes you might need to use Converters, but I would say it is LESS use in MVVM.

“MVVM is only suitable for BIG projects.”

It is suitable in lot of cases that we can gain advantages of Data Binding.

“MVVM Costs Performance.”

Well, I would say Developer Performance since it requires some skills to understand the Patter and you have to do some additional coding such as Commands etc. But I think the Data Bindings are very optimized. You mileage may vary.

“MVVM is complex. Makes my head hurt”

Well, its nothing but a Pattern. The rest of the stuff are helpers and if your head hurts, it probably becoz of Last Night Smile | :)

“I dont need separation coz I dont have a designer.”

I think separation is about maintainability, and testability other than blend-ability.

“You cannot use MVVM with other patterns.”

Well, Why Not. You can use in a User Control with MVVM, and use this User Control in any other places.

“MVVM will make you rich and attractive.”

Using the code

Requirements:

YouTube SDK 1.9.0.0 from http://code.google.com/p/google-gdata/downloads/list

MVVM Light Toolkit from http://mvvmlight.codeplex.com/

Adobe Flash Player installed

Microsoft Expression Blend SDK

Namespaced needed:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Google.GData.Client;
using Google.YouTube;  

Working with the YouTube API

First we need to authenticate our Application. You can create a developer key within your google account.

QueryClientLoginToken returns a string if User credentials are valid, otherwise it will throw an exception. For the case if the User suffers under amnesia and is trying to log in with wrong credentials several times, we also catch the CaptchaRequiredException.

YouTubeService service = new YouTubeService("APP_NAME", "DEVELOPER_KEY");
service.setUserCredentials("USERNAME","PASSWORD");
            
try
{
     service.QueryClientLoginToken();    
}
catch (Exception ex)
{
        if (ex is Google.GData.Client.CaptchaRequiredException || ex is Google.GData.Client.InvalidCredentialsException)
        {           
             Console.WriteLine(ex.Message.ToString());     
        }
        else
        {
             Console.WriteLine(ex.Message.ToString());
        }
}   

Now we're able to call a new request to get YouTube Video Feeds, Playlists, ...

try
{       
YouTubeRequestSettings settings = new  YouTubeRequestSettings("APP_NAME", "DEVELOPER_KEY", "YouTube_USERNAME", "YouTube_PASSWORD");
      if (settings != null)
      reqeuest = new YouTubeRequest(settings);
}
catch (Exception ex)
{
      if (ex is Google.GData.Client.CaptchaRequiredException || ex is Google.GData.Client.InvalidCredentialsException || ex is GDataRequestException)
      {
          Console.WriteLine(ex.Message.ToString());
      }
      else
      {
          Console.WriteLine(ex.Message.ToString());
      }
}  

To store general VideoFeed data we need our ObservableCollection, for this sample I've created a second Collection to load VideoComments after a video is selected in the ListView (I thought about the performance, so comments will be loaded only if a video is selected)

private ObservableCollection<you_tube_data> _detail_list = new ObservableCollection<you_tube_data>();
private ObservableCollection<string> _video_comments = new ObservableCollection<string>(); 

Additional class to store the video details.

public class you_tube_data
{
            public string video_title { get; set; }
            public string video_thumb_url { get; set; }
            public string video_summary { get; set; }
            public string video_id { get; set; }
            public string video_uploader { get; set; }
            public string video_view_count { get; set; }
            public string video_rating { get; set; }
            public string video_description { get; set; }
}

Let's search for videos and store video details in the ObservableCollection.

try
{
         string uriSearchTerm = System.Web.HttpUtility.UrlEncode(SearchTerm);
         detail_list.Clear();
         Request();
         //Get video feed for your search term
         Feed<Video> vid_feed = reqeuest.Get<Video>(new Uri("https://gdata.youtube.com/feeds/api/videos?q=" + uriSearchTerm + "&key=DEVELOPER_KEY"));
         //save video details to observable collection
         foreach (Video entry in vid_feed.Entries)
         {
              detail_list.Add(new you_tube_data {video_description=entry.Description, video_uploader = "Uploader: " + entry.Uploader, video_view_count = "Views: " + entry.ViewCount.ToString(), video_title = entry.Title, video_summary = entry.Description, video_thumb_url = entry.Media.Thumbnails[0].Url, video_id = entry.VideoId });
         }
}
catch (Exception ex)
{
         Console.WriteLine(ex.Message.ToString());
}

Navigate to the next/previous 25 Entries.

To show the next 25 feeds, we take the URL and our seachterm and add 25 to &start-index=INDEX. Per default YouTube returns 25 feeds.

You can imagine how to load previous entries. Right, just subtract it from index value.

In addition we had to clear our Collection and fill it with new entries.

try
{
       string uriSearchTerm = System.Web.HttpUtility.UrlEncode(SearchTerm);
       detail_list.Clear();
       Request();
       //Get video feed for your search term
       index+=25;
       Feed<Video> vid_feed = reqeuest.Get<Video>(new Uri("https://gdata.youtube.com/feeds/api/videos?q=" + uriSearchTerm + "&key=DEVELOPER_KEY" + "&start-index=" + index.ToString()));
       //save video details to observable collection 
       foreach (Video entry in vid_feed.Entries)
       {
           detail_list.Add(new you_tube_data {video_description=entry.Description, video_uploader = "Uploader: " + entry.Uploader, video_view_count = "Views: " + entry.ViewCount.ToString(), video_title = entry.Title, video_summary = entry.Description, video_thumb_url = entry.Media.Thumbnails[0].Url, video_id = entry.VideoId });
       }
}
catch (Exception ex)
{
      Console.WriteLine(ex.Message.ToString());
}

Customize the WebBrowser Control

Here we have to face with a small problem because the WebBrowser Control is quite an old Win32 Component so binding the URL to the Control is not possible per default.

So let's create a customized WebBrowser UserControl.

The XAML looks usual:

<Grid>
<WebBrowser x:Name="browser" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" />
</Grid>

Code Behind:

As I said, the goal of our customized WebBrowser UserControl is to bind the YouTube Video-URL to the Webbrowser.

So we're doing the following:

1. Create a new DependencyProperty

2. If the property is changed (in our case when the user selects a video and new VideoID is "loaded"), Browse() is called.

3. Display(string url) is called an we send the default YouTube-URL + our VideoID with it.

4. We create a new simple HTML-Page which only contains the Video as Shockwave Flash Player.

5. Sorry for the perhaps slightly confusing expressions (GetYouTubeScript_playlist i.e), it will be used for playlist autoplaying in future.

private static readonly DependencyProperty HtmlTextProperty = DependencyProperty.Register("HtmlText", typeof(string), typeof(CustomWebbrowserControl));

public string HtmlText
        {
            get { return (string)GetValue(HtmlTextProperty); }
            set { SetValue(HtmlTextProperty, value); }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == HtmlTextProperty)
            {
                Browse();
            }
        }

        private void Browse()
        {
            if (!string.IsNullOrEmpty(HtmlText))
            {
                Display("http://www.youtube.com/watch?v=" + HtmlText);
            }
        }

        private void Display(string url)
        {
            Match m = YouTubeURLIDRegex.Match(url);
            String id = m.Groups["v"].Value;
            string page =
            "<!DOCTYPE html PUBLIC \" -//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" >\r\n"
            + @"<!-- saved from url=(0022)http://www.youtube.com -->" + "\r\n"
            + "<html><body scroll=\"no\" leftmargin=\"0px\" topmargin=\"0px\" marginwidth=\"0px\" marginheight=\"0px\" >" + "\r\n"
            + GetYouTubeScript_playlist(id)
            + "</body></html>\r\n";

            browser.NavigateToString(page);
          
        }

        private string GetYouTubeScript_playlist(string id)
        {
            string scr = "";
            scr = @"<object width='400' height='300'> " + "\r\n";
            scr = scr + @"<param name='movie' value='http://www.youtube.com/v/" + HtmlText + "?version=3" + "'></param> " + "\r\n";
            scr = scr + @"<param name='allowFullScreen' value='false'></param> " + "\r\n";
            scr = scr + @"<param name='allowscriptaccess' value='always'></param> " + "\r\n";
            scr = scr + @"<embed src='http://www.youtube.com/v/" + HtmlText + "?version=3" + "' ";
            scr = scr + @"type='application/x-shockwave-flash' allowscriptaccess='always' ";
            scr = scr + @"allowfullscreen='false' width='400' height='300'> " + "\r\n";
            scr = scr + @"</embed></object>" + "\r\n";
           
            return scr;
        }

    } 

To use it in our MainWindow.xaml we add the UserControl and bind the VideoID in our HtmlText Property.

<mw:CustomWebbrowserControl Width="Auto" Height="300" HtmlText="{Binding VideoID,Mode=OneWay}"></mw:CustomWebbrowserControl>

Add SelectionChangedEvent to ListView

To comply to the conditions of MVVM the default SelectionChangedEvent is useless for us.

The Windows.System.Interactivity namespace contains an EventTrigger and we could use

the EventToCommand Action to notify our ViewModel that SelectionChanged Event is fired.

This is usually indended for Expression Blend but it's well working in XAML directly, you don't need to install Blend, the DLL-File could be found in the BlendSDK.

 <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <cmd:EventToCommand Command="{Binding Path=ControlListView, Mode=OneWay}" CommandParameter="{Binding Path=SelectedItem, ElementName=controlListView}" PassEventArgsToCommand="True"/>
                    </i:EventTrigger>
</i:Interaction.Triggers> 

In our XAML, the Command is binded to a ICommand interface in our ViewModel to Handle the action, creating a new RelayCommand and send it as object to our SelectionChangedEvent-Function.

public ICommand ControlListView
{
      get
     {
           return _cntrolListView ?? (_cntrolListView = new RelayCommand<object>(SelectionChangedEvent)); 
     }
}

private void SelectionChangedEvent(object param)
{
            try
            {
                SelectionChangedEventArgs e = (SelectionChangedEventArgs)param;
                //check if at least one object is "loaded" 
                if (e.AddedItems.Count > 0)
                    selected_data = (you_tube_data)e.AddedItems[0];
            }
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }

            try
            {
                //Get Comments for selected video
                Video video = reqeuest.Retrieve<Video>(new Uri("http://gdata.youtube.com/feeds/api/videos/" + selected_data.video_id));
                Feed<Comment> comment = reqeuest.GetComments(video);
                VideoComments.Clear();
                int comments_count = 0;
                foreach (Comment c in comment.Entries)
                {
                    //show only ten comments
                    if (comments_count == 10)
                        break;
                    if (c.Content != null)
                    {
                        VideoComments.Add(c.Content);
                        comments_count++;
                    }
                }
            }
            catch (GDataRequestException ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }

            try
            {
                if (selected_data.video_id != null)
                    VideoID = selected_data.video_id;
                if (selected_data.video_summary != null)
                    VideoSummary = selected_data.video_summary;
                if (selected_data.video_thumb_url != null)
                    VideoThumbURL = selected_data.video_thumb_url;
                if (selected_data.video_title != null)
                    VideoTitle = selected_data.video_title;

                RaisePropertyChanged("VideoComments");
                RaisePropertyChanged("VideoID");
                RaisePropertyChanged("VideoSummary");
                RaisePropertyChanged("VideoThumbURL");
                RaisePropertyChanged("VideoURL");
                RaisePropertyChanged("VideoTitle");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
           
}


Open a new Window/inform MainViewModel with Messenger

We would like to receive our YouTube-Playlists in a new Window.

If User selects a playlist in that new Window, we had to inform our MainViewModel to get the Video Feeds of the selected playlist.

First, we open up the new Window:

<Button Command="{Binding ShowPlaylists}" Content="My Playlists" Width="75" Background="Transparent" Foreground="White"></Button>

In MainViewModel, we add the new RelayCommand "ShowPlaylists":

ShowPlaylists = new RelayCommand(OpenPlayListWindow); 

We also have to Register a new Action with the MVVM-Light Messenger, we pass a string with it which contains the name of our selected playlist:

Messenger.Default.Register<String>(this, (action) => onReceiveMessage(action));

When our MainViewModel receives a messege, following method will be executed to receive video feeds of that playlist:

private object onReceiveMessage(string Playlist)
        {
            Feed<Playlist> pl_feeds = YouTube_Helper.reqeuest.GetPlaylistsFeed("USERNAME");
            if (pl_feeds.Entries != null)
            {
                try
                {
                    string thumb_url = "";
                    detail_list.Clear();
                    foreach (Playlist pl in pl_feeds.Entries)
                    {
                        if (pl.Title == Playlist)
                        {
                            Feed<PlayListMember> video_list = YouTube_Helper.reqeuest.GetPlaylist(pl);
                            
                                foreach (Video entry in video_list.Entries)
                                {
                                    foreach (MediaThumbnail thumbnail in entry.Thumbnails)
                                    {
                                        thumb_url = thumbnail.Url;
                                    }
                                    detail_list.Add(new you_tube_data { video_description = entry.Description, video_uploader = "Uploader: " + entry.Uploader, video_view_count = "Views: " + entry.ViewCount.ToString(), video_title = entry.Title, video_summary = entry.Description, video_thumb_url = entry.Media.Thumbnails[0].Url, video_id = entry.VideoId });

                                }
                            
                        }
                    }

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
            return null;
        }

In our PlaylistViewModel, we need our SelectionChangedEvent again to inform the MainViewModel:

private void SelectionChangedEvent(object param)
        {
            SelectionChangedEventArgs e = (SelectionChangedEventArgs)param;
            //check if at lease one object parameter is "loaded" 
            if (e.AddedItems.Count > 0)
                selected_data = (playlist_data)e.AddedItems[0];
            Messenger.Default.Send<String>(selected_data.pl_title);  

        }

History

The first version of my first (but not the last) article on Codeproject.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

El_Codero
Software Developer
Germany Germany
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberSergio Andrés Gutiérrez Rojas27-Feb-12 9:26 
GeneralRe: My vote of 5 PinmemberBjörn Ranft27-Feb-12 9:30 
GeneralMy vote of 4 PinmemberDean Oliver27-Feb-12 8:13 
GeneralRe: My vote of 4 [modified] PinmemberBjörn Ranft27-Feb-12 8:21 
QuestionMessage Automatically Removed Pingroupghjtyktyk15-Feb-12 15:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 28 Feb 2012
Article Copyright 2012 by El_Codero
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid