search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Silverlight 2 Beta 2 - Doing Data Part IV: Browser / Cookie Interaction


By Peter Bromberg
Printer Friendly Version
View My Articles
14 Views
    

As you travel in your Silverlight oddysey, you will discover that there is often a need for persisting simple state and interacting with the underlying browser DOM. Fortunately, Silverlight's System.Windows.Browser namespace makes this easy. We create a Silverlight Twitter Search widget using the Twitter API. We'll save searches in a cookie, navigate to selected Tweets using the browser, and when the Back button is pressed, we'll recapture the last search via the cookie and redisplay it.


First of all, I'm a big proponent of "not reinventing the wheel". There's some excellent code put out by Yavor Georgiev of the Connected Framework team that I was able to use for much of the basis of this sample app.

This app uses the Twitter REST API to perform a search and return RSS like so: 

http://search.twitter.com/search.rss?lang=en&rpp=100&q=searchterm

If you check  http://search.twitter.com/crossdomain.xml you'll see that our kind friends at Twitter are enabling cross-domain access for us.

When the Silverlight Page class initializes, I check for a cookie called "search". If it isn't null, I execute my asynchronous MakeRequest call which then databinds the ListBox that is used to show the results. When you click on a result, we grab the Url and use the System.Windows.Browser.HtmlPage.Window.Navigate method to make the browser - not the Silverlight app - request the selected page. Having saved the search in a cookie, when the user clicks their browser's Back button, our Silverlight app reads the cookie, and re-issues the request, redisplaying the results of the last search. If we wanted, we could actually use the retrieved cookie value to load the previous results from IsolatedStorage, a technique which I've covered in a previous article.  Since all of the business logic is included in the Page.Xaml.cs class, I present it here:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.ServiceModel.Syndication;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml;

namespace SLTwitterSearch
{
    public partial class Page : UserControl
    {
            SynchronizationContext uiThread;
            string searchUrl = "http://search.twitter.com/search.rss?lang=en&rpp=100&q=";

            public Page()
            {
                InitializeComponent();
                // Grab a reference to the UI thread
                uiThread = SynchronizationContext.Current;
                string cookval = GetCookie("search");
                if(cookval!=null)
                {
                    this.feedAddress.Text = cookval;
                    MakeRequest();

                }
            }

            private string GetCookie(string key)
            {
                string[] cookies = HtmlPage.Document.Cookies.Split(';');
                foreach (string cookie in cookies)
                {
                    string[] keyValue = cookie.Split('=');
                    if (keyValue.Length == 2)
                    {
                        if (keyValue[0].ToString() == key)
                            return keyValue[1];
                    }
                }
                return null;
            }

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                itemsList.Visibility = Visibility.Collapsed;
                MakeRequest();
            }


        void MakeRequest ()
        {
             // save this search in a cookie for later retrieval
            this.SetCookie("search", feedAddress.Text);
            // Begin HTTP request to get feed
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(searchUrl + feedAddress.Text));
            request.BeginGetResponse(new AsyncCallback(responseHandler), request);
        }

            void responseHandler(IAsyncResult asyncResult)
            {
                // Get HTTP response
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                // Load feed into SyndicationFeed object
                XmlReader reader = XmlReader.Create(response.GetResponseStream());
                SyndicationFeed feed = SyndicationFeed.Load(reader);
                // invoke the databinding method on the UI thread
                uiThread.Post(setItemsListDataContext, feed);
            }

            void setItemsListDataContext(object feed)
            {
                // Set up databinding
                itemsList.DataContext = (feed as SyndicationFeed).Items;
                itemsList.Visibility = Visibility.Visible;
            }

            private void itemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                SyndicationItem itm = (sender as ListBox).SelectedItem as SyndicationItem;
               // Twitter puts the Url to the Tweet in the "Id" field:
System.Windows.Browser.HtmlPage.Window.Navigate(new Uri(itm.Id.ToString())); } private void SetCookie(string key, string value) { // Expire in 7 days DateTime expireDate = DateTime.Now + TimeSpan.FromDays(7); string newCookie = key + "=" + value + ";expires=" + expireDate.ToString("R"); HtmlPage.Document.SetProperty("cookie", newCookie); } } // Helper classes to clean up and shape received data for UI databinding public class HtmlSanitizer : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // Remove HTML tags and empty newlines and spaces string returnString = Regex.Replace(value as string, "<.*?>", ""); returnString = Regex.Replace(returnString, @"\n+\s+", "\n\n"); // Decode HTML entities returnString = HttpUtility.HtmlDecode(returnString); return returnString; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } public class LinkFormatter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // Get the first link - that's the link to the post return ((Collection)value)[0].Uri; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
This is a pretty simple implementation; if you are interested in Twitter you can find some very interesting folks to follow on Twitter by using this little search app. You can download the complete Visual Studio 2008 Silverlight Application and web here.

NOTE: Some Twitter API's require HTTP BASIC authentication. Unfortunately the "Authorization" HttpWebRequest header in Silverlight is on the "restricted" list, so if you want to use those methods, you'll need to implement a page or service on your hosting web server application to accept the username:password combo on the querystring (encrypted if you like) or as POST form parameters, then have your server-side proxy make the HttpWebRequest call using the full Framework (which is not "restricted") and return the results to your client-side Silverlight app. See here for a detailed list. You will note that the help topic states that "many of these headers are set by the hosting browser". Unfortunately, if you use the browser-based HttpRequest object, it isn't smart enough to read the crossdomain.xml policy file on Twitter, so it becomes, effectively, useless.

Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites.
Please post questions at forums, not via email!

button
Article Discussion: Silverlight 2 Beta 2 - Doing Data Part IV: Browser / Cookie Interaction
Peter Bromberg posted at Monday, August 04, 2008 7:11 PM
Original Article