Building Blocks 1: Silverlight Slideshow Viewer for SharePoint

It has been a while (OK, a long while) since I have published a series of posts on a topic. My last series, SharePoint Image Search, has been very well received. This series will walk through the Building Blocks that I have created to demonstrate and improve the SharePoint end user experience. I have been using many of these techniques in my developer focused demos for SharePoint 2010 (and in some cases SharePoint 2007). My plan is to use this post series to distribute my code and provide more detail on the why and the how of many of my code samples. My challenge in presenting a 75 minute session (or less) is to convey both the concept and the key code sections in such a tight time frame. This post series will allow me that freedom. Let’s go!

CodePlex: How do I love thee?

I confess that I am pretty lazy and have come to embrace the fact that there are a LOT of talented folks working in many of the new technologies that are better at UI design than me. Case in point is the amazing folks at Vertigo who produced and then released for FREE Slide.Show. This is a gorgeous control for presenting slide shows. It includes all of the great features you would want to add snap to your photo collections. The 1.2 version on CodePlex is built for Silverlight 2.0 but upgrading to Silverlight 3 was simple.

My beautiful Willa

The Vertigo Slide.Show viewer includes:

  • Full Screen and Embedded mode
  • Titles and captions
  • Thumbnail previews
  • Gallery Support (See below)
  • Everything the out of the box slideshow lacks

Make it SharePointy

My only challenge after downloading and upgrading the control to Silverlight 3 was making it work for SharePoint. The control ships with tons of sample code and the Flikr example demonstrates a web service approach, so let the copying begin! I began by implementing my own SPDataProvider based on the Flikr example.

NOTE: This example and code are all based on storing your images in a Picture Library. The Lists Web service query I use expects the fields from a Picture library. If you use a different list you will have to alter the query.

All of the configuration parameters are provided either through the InitParams or an associated configuration file.

  
  <object type="application/x-silverlight-2" data="data:application/x-silverlight-2," width="640" height="480">
    <param name="background" value="#fff" />
    <param name="source" value="../ClientBin/Vertigo.SlideShow.xap" />
    <param name="initParams" value="ConfigurationProvider=LightTheme,DataProvider=SPDataProvider;Web=http://me/personal/willa/;List=Shared Pictures" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"><img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none" /></a>
</object>
In the constructor for the SPDataProvider we check for the necessary initParam values Web and List:
  	
public SPDataProvider(Dictionary<string, Dictionary<string, string>> initParams)
{
    // Priority: 1st check initParams, 2nd check Options["dataProvider"]

    //TODO : For SharePoint we need the URL of the site (later we can get it from the current location)
    if (initParams.ContainsKey("DataProvider") && initParams["DataProvider"].ContainsKey("Web"))
    {
        // use init params
        web = initParams["DataProvider"]["Web"];
    }

    if (string.IsNullOrEmpty(web) && !string.IsNullOrEmpty(Configuration.Options.DataProvider.Web))        
    {
        // use configuration
        web = Configuration.Options.DataProvider.Web;
    }

    // check that we have a web
    if (string.IsNullOrEmpty(web))
    {
        throw new SPRequestException("SharePoint Web not specified");
    }
This process repeats for the list name.

Get the images client side

This was a fun project for me for two reasons. First, over a year ago, it was my first foray into Silverlight. Second I got to start playing with LINQ. Grabbing the images was pretty easy using the SharePoint lists Web service. I have a variable declared for the location of the service and append it to the Web parameter that we grabbed above. Then we create a SOAP client to invoke our web service.

Note: I commented out the custom field that I added to store the URL I wanted an image click to navigate to because I wanted the code to work for you out of the box.

private const string SPWebService = "/_vti_bin/lists.asmx";
private void CallSPWebService()
{
EndpointAddress endpoint = new EndpointAddress(String.highlight("{0}{1}",web,SPWebService));
BasicHttpBinding binding = new BasicHttpBinding();
SPLists.ListsSoapClient client = new Vertigo.SlideShow.SPLists.ListsSoapClient(binding, endpoint);
client.GetListItemsCompleted += new EventHandler<Vertigo.SlideShow.SPLists.GetListItemsCompletedEventArgs>(client_GetListItemsCompleted);

//Create the View Field Request
XElement fields = new XElement("ViewFields",
new XElement("FieldRef", new XAttribute("Name","ID")),
new XElement("FieldRef", new XAttribute("Name", "Title")),
new XElement("FieldRef", new XAttribute("Name", "Description")),
new XElement("FieldRef", new XAttribute("Name", "Created")),
new XElement("FieldRef", new XAttribute("Name", "ImageCreateDate")),
new XElement("FieldRef", new XAttribute("Name", "EncodedAbsUrl")),
new XElement("FieldRef", new XAttribute("Name", "FileDirRef")), //Use Folder Names for the image categories
//newXElement(("FieldRef", new XAttribute("Name", "LinkUrl")), //NOTE: If you add a LinkUrl field you can add the link to your images.
new XElement("FieldRef", new XAttribute("Name", "EncodedAbsThumbnailUrl"))//NOTE: this only works on Image Libraries
);

//Create the query options Request
XElement options = new XElement("QueryOptions",
new XElement("ViewAttributes", new XAttribute("Scope", "Recursive")),
new XElement("IncludeMandatoryColumns","False"),
new XElement("DateInUtc","False"));

client.GetListItemsAsync(list, null, null, fields, null, options, null);

}

Then we catch the result in the callback. This is where I got to learn some LINQ techniques to create groups of images based on the SharePoint Folders in the Image Library.

void client_GetListItemsCompleted(object sender, Vertigo.SlideShow.SPLists.GetListItemsCompletedEventArgs e)
{
if (e.Error == null)
{
//Debug.WriteLine(e.Result.ToString());


//Populate the photolist and if category is present create albums
string setId = string.Empty;
IEnumerable<Photo> photos = new List<Photo>();

XNamespace s = "http://schemas.microsoft.com/sharepoint/soap/";
XNamespace rs = "urn:schemas-microsoft-com:rowset";
XNamespace z = "#RowsetSchema";

XDocument listXml = XDocument.Parse(e.Result.ToString());

var albums = from album in listXml.Descendants(z + "row")
//orderby (DateTime)photo.Attribute("ows_Created") descending
group album by album.Attribute("ows_FileDirRef").Value.Substring(album.Attribute("ows_FileDirRef").Value.IndexOf("#") + 1) 
into photoGroup
//Select the photos into the photo group (Albums)
select new
{
 Album = photoGroup.Key,
 //Photos = from photo in listXml.Descendants(z + "row")
 Photos = from photo in photoGroup
		  select new Photo
		 {
			 Title = (string)photo.Attribute("ows_Title"),
			 DateTaken = (string)photo.Attribute("ows_ImageCreateDate"),
			 Description = (string)photo.Attribute("ows_Description"),
			 Url = (string)photo.Attribute("ows_EncodedAbsUrl"),
			 //LinkUrl = (string)photo.Attribute("ows_LinkUrl"),
			 Thumbnail = (string)photo.Attribute("ows_EncodedAbsThumbnailUrl")
		 }
	};

	List<Photoset> sets = new List<Photoset>();

	//Group By Folder or Group By Category
	foreach (var album in albums)
	{
	Photoset set = new Photoset();

	set.Id = album.Album.ToString();
	set.Title = album.Album.ToString().Substring(album.Album.ToString().LastIndexOf("/")+1);
	set.Photos = album.Photos.ToArray();
	sets.Add(set);

	}


	photosets = sets.ToArray();

	//All Set, Load the Data
	LoadSPData();

	}
	else
	{
	Debug.WriteLine(e.Error.ToString());
}
}

Are we done yet?

Finally I call LoadData to stuff all this into the control and build the Catalogs.

private void LoadSPData()
{
List<Album> albums = new List<Album>();

foreach (Photoset set in photosets)
{
List<Slide> slides = new List<Slide>();

foreach (Photo photo in set.Photos)
{
//TODO : Add some null string error handling
slides.Add(new Slide()
{
Title = photo.Title,
Description = String.Format("{0} ({1})", photo.Description, DateTime.Parse(photo.DateTaken).ToString("d.MM.yyyy")),//
Thumbnail = new Uri(photo.Thumbnail,UriKind.Absolute),
Preview = new Uri(photo.Url, UriKind.Absolute),
//Link = new Uri(photo.LinkUrl,UriKind.Absolute), //TODO: If you add a link field you can make the pictures clickable
Link = new Uri("http://www.k9search.org",UriKind.Absolute), //TODO: Hard coded for demo purposes
Source = new Uri(photo.Url, UriKind.Absolute)

});
}
//Create the albums based on the folders 
albums.Add(new Album()
{
Title = set.Title,
Description = set.Description,
Thumbnail = slides[0].Thumbnail, 
Slides = slides.ToArray()
});
}

Data.Albums = albums.ToArray();

OnDataFinishedLoading();
}

Albums too!

Yes, the control provides for grouping your images into albums. I simply use the folders in the library. I use the first image in the folder as the thumbnail. The result looks like this:

Vertigo with albums

Give me the code already!

OK, but I need to say something first. I don’t like hard coded paths. I also don’t like long blog posts and this one is WAY too long already. So I am going to stop here and start a new (shorter) post on how to make this all portable. One last note the code was last built with Visual Studio 2010 if you open it in Visual Studio 2008 the project file may not load correctly.

Here is the code: Vertigo Slide.Show for SharePoint 2007

|| Building Blocks || Development || SharePoint 2007 || SharePoint 2010 || Silverlight || Speaking Engagements

comments powered by Disqus

Let's Get In Touch!


Ready to start your next project with us? That’s great! Give us a call or send us an email and we will get back to you as soon as possible!

+1.512.539.0322