Part 2: Live Search using Silverlight (Live Search Call and Progress)
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.
[...] Silverlight Live Search [...]
Chris Love's Official Blog - Professional ASP.NET : Links of the Week
March 30, 2008 at 1:55 am
[...] 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 … [...]
Wrapper Counters and the rest of the world » Blog Archive » What others have been saying about wrapper counter
March 30, 2008 at 6:43 pm
[...] 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 … [...]
Wrapper Counters and the rest of the world » Blog Archive » Quick Roundup
April 2, 2008 at 3:50 pm
[...] 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 … [...]
Wrapper Counters and the rest of the world » Blog Archive » Quick scan of the net - wrapper counter
April 3, 2008 at 3:56 pm
[...] 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 … [...]
Wrapper Counters and the rest of the world » Blog Archive » ‘wrapper counter’ on the web
April 5, 2008 at 3:52 pm
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