Desperately Seeking Love of Sophie

Part 2: Live Search using Silverlight (Live Search Call and Progress)

with 6 comments

In Part 2, I wanted to add support for communicating with Live Search APIs. These APIs are allowed to be called cross domain. The way I made sure is http://soap.search.live.com/crossdomain.xml. Here is the link to documentation of this APIs.

Getting basic service integrated is very straight forward, thanks to VS integration for web services. Right Click on References and clicked on “Add Service Reference”. It adds all the necessary client wrappers that let you call web service like any other objects in your app. I set the namespace to be SearchService so all these wrapper classes are available in Search.SearchService, Search being the root namespace for the application.

Code for talking to webservice looks something like this….

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Windows;
   5: using System.Windows.Controls;
   6: using System.Windows.Documents;
   7: using System.Windows.Input;
   8: using System.Windows.Media;
   9: using System.Windows.Media.Animation;
  10: using System.Windows.Shapes;
  11: using Search.SearchService;
  12: using System.Collections.ObjectModel;
  13: using System.ServiceModel;
  14: using System.Net;
  15: using System.Xml;
  16: using System.IO;
  17:  
  18: namespace Search
  19: {
  20:     public class SearchResultCollection : ObservableCollection<SearchService.Result>
  21:     {
  22:     }
  23:  
  24:     public partial class Page : UserControl
  25:     {
  26:         private Storyboard _progress;
  27:         private SearchResultCollection _imagesearchresultcollection;
  28:         Result[] _imageresults;
  29:         Boolean _searchcompleted;
  30:  
  31:  
  32:         public Page()
  33:         {
  34:             InitializeComponent();
  35:             _progress = LayoutRoot.FindName("SearchProgressAnimation") as Storyboard;
  36:             _progress.Completed += new EventHandler(_progress_Completed);
  37:         }
  38:  
  39:         void _progress_Completed(object sender, EventArgs e)
  40:         {
  41:             if (_searchcompleted == true)
  42:             {
  43:                 _progress.Stop();
  44:             }
  45:             else
  46:             {
  47:                 _progress.Begin();
  48:             }
  49:         }
  50:  
  51:         private void Button_Click(object sender, RoutedEventArgs e)
  52:         {
  53:             _searchcompleted = false;
  54:             _progress.Begin();
  55:             GetLiveData();
  56:         }
  57:  
  58:  
  59:  
  60:         private void GetLiveData()
  61:         {
  62:             try
  63:             {
  64:                 //Initialize web service
  65:                 string searchquery = txtSearch.Text;
  66:                 BasicHttpBinding binding = new BasicHttpBinding();
  67:                 EndpointAddress address = new EndpointAddress("http://soap.search.msn.com/webservices.asmx");
  68:                 MSNSearchPortTypeClient searchservice = new MSNSearchPortTypeClient(binding, address);
  69:                 SearchRequest searchrequest = new SearchRequest();
  70:                 SourceRequest[] sr = new SourceRequest[3];
  71:  
  72:                 sr[0] = new SourceRequest();
  73:                 sr[0].Source = SourceType.Image;
  74:                 sr[0].ResultFields = ResultFieldMask.Url | ResultFieldMask.Title | ResultFieldMask.Description | ResultFieldMask.Image;
  75:                 sr[0].Count = 50;
  76:  
  77:                 searchrequest.Query = searchquery;
  78:                 searchrequest.Requests = sr;
  79:                 searchrequest.AppID = "Enter your key here";
  80:                 searchrequest.CultureInfo = "en-US";
  81:                 searchservice.SearchCompleted += new EventHandler<Search.SearchService.SearchCompletedEventArgs>(searchservice_SearchCompleted);
  82:                 searchservice.SearchAsync(searchrequest);
  83:             }
  84:             catch (WebException webx)
  85:             {
  86:                 txtSearch.Text = webx.ToString();
  87:                 _searchcompleted = true;
  88:             }
  89:         }
  90:  
  91:         void searchservice_SearchCompleted(object sender, Search.SearchService.SearchCompletedEventArgs e)
  92:         {
  93:             if (e.Error != null)
  94:                 txtSearch.Text = e.Error.Message;
  95:             else
  96:             {
  97:                 _imagesearchresultcollection = new SearchResultCollection();
  98:                 _imageresults = e.Result.Responses[0].Results;
  99:                 for (int counter = 0; counter < _imageresults.Length; counter++)
 100:                 {
 101:                     if (_imageresults[counter].Image.ImageURL.Contains("jpg") || _imageresults[counter].Image.ImageURL.Contains("png"))
 102:                         _imagesearchresultcollection.Add(_imageresults[counter]);
 103:                 }
 104:             }
 105:             _searchcompleted = true;
 106:         }
 107:     }
 108: }

To add the progress UI, I was thinking of writing a control but then I changed my mind and I thought it would look much cooler if the brush that is background for the page can be animated (it looks like a focus so when it animates, it looks sort of like somebody is looking with a flashlight.

I have used Blend to do everything here.

<Storyboard x:Name="SearchProgressAnimation" RepeatBehavior="1x">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
                                   Storyboard.TargetProperty="(Panel.Background).

(Brush.RelativeTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" 

BeginTime="00:00:00">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="90.145"/>
        <SplineDoubleKeyFrame KeyTime="00:00:02" Value="180.143"/>
        <SplineDoubleKeyFrame KeyTime="00:00:03" Value="270.128"/>
        <SplineDoubleKeyFrame KeyTime="00:00:04" Value="359.946"/>
    </DoubleAnimationUsingKeyFrames>
    <PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse"
                                  Storyboard.TargetProperty="(Shape.Fill).(LinearGradientBrush.StartPoint)" BeginTime="00:00:00">
        <SplinePointKeyFrame KeyTime="00:00:01" Value="0.716000020503998,0.439000010490417"/>
        <SplinePointKeyFrame KeyTime="00:00:02" Value="0.382999986410141,0.684000015258789"/>
        <SplinePointKeyFrame KeyTime="00:00:03" Value="0.0759999975562096,0.231000006198883"/>
        <SplinePointKeyFrame KeyTime="00:00:04" Value="0.726000010967255,0.395999997854233"/>
    </PointAnimationUsingKeyFrames>
    <PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse"
                                  Storyboard.TargetProperty="(Shape.Fill).(LinearGradientBrush.EndPoint)" BeginTime="00:00:00">
        <SplinePointKeyFrame KeyTime="00:00:01" Value="0.0769999995827675,0.228000000119209"/>
        <SplinePointKeyFrame KeyTime="00:00:02" Value="0.409999996423721,-0.017000000923872"/>
        <SplinePointKeyFrame KeyTime="00:00:03" Value="0.717000007629395,0.435999989509583"/>
        <SplinePointKeyFrame KeyTime="00:00:04" Value="0.0670000016689301,0.270999997854233"/>
    </PointAnimationUsingKeyFrames>
</Storyboard>

Only thing I do to run this storyboard is in Button Click event, before I make a call to webservice, I start the storyboard and when call returns (or throws exception), I stop the animation. One problem I hit was I needed to run the animation atleast once otherwise when service call returns, it looks really choppy. I am not sure if there is a better way to do this but the way I ended up doing it was I kept the animation 1x and then I keep on running it till service API returns.

   1: void _progress_Completed(object sender, EventArgs e)
   2: {
   3:     if (_searchcompleted == true)
   4:     {
   5:         _progress.Stop();
   6:     }
   7:     else
   8:     {
   9:         _progress.Begin();
  10:     }
  11: }

Here is the link to working project.

Written by Vivek

March 29, 2008 at 2:13 am

Posted in Professional, Software

Tagged with

6 Responses

Subscribe to comments with RSS.

  1. [...] Silverlight Live Search [...]

  2. [...] http://vivekdalvi.wordpress.com/2008/03/29/part-2-live-search-using-silverlight-live-search-call-and…It adds all the necessary client wrappers that let you call web service like any other objects in your app. I set the namespace to be SearchService so all these wrapper classes are available in Search.SearchService, Search being the … [...]

  3. [...] http://vivekdalvi.wordpress.com/2008/03/29/part-2-live-search-using-silverlight-live-search-call-and…It adds all the necessary client wrappers that let you call web service like any other objects in your app. I set the namespace to be SearchService so all these No Annual Fee Credit Card wrapper classes are available in Search.SearchService, Search being the … [...]

  4. [...] http://vivekdalvi.wordpress.com/2008/03/29/part-2-live-search-using-silverlight-live-search-call-and…It adds all the necessary client wrappers that let you call web service like any other objects in your app. I set the namespace to be SearchService so all these ANZ Frequent Flyer wrapper classes are available in Search.SearchService, Search being the … [...]

  5. [...] http://vivekdalvi.wordpress.com/2008/03/29/part-2-live-search-using-silverlight-live-search-call-and…It adds all the necessary client wrappers that let you call web service like any other objects in your app. I set the namespace to be SearchService so all these Qantas wrapper classes are available in Search.SearchService, Search being the … [...]

  6. Hey there.. thanks for this, I was just trying to write something similar. However, I get the same thing with your code as mine… I’m everything runs, but not results come back in the e.results… any ideas?

    SnowyNomad

    May 20, 2009 at 5:06 pm


Leave a Reply