At Eggheadcafe.com we're working on a Silverlight Makeover for the site, and this
gives us the opportunity to experiment with various concepts and ideas along
the way. Recently I found the "BingSharp" Bing API Wrapper at Codeplex, and decided to port it to Silverlight. The port was
extremely easy, all I had to do was create a Silverlight Class Library project
and bring in all the .cs code files. One minor change to a signature, and I was
done. Well, almost: BingSharp was not written with Silverlight's asynchronous-only
http calls in mind. However, with some minor modifications, all the code is eminently
usable. Here is an example using the "Web" search query, which is the
one I needed:
private void GetBingResults()
{
SearchRequest
searchRequest = new SearchRequest { AppId = API_KEY, Query = App.SearchTerm, Market = "en-US" };
Bing.WebRequest
webRequest = new Bing.WebRequest();
webRequest.Count
= 50; // Required by the search that's all they give you!
string requestString = API.Web(searchRequest, webRequest);
WebClient
wc = new WebClient();
wc.DownloadStringCompleted
+= new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
wc.DownloadStringAsync(new Uri(requestString));
}
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string response = e.Result;
// XElement.Load wants a stream:
MemoryStream
ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(response));
XElement
element = XElement.Load(ms);
Bing.WebResponse
wr = Bing.WebResponse.ParseElement(element);
this.itemsList.DataContext = wr.Results;
}
The above code uses the API Wrapper's API.Web method, which won't work for Silverlight,
so I modified it as follows:
public static string Web(SearchRequest searchRequest, WebRequest request)
{
searchRequest.Sources
= new[] { ApiHelper.SourceType.Web };
searchRequest.Version
= "2.1";
string requestString = searchRequest.ToQueryString() + request.ToQueryString();
return requestString;
// XElement element = XElement.Load(requestString);
// return WebResponse.ParseElement(element);
}
The return type was originally "WebResponse", but now you would handle
that in the callback. If you decide to use this API wrapper library, you will
probably find you need to make similar adjustments for all the other methods.
To check for a referer from a search engine link and grab the search term, I use
the following code in App.Xaml.cs:
private void Application_Startup(object sender, StartupEventArgs e)
{
string referer = Convert.ToString(HtmlPage.Document.GetProperty("referrer"));
// if referer is empty or is coming from our site, don't want it.
if (referer == "" || referer.ToLower().IndexOf("eggheadcafe") > -1) return;
System.Uri
uri = new System.Uri(referer);
string query = uri.Query;
// If there is no search term, bail
if (String.IsNullOrEmpty(query))
return;
query
= query.TrimStart('?');
int startPos = query.IndexOf("q=") + 2;
query
= query.Substring(startPos);
int endPos = query.IndexOf("&");
if (endPos > 0)
query
= query.Substring(0, endPos);
query
= query.Replace("\"", "").Replace("+", " ").Replace("%22", "").Replace("%20", " ");
SearchTerm
= query;
this.RootVisual = new MainPage();
}
Then, in the control proper, the GetBingResults method uses the App.SearchTerm to
create the actual Bing Search. You would probaly want to abort the search if
there is no search term so that nothing is displayed.
Finally, the XAML for the actual display in the control, showing the databinding:
<UserControl x:Class="BingSearchWidget.BingControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BingSearchWidget"
xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
Width="210" Height="450" BorderThickness="1" Background="Bisque" BorderBrush="DarkBlue" UseLayoutRounding="True">
<Grid x:Name="LayoutRoot" Background="White" Width="210" >
<Grid.RowDefinitions>
<RowDefinition
Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock
Text="Related Items We found:" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0" Grid.Row="0" />
<ListBox x:Name="itemsList" ItemsSource="{Binding}" VerticalAlignment="Bottom" Grid.Row="1" ScrollViewer.HorizontalScrollBarVisibility="Hidden" >
<ListBox.ItemTemplate>
<DataTemplate
>
<StackPanel
>
<HyperlinkButton
Width="200"
NavigateUri="{Binding Link}" >
<TextBlock
Text="{Binding Title}" Foreground="Navy" FontSize="11" TextWrapping="Wrap" Width="195"/>
</HyperlinkButton>
<TextBlock
Text="{Binding Description}" Foreground="SteelBlue" FontSize="9" TextWrapping="Wrap" HorizontalAlignment="Stretch" Width="195"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Obviously, the XAML could use some nice styling, but I'm just not a Blend kind of
guy. I'll probably ask David Silverlight to help me with that, cause he really groks Blend. (After all, with a surname like
Silverlight...). UPDATE: I did get David to style it. He changed it to a StackPanel
with a nice "flyin" rendering animation, and added a nice feature to
display the details on mouse-enter.
And this is what David's styling makeover looks like in action:

I have modified the control to operate when there is either a querystring item "q=xxx"
or the same with the http referer. There are a few parameters needed in the
Silverlight page <OBJECT -- tag. Here they are:
<div id="silverlightControlHost" align="left" style="width:210px">
<object id="sl1" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="210px" height="500px">
<param
name="source" value="ClientBin/BingSearchWidget.xap"/>
<param
name="onError" value="onSilverlightError" />
<param
name="background" value="white" />
<param
name="minRuntimeVersion" value="3.0.40624.0" />
<param
name="autoUpgrade" value="true" />
<param
name="enablehtmlaccess" value="true"/>
<param
name="initparams" value="API_KEY=YOUR_BING_API_KEY,test=true"/>
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe>
</div>
Note that if you do not replace the "YOUR_BING_API_KEY" with a valid API
key, you'll get a MessageBox warning. The boolean "test" InitParam
is for bypassing the same-host origination of the page. The "enablehtmlAccess"
parameter is required to be able to get the referer or querystring from the page
itself. Otherwise, you're free to do whatever you like with the control and the
code.
You can download the working Silverlight 3 RTW Solution here. You'll need a Bing API key, which you can easily get here.