Posts tagged as:

silverlight

SEO for Silverlight

by Ryan Lane on June 24, 2008

There are more challenges when it comes to optimizing for search engines to consume your content if it is a dynamic Rich UI application that doesn’t rely on Ajax. While Google is able to pick up Flash SWF files during its crawl, this does not guarantee that the content is parsed correctly or given the same weight as any other file formats or a pure HTML/AJAX page. Worse, if the application uses a web service, how can it be guaranteed that all the pages are crawled and returned correctly?

In most cases you will need to make sure that the page hosting the application has some html text that describes the application and what it offers. So in essence treat the page in the same way you would HTML.

When possible expose the content of the application so that it too can be indexed.

Simple Silverlight application – XAML to XHTML with XSLT

For a Silverlight element that contains all of its content in the XAML the best method would be to transforms the XAML using XSLT into friendly XHTML. The goal is to contain the translated XAML into a <div> element that would be replaced by the Silverlight control. Search engines would find the XHTML while browsers with Silverlight installed would see the Silverlight app.

 

   1: <div id="SLHost">
   2:     <asp:Xml ID="XHTML" runat="server" DocumentSource="seo.xaml"
   3:   TransformSource="XAML2XHTML.xslt" EnableViewState="False"/>
   4:     <script type="text/JavaScript">
   5:         createSilverlight();
   6:     </script>
   7: </div>

Then use the following XAML2XHTML.xslt

   1: <?xml version="1.0" encoding="utf-8"?>
   2:  
   3: <xsl:stylesheet version="1.0"
   4:     xmlns:sl="http://schemas.microsoft.com/client/2007"
   5:     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   6:     exclude-result-prefixes="sl">
   7:  
   8:   <xsl:output omit-xml-declaration="yes" indent="yes"/>
   9:  
  10:   <xsl:template match="/">
  11:     <xsl:comment>This is the text that is in the Silverlight XAML:</xsl:comment>
  12:     <xsl:apply-templates select="*"/>
  13:   </xsl:template>
  14:  
  15:   <xsl:template match="sl:Canvas">
  16:     <div>
  17:       <xsl:apply-templates select="*"/>
  18:     </div>
  19:   </xsl:template>
  20:  
  21:   <xsl:template match="node()"/>
  22:  
  23:   <xsl:template match="sl:Image">
  24:     <div>
  25:       <img src="{@Source}"/>
  26:     </div>
  27:   </xsl:template>
  28:   <xsl:template match="sl:MediaElement">
  29:     <div class="Media">
  30:       <a href="{@Source}">Media</a>
  31:     </div>
  32:   </xsl:template>
  33:  
  34:   <xsl:template match="sl:TextBlock">
  35:     <div>
  36:       <xsl:value-of select="@Text"/>
  37:       <xsl:value-of select="text()"/>
  38:       <xsl:apply-templates select="*"/>
  39:     </div>
  40:   </xsl:template>
  41:   <xsl:template match="sl:LineBreak">
  42:     <br/>
  43:   </xsl:template>
  44:  
  45:   <xsl:template match="sl:Run">
  46:     <span>
  47:       <xsl:value-of select="@Text"/>
  48:       <xsl:value-of select="text()"/>
  49:     </span>
  50:   </xsl:template>
  51: </xsl:stylesheet>

 

Handling more advanced Silverlight applications

How do you design an application that could have dynamic content and robust interaction while at the same time enable a web crawler to understand and categorize the underlying content correctly? Unfortunately there is no simple answer or single correct answer. It depends highly on the application’s purpose. In some situations there will be no way to offer the content of the application up to spider outside of the application. In this case it’s best to have as much meta information on the page hosting the application as possible. By following the standard HTML methods in this document you can still extend the indexability of your application by making sure external links are properly formatted and that high ranking sites link to your application.

Detect and Serve

It requires a little more work up front from a development standpoint to go this route, but if you really have a strong need to get the content in your application indexed then this will be the best approach. The goal is to develop the code of the site to be delivered in multiple formats based on the user agent that is accessing the content. This isn’t all that uncommon anyway with the large range of browser and devices out there that are consuming the web already. Most applications are built with the data stored separately from the interaction in a database or local XML document. The site would have to be built so that it can serve up HTML pages for those who don’t have Flex/Flash/Silverlight installed. Plus, we could potentially change these pages for mobile devices like the iPhone that don’t yet support Flash or Silverlight.

It’s also recommend to have some enticement or value proposition to explain to real users why it would be beneficial to add the plug-ins required to get the optimal experience.

The goal would be to have clients that have the plug-in or runtime installed, would be provided the rich interaction. On those that don’t, a functional page in HTML will be provided. More importantly, to the search engines, these pages that are generated will be tagged and indexed correctly, making the content of our applications visible and increase their visibility.

{ 0 comments }

Up to date books from O’Reilly

by Ryan Lane on May 21, 2008

2512159922_f5fa552a18 I picked up the Essential Silverlight 2.0 book while I was at the Web 2.0 Expo.  I’ve been meaning to share.  It’s the first time I’ve purchased an “Up To Date” book from O’Reilly.  I’m waiting form my first update to come in the mail.

To update your book you have the  option to print out the pages or view them online for free, but I like the idea of updating the book itself. 

To put in the new pages all you have to do is pop the little metal posts out from the plastic cover then add in the pages.

The idea behind O’Reilly’s “Up To Date” books

For publishers of printed technology books, it’s always been challenging to get a book written, edited, printed, and delivered while the content of the book is still relevant and needed by the audience. Some of our readers are happy to have the information they need delivered quickly in electronic form, but we hear from others that they still prefer the actual printed page. The Up-to-Date format is our answer to the need to deliver information on new technologies quickly, yet in printed form. And the beauty of it is that the book can take shape right along with the software, providing coverage of the CTPs, betas, and RTM releases as they become available.

Because the cover is plastic I have noticed a small issue in that the printing seems to flake off from the cover.  Hopefully they find a way to fix this in future version.  Of course I’d be super happy if they had a version that would work on my Sony Reader too.

Oh, and the book is very good too, but that’s another post!

{ 0 comments }

Expression 2 is out

by Ryan Lane on May 2, 2008

image

Microsoft released the Expression 2.0  suite of products today.  I’ve been using expression since last year and religiously updating whenever a new beta or preview version of any of the applications as they were released.  At first I wasn’t a big fan of the tools, but over time I’ve really come to enjoy them and they have become my default tools to use for web and Silverlight development.  Combined with Visual Studio 2008 it all becomes a powerful environment.   Now that I’m learning how to develop and design in WPF I’m using Blend all the time to put together my UI objects.

[click to continue…]

{ 0 comments }

Silverlight 2.0 Flickr WordPress Widget

by Ryan Lane on April 8, 2008

I had the challenge of creating both a simple Silverlight 2.0 flickr viewer and a WordPress widget that used silverlight so I combined the two projects in to one.

The first part was creating the Silverlight 2.0 Flickr RSS viewer.

   1: <UserControl
   2:     xmlns="http://schemas.microsoft.com/client/2007"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     x:Class="FlickrShow.Page"
   5:     Width="auto" 
   6:     Height="auto" 
   7:     x:Name="FlickrShow" 
   8:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
   9:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
  10:     mc:Ignorable="d">
  11:  
  12:     <Grid x:Name="LayoutRoot" Background="#FF424242" >
  13:         <Image HorizontalAlignment="Stretch" Margin="0,0,0,0" x:Name="ImageItem" VerticalAlignment="Stretch" Stretch="Uniform" Cursor="Hand" Source="OpeningImage.jpg">
  14:             <Image.Resources>
  15:                 <Storyboard x:Name="FadeOutAnimation">
  16:                     <DoubleAnimation Duration="00:00:00.20" From="1" To="0"                        
  17:                                      Storyboard.TargetProperty="Opacity"                        
  18:                                      Storyboard.TargetName="BigImage" />
  19:                 </Storyboard>
  20:                 <Storyboard x:Name="FadeInAnimation">
  21:                     <DoubleAnimation Duration="00:00:00.20" From="0" To="1"                        
  22:                                      Storyboard.TargetProperty="Opacity"                        
  23:                                      Storyboard.TargetName="BigImage" />
  24:                 </Storyboard>
  25:             </Image.Resources>
  26:         </Image>
  27:         <TextBox Height="30" VerticalAlignment="Bottom" Text="" x:Name="LabelBox" Background="#2B000000" Foreground="#FFFFFFFF" BorderThickness="0,0,0,0" FontSize="11" />
  28:     </Grid>
  29: </UserControl>

page.xaml

   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 System.Windows.Threading;
  12: using System.Windows.Browser;
  13: using System.Xml.Linq;
  14: using System.Windows.Media.Imaging;
  15: using System.Windows.Resources;
  16: using System.IO;
  17: using System.Net;
  18: using System.Xml;
  19: using System.ServiceModel.Syndication;
  20:  
  21:  
  22:  
  23: namespace FlickrShow
  24: {
  25:     public class SettingDefinition
  26:     {
  27:         public string userID { get; set; }
  28:         public string feedType  { get; set; }
  29:         public string tags  { get; set; }
  30:         public string duration { get; set; }
  31:     }
  32:  
  33:     public partial class Page : UserControl
  34:     {
  35:         string currentUserID;
  36:         string currentFeedType;
  37:         string currentTags;
  38:         int currentDuration;
  39:  
  40:         DateTime lastUpdate;
  41:         string currentLink;
  42:         String[,] imgArray = new String[20, 3]; //Array to store information pulled from the RSS
  43:         int i = 0;  //handy little index number to count with
  44:         int imageIndex = 0;  //another index used for displaying specific images
  45:         DispatcherTimer dt = new DispatcherTimer();
  46:         
  47:         public Page(string UserID, string FeedType, string Tags, int Duration)
  48:         {
  49:             InitializeComponent();
  50:  
  51:             currentUserID = UserID;
  52:             currentFeedType = GetFeedType(FeedType);
  53:             currentTags = Tags;
  54:             currentDuration = Duration;
  55:  
  56:             this.Loaded += new RoutedEventHandler(Page_Loaded);
  57:             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://api.flickr.com/services/feeds/" + currentFeedType + currentUserID + currentTags + "&format=rss2"));
  58:  
  59:             request.BeginGetResponse(new AsyncCallback(getImages), request);
  60:  
  61:             lastUpdate = DateTime.Now;
  62:  
  63:             dt.Interval = new TimeSpan(0, 0, 0, currentDuration);
  64:             dt.Tick += new EventHandler(dt_Tick);
  65:             dt.Start();
  66:  
  67:         }
  68:  
  69:         void getImages(IAsyncResult asyncResult)
  70:         {
  71:             XNamespace mediaNamespace = "http://search.yahoo.com/mrss/";
  72:  
  73:  
  74:             HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
  75:             HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
  76:  
  77:             XmlReader reader = XmlReader.Create(response.GetResponseStream());
  78:             SyndicationFeed feed = SyndicationFeed.Load(reader);
  79:  
  80:             foreach (SyndicationItem item in feed.Items)
  81:             {
  82:                 //let's put the results in a multidimesional Array, because they are fun 
  83:                 imgArray.SetValue(item.ElementExtensions[1].GetReader().GetAttribute("url"), i, 0);
  84:                 imgArray.SetValue(item.Title.Text, i, 1);
  85:                 imgArray.SetValue("http://www.flickr.com" + item.Links[0].Uri.AbsolutePath, i, 2);
  86:                 i++;
  87:             }
  88:         }
  89:  
  90:         void Page_Loaded(object sender, RoutedEventArgs e)
  91:         {
  92:                       
  93:             ImageItem.MouseEnter += new MouseEventHandler(ImageItem_MouseEnter);
  94:             ImageItem.MouseLeave += new MouseEventHandler(ImageItem_MouseLeave);
  95:             ImageItem.MouseLeftButtonDown += new MouseButtonEventHandler(ImageItem_MouseButtonDown);
  96:  
  97:             
  98:         }
  99:  
 100:  
 101:  
 102:         void dt_Tick(object sender, EventArgs e)
 103:         {
 104:             //this is our timer control, every time we update this will run
 105:             Update();   
 106:         }
 107:  
 108:         void Update()
 109:         {
 110:             //Updating the time
 111:             DateTime now = DateTime.Now;
 112:             TimeSpan elapsed = now - lastUpdate;
 113:             lastUpdate = now;
 114:             //do your loop processing here
 115:             ChangeImage();
 116:             
 117:         }
 118:  
 119:         void ChangeImage()
 120:         {
 121:             //This is where we actually update the <image> XAML. 
 122:  
 123:             if (imageIndex <= (i - 1))
 124:             {
 125:                 LabelBox.Text = imgArray.GetValue(imageIndex, 1) as string;
 126:                 ImageItem.SetValue(Image.SourceProperty, imgArray.GetValue(imageIndex, 0) as string);
 127:                 currentLink = imgArray.GetValue(imageIndex, 2) as string;
 128:                 imageIndex++;
 129:             }
 130:             else
 131:             {
 132:                 imageIndex = 0;
 133:                 LabelBox.Text = imgArray.GetValue(imageIndex, 1) as string;
 134:                 ImageItem.SetValue(Image.SourceProperty, imgArray.GetValue(imageIndex, 0) as string);
 135:                 currentLink = imgArray.GetValue(imageIndex, 2) as string;
 136:                 imageIndex++;
 137:             }
 138:  
 139:         }
 140:  
 141:         public string GetFeedType(string feedSetting)
 142:         {
 143:             switch (feedSetting)
 144:                 {
 145:                 case "Public":
 146:                         return "photos_public.gne?id=";                        
 147:                 case "Friends":
 148:                     //This is for a future release
 149:                         return "photos_friends.gne?user_id=";
 150:                 default:
 151:                         return "photos_public.gne?id="; 
 152:                 }
 153:             
 154:         }
 155:  
 156:         public string FormatTags(string tagsElement)
 157:         {
 158:             if (tagsElement.Length > 0)
 159:             {
 160:                 return "&tags=" + tagsElement;
 161:             }
 162:             else { return ""; }
 163:         }
 164:  
 165:         private void ImageItem_MouseEnter(object sender, MouseEventArgs e)
 166:         {            
 167:                 dt.Stop();
 168:         }
 169:  
 170:         private void ImageItem_MouseLeave(object sender, MouseEventArgs e)
 171:         {
 172:             dt.Start();
 173:         }
 174:  
 175:         private void ImageItem_MouseButtonDown(object sender, MouseButtonEventArgs e)
 176:         {
 177:             HtmlPage.Window.Navigate(new Uri(currentLink, UriKind.Absolute), "_flickr");
 178:         }
 179:     }
 180: }

page.xaml.cs

   1: using System.Windows;
   2: using System;
   3:  
   4: namespace FlickrShow
   5: {
   6:     public partial class App : Application 
   7:     {
   8:  
   9:         public App() 
  10:         {
  11:             this.Startup += this.OnStartup;
  12:             this.Exit += this.OnExit;
  13:  
  14:             InitializeComponent();
  15:         }
  16:  
  17:         private void OnStartup(object sender, StartupEventArgs e) 
  18:         {
  19:             // Load the main control here
  20:             string currentUserID = e.InitParams["flickr_userID"];
  21:             string currentFeedType = "Public";
  22:             string currentTags = "";
  23:             int currentDuration = Convert.ToInt32(e.InitParams["time"]);
  24:  
  25:             this.RootVisual = new Page(currentUserID, currentFeedType, currentTags, currentDuration);
  26:         }
  27:  
  28:         private void OnExit(object sender, EventArgs e) 
  29:         {
  30:  
  31:         }
  32:     }
  33: }

App.xaml.cs

   1: <?php
   2: /*
   3: Plugin Name: Silverlight Flickr Widget
   4: Plugin URI: http://www.futile.com/
   5: Description: A widget which will display your latest Flickr photos using Silverlight.
   6: Author: Ryan T. Lane
   7: Version: 0.2
   8: Author URI: http://futile.com/
   9: 
  10: Installing
  11: 1. Make sure you have the Widget plugin available at http://automattic.com/code/widgets/
  12: 1. Copy wordpressWidgetSilverlightFlickr.php to your plugins folder, /wp-content/plugins/widgets/
  13: 2. Activate it through the plugin management screen.
  14: 3. Go to Themes->Sidebar Widgets and drag and drop the widget to wherever you want to show it.
  15: 
  16: Changelog
  17: 0.2 = First public release.
  18: */
  19:  
  20:  
  21: function WidgetFlickrSilverlight($args) {
  22:     extract($args);
  23:     
  24:     $options = get_option('WidgetFlickrSilverlight');
  25:     if( $options == false ) {
  26:         $options[ 'flickr_userID' ] = '21854617@N00';
  27:         $options[ 'time' ] = 4;
  28:     }
  29:     
  30:     $time = $options[ 'time' ];
  31:     $flickr_userID = $options[ 'flickr_userID' ];
  32:     
  33:     echo $before_widget;
  34:     
  35:     echo $before_title . 'Flickr' . $after_title; 
  36:     ?>
  37:                <script type="text/javascript">
  38:                     function onSilverlightError(sender, args) {
  39:                     if (args.errorType == "InitializeError")  {
  40:                         var errorDiv = document.getElementById("errorLocation");
  41:                         if (errorDiv != null)
  42:                         errorDiv.innerHTML = args.errorType + "- " + args.errorMessage;
  43:                         }
  44:                     }
  45:                 </script>
  46:                 
  47:                 <!-- Runtime errors from Silverlight will be displayed here.
  48:                     This will contain debugging information and should be removed or hidden when debugging is completed -->
  49:                 <div id='errorLocation' style="font-size: small;color: Gray;"></div>
  50:  
  51:                 <div id="silverlightControlHost">
  52:                     <object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="100%" height="100%">
  53:                         <param name="source" value="/wp-content/plugins/widgets/FlickrShow.xap"/>
  54:                         <param name="onerror" value="onSilverlightError" />
  55:                         <param name="background" value="white" />
  56:                         <param name="initParams" value="time=<?php echo $time; ?>,flickr_userID=<?php echo $flickr_userID; ?>" />
  57:                         
  58:                         <a href="http://go.microsoft.com/fwlink/?LinkID=108182" style="text-decoration: none;">
  59:                              <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
  60:                         </a>
  61:                     </object>
  62:                     <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
  63:                 </div>   
  64:         <?php echo $after_widget; ?>
  65: <?php
  66: }
  67:  
  68: function WidgetFlickrSilverlight_control() {
  69:     $options = $newoptions = get_option('WidgetFlickrSilverlight');
  70:     if( $options == false ) {
  71:         $newoptions[ 'title' ] = 'Flickr Photos';
  72:     }
  73:     if ( $_POST["flickr-submit"] ) {
  74:         $newoptions['title'] = strip_tags(stripslashes($_POST["flickr-title"]));
  75:         $newoptions['time'] = strip_tags(stripslashes($_POST["time"]));
  76:         $newoptions['flickr_userID'] = strip_tags(stripslashes($_POST["flickr-userID"]));
  77:     }
  78:     if ( $options != $newoptions ) {
  79:         $options = $newoptions;
  80:         update_option('WidgetFlickrSilverlight', $options);
  81:     }
  82:     $title = wp_specialchars($options['title']);
  83:     $time = wp_specialchars($options['time']);
  84:     if ( empty($items) || $items < 1 ) $items = 3;
  85:     $flickr_userID = wp_specialchars($options['flickr_userID']);
  86:  
  87: ?>
  88:     <p><label for="flickr-title"><?php _e('Title:'); ?> <input style="width: 250px;" id="flickr-title" name="flickr-title" type="text" value="<?php echo $title; ?>" /></label></p>
  89:     <p><label for="flickr-userID"><?php _e('Flickr User ID:'); ?> <input style="width: 250px;" id="flickr-title" name="flickr-userID" type="text" value="<?php echo $flickr_userID; ?>" /></label></p>
  90:     <p style="text-align:center; line-height: 30px;"><?php _e('Time per image in seconds:'); ?> <select id="time" name="time"><?php for ( $i = 1; $i <= 10; ++$i ) echo "<option value='$i' ".($time==$i ? "selected='selected'" : '').">$i</option>"; ?></select></p>
  91:     <p align='left'>
  92:     * Your user ID can be found on your Flickr RSS page. Scroll down to the bottom of the page until you see the <em>Feed</em> link and copy the value after id from the URL into the box above.<br />
  93:     <br clear='all'></p>
  94:     <input type="hidden" id="flickr-submit" name="flickr-submit" value="1" />
  95: <?php
  96: }
  97:  
  98:     function WidgetFlickrSilverlight_init() {
  99:         register_widget_control('Silverlight Flickr', 'WidgetFlickrSilverlight_control', 500, 250);
 100:         register_sidebar_widget('Silverlight Flickr', 'WidgetFlickrSilverlight');
 101:     }
 102:     add_action( "init", "WidgetFlickrSilverlight_init" );
 103:  
 104: ?>

wordpressWidgetSilverlightFlickr.php

 

The php page and compiled .xap files are placed in the /wp-content/plugins/widgets/ folder.

First you will have to activate the plug-in.

image

Then enable the widget in one of your sidebars and edit the properties to include your information.

image

if you don’t know your flickr user id you can get it from idGettr

That’s all for now.  I will do a more detailed write up of what is going on up above very soon.

You can grab the source from CodePlex.

{ 2 comments }

Happy Valentine’s Day

February 14, 2008

I worked on a little Silverlight Valentine’s day e-card for Microsoft. The original card design was going to be a bit more, but we had some time constraints to deal with. Overall, it’s a simple application. The meat of the developement work was done by the talented folks at Aeshen. I would like to go [...]

Read the full article →

Mix 2008

February 7, 2008

I’ll be on a panel at this year’s Mix event, talking about workflow and process and such. It’s a pretty talented group of people and should make for a good time. Making it Simple: Designer/Developer Workflow Speakers: Marcelo Marer (Avenue A Razorfish), Ken Azuma (Second Factory), Robby Ingebretsen (Identity Mine), Ryan Lane (Wunderman), Mark Ligameri [...]

Read the full article →

My first Chumby widget

November 27, 2007

I recently got a Chumby and have been playing around with writing some widgets that I wanted. The first one is this Seattle Traffic Cam widget: I built it using OpenLaszlo instead of Adobe Flash tools since I don’t own a remotely recent version of the Flash software and I wasn’t going to spend $700 [...]

Read the full article →