Amazon.com Widgets

Silverlight FlickR Example

In this example I will demonstrate a very simple call to the FlickR REST APIs from a Silverlight client. At the end we will end up with an app that looks like this:

image

Part 1. Define some Silverlight UI

Part 2. Show the local open file dialog support

Part 3. Call the FlickR Service to find a picture

Part 4. Use IsolatedStorage to preserve some local settings across runs

Part 5. Skinning the UI

You are welcome to also get the completed sample, and demo files

Part 1. Define some Silverlight UI

You can go back and look at the post on my End to End Silverlight Application post for the getting started.  In Blend add a TextBox, and Button to the window and layout as shown. 

image

Be sure to give them some meaningful name in the properties window so we can refer to them pragmatically later.   Mine are called searchTermTextBox, and button. 

Drag an example image on to the window so we can have something to work with.  (You can use cow.jpg from the SilverlightFlickRDemoFiles zip)

image

Make sure you name this one as well... I used searchResultsImage

Part 2. Local Open File Dialog

Just to test out our layout, add support for popping open the open file dialog and work with the image client side.  This is something that you can't readily do in Ajax\HTML today.

In page.xaml Add a Click event handler

<Button x:Name="button" Width="100" Height="50" 
        Content="Go"
        Click="button_Click"

In page.xaml.cs implement the button click to call open file dialog.

    private void button_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog ofd = new OpenFileDialog();
        ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg | All Files (*.*)|*.*";
        ofd.FilterIndex = 1;

        if (ofd.ShowDialog() == DialogResult.OK) {
            Stream stream = ofd.SelectedFile.OpenRead();
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            searchResultsImage.Source = bi;
            stream.Close();
        }
    }

This code will open the system open file dialog allowing the user to select a file on disk.  Then the developer is given only save access to the file (that is just the bits, not hte path to the file).  Notice how we are able to work with the image client slide. 

image

image

At this point you could upload the file to a server or store it locally in Isolated Storage.   But this is a little off from where we were going with searching flickr for an image..

Part 3. Call the FlickR Service to find a picture

Now we get into the meat of it.   We need to pass our search term into FlickRs REST API and set the image to be the results.  As the user clicks on the image, we can show the next one in the results set. 

First we need to call FlickRs REST API.  To do this you need a key, which you can get for free from FlickR...

Next we need to actually call the REST API from the Silverlight client.  To do that, let's define a helper method

void LoadPhotos(string topic)
{
    string apiKey = "<<get your own >>";
    string secret = "<<get your own >>";
    string url = String.Format("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={1}&text={0}",
       topic, apiKey, secret);
    WebClient flickRService = new WebClient();
    flickRService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(flickRService_DownloadStringCompleted);
    flickRService.DownloadStringAsync(new Uri(url));
searchTermTextBox.Text = "Calling FlickR...";
}

 

Next we need to parse the results.  You can see the format of the results by looking at http://flickr.com/services/api/explore/ .  Basically, the results look like this:

<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
  <photos page="1" pages="32769" perpage="100" total="3276843">
    <photo id="2436622217" owner="22956152@N04" secret="6c8293bb5c" server="2070" farm="3" title="IMG_3492_resize" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="2437437876" owner="41848473@N00" secret="97a7e1a066" server="2303" farm="3" title="Eric & Dog" ispublic="1" isfriend="0" isfamily="0" />
  </photos>
</rsp>
  

So, we need to do a little Xml parsing.  Luckily this is very easy to do in Silverlight with LinqToXml support.   Just add a reference to the System.Xml.linq.dll  assembly.

image

Now let's implement flickRService_DownloadStringCompleted.  The first thing we need to do is a little error checking... This will help a lot making sure everything is right calling FlickR.

XDocument xmlPhotos = XDocument.Parse(e.Result);
if (e.Error != null ||
    xmlPhotos.Element("rsp").Attribute("stat").Value == "fail"){
    string results = e.Result;
    searchTermTextBox.Text= "Error! (" + results + ")";
    return;
}
else {
    searchTermTextBox.Text = "It worked!";
}

Now we just need to wire up the call to LoadPhotos.

private void button_Click(object sender, RoutedEventArgs e)
{
    LoadPhotos(searchTermTextBox.Text);
}

Run it.  If you see this, go back and check your API key.

image

When you see this, you are golden and ready for the next step!

image

Now, we need to parse the Xml results and pull out the URLs to the image.  We are going to use the magic of Linq to handle all the ugly Xml parsing.  All we need to do is to define a .NET class we want the XML elements mapped into.

public class FlickRPhoto
{
    public string Id { get; set; }
    public string Owner { get; set; }
    public string Secret { get; set; }
    public string Server { get; set; }
    public string Farm { get; set; }
    public string Title { get; set; }
}

And, let's just add another property that class which follows the FlickR URL conventions for form up a Url to the image

public string ImageUrl
{
    get
    {
        return string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg",
            Farm,Server,Id,Secret);
    }
}

Now, we need the Linq code that maps the Xml elements into this class.

Photos = from photo in xmlPhotos.Element("rsp").Element("photos").Descendants().ToList()
         select new FlickRPhoto
         {
             Id = (string)photo.Attribute("id"),
             Owner = (string)photo.Attribute("owner"),
             Secret = (string)photo.Attribute("secret"),
             Server = (string)photo.Attribute("server"),
             Farm = (string)photo.Attribute("farm"),
             Title = (string)photo.Attribute("title"),
         };

Let's define Photos as a field on this class, so we can access it later.

IEnumerable<FlickRPhoto> Photos;

Now we just need to display the image. To do that, we just grab the first record from the result set and display it! 

FlickRPhoto p = Photos.First();
this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
searchTermTextBox.Text = p.Title;

Now that is cool, but I want to see the other photos.. A quick a dirty way to do this is to change the photo when the photo is clicked.  To do this sign up for the event handler

 

<Image MouseLeftButtonDown="searchResultsImage_MouseLeftButtonDown" 
             x:Name="searchResultsImage" 

And implement it.  I  do a little error checking up front, and use a new class field called ImageNumber to keep up with here I am.

private void searchResultsImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (Photos == null) return;
    if (ImageNumber >= Photos.Count()) ImageNumber = 0;

    FlickRPhoto p = Photos.Skip(ImageNumber).First();
    this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);

    ImageNumber++;

}

Now as you click on the picture, it cycles you through the result set

image

Part 4: Use IsolatedStorage to preserve some local settings across runs

Now, let's see about preserving some of this state across runs. 

First, let's save off the results of the textbox when the "Go" button is pressed. 

private void button_Click(object sender, RoutedEventArgs e)
{
    LoadPhotos(searchTermTextBox.Text);
    ApplicationSettings.Default["searchTerm"] = txtBox.Text;
    ApplicationSettings.Default.Save();

}

and the same idea when the image changes

private void searchResultsImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (Photos == null) return;
    if (ImageNumber >= Photos.Count()) ImageNumber = 0;

    FlickRPhoto p = Photos.Skip(ImageNumber).First();
    this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
    searchTermTextBox.Text = p.Title;

    ApplicationSettings.Default["imageNumber"] = ImageNumber;
    ApplicationSettings.Default.Save();

    ImageNumber++;

}

Then when the application starts up we can pull the latest set and initialize with it. 

public Page() {
    InitializeComponent();
    if (ApplicationSettings.Default.Contains("searchTerm")){
        this.txtBox.Text = (string)ApplicationSettings.Default["searchTerm"];
        button_Click(null, null);
    }
    if (ApplicationSettings.Default.Contains("imageNumber")){
        ImageNumber = (int)ApplicationSettings.Default["imageNumber"];
    }
}

When you run it the first time, it has not saved state so it uses the default.  But run it again and notice it picks up where you left off! 

Part 5: Skin it

Now, let's give it a nice skin.  Again, I will use Corrina's Rough Skin

Just cut and paste the <ApplicationResources> section from Corrina's example into your App.Xaml

Then add the styles

<Button Style="{StaticResource buttonStyle}"
<TextBox Style="{StaticResource textBoxStyle}" 

Done!

image

You are welcome to also get the completed sample, and demo files

Update (6/19/08):  I updated this sample for Silveright Beta2... 

Published 25 April 08 11:23 by BradA
Filed under:

Comments

# Ben Hayat said on April 25, 2008 3:16 PM:

Brad, the link to "Demo Files" is broken!

..Ben

# Ben Hayat said on April 25, 2008 4:35 PM:

Very nice tutorial Brad;

It seems like the more I see codes talking to services, the comprehension gets easier!

Thank you!

..Ben

# BradA said on April 25, 2008 5:29 PM:

Thanks Ben, I got the demo files link fixed..

# SilverlightShow.net said on April 26, 2008 3:40 AM:

In this example I will demonstrate a very simpl

# 李永京 said on April 26, 2008 8:40 AM:

Silverlight 2支持JSON、Web Service、WCF以及Sockets等新特性对数据CRUD操作,这个系列用实例结合数据库一步一步的图文描述来学习一下Silverlight 2 beta 1中进行数据库的CRUD操作方面的实战能力。

# BenHayat said on April 26, 2008 2:00 PM:

Brad, as I'm looking at the following code:

string url = string.Format("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={1}&text={0}",

topic, apiKey, secret);

I don't see you passing the "secret" date as part of your URL call. Am I missing something or the secret is optional?

Thanks!

..Ben

# BenHayat said on April 26, 2008 2:03 PM:

Sorry, the string got chopped off. I try again.

string url = String.Format("http://api.flickr.com/services/rest/

?method=flickr.photos.search&api_key={1}&text={0}",

topic, apiKey, secret);

# Bill White said on April 26, 2008 11:59 PM:

Brad,

Thanks for the tutorial.  The explanation is great but the sample is not working for me here, either is VS or Blend. Blend shows a problem with a null value in the button control template, and VS  would compile and then give me a blank test page. So then I ran the test page from Windows and the control opened but when I click the button it just hangs. More later when I get some debugging time. There are a ton of good code snippets in here but it would be nice to see them running as intended. Oh well. Thanks again.

# BenHayat said on April 27, 2008 12:04 AM:

Bill, Make sure you set the TestPage.ASPX as your start page and try to run it. I saw that problem first.

But then everything runs!

..Ben

# Joycode@Ab110.com said on April 29, 2008 2:57 AM:

【原文地址】 Silverlight FlickR Example 【原文发表日期】 25 April 08 11:23 在这个示例中,我将示范在Silverlight客户端通过一个非常简单的调用去访问FlickR的REST

# XAML Templates said on April 29, 2008 2:51 PM:

Hy, at http://www.xamltemplates.net you can download a full style to all the controls for wpf.

# Mike said on June 5, 2008 8:26 AM:

Brad,

The call to flickRService.DownloadStringAsync throws a security exception since it's a cross-domain call. What's the recommended technique to make a sample like this one work, or am I missing something (wouldn't be the first time...)?

Thanks,

Mike

# MyquiH said on June 5, 2008 9:24 AM:

OK... now I know I'm missing something. Your sample code works fine, but mine does not... hmmm... Did you have to tweak something in your web server config to get the cross domain call to work? I've compared our web.config files, and looked at the ASP.NET Website Config wizards, and everything looks the same.

So confused... so confused...

Mike

# MyquiH said on June 5, 2008 4:09 PM:

Apologies for littering in this fine blog post, but I recreated my project from scratch, with the exception of copying my XAML and CS files, and it works just fine. The difference? I don't know... but one key could be that I used Blend to create my Solution/Project in the first (broken) project, and VS2008 to create the second (working) project.

*shrug*

# Brad Abrams said on June 19, 2008 9:53 AM:

I had a little time while traveling to South Africa so I thought I'd update my Silverlight FlickR demo

# Lindsay's Blog O' Stuff v1.9 said on June 27, 2008 7:30 PM:

The last part of webcast series didn't focus specifically on one facet of Silverlight 2, but rather highlighted

New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker