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.

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.Format("{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:
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



I love the idea of being able to use this slideshow with a SharePoint library. I am however completely confused on how to implement it. My coding experiece is very limited. I am just now starting to fiddle with java and C#. Is there a way that you could explain how to use this code. In the constructor for the SPDataProvider is where i get lost. Unfortunatly it seems that is the beginning. I dont know where this code is to go. I apologize for needing such a basic Tutorial but this slideshow would be very beneficial for my companies Sharepoint site.
Hi I am hit by the following error message in Vertigo.SlideShow.SPLists.GetListItemsResponse _result = ((Vertigo.SlideShow.SPLists.GetListItemsResponse)(base.EndInvoke(“GetListItems”, _args, result)));
An error occurred while trying to make a request to URI ‘http://moss:9090//_vti_bin/lists.asmx’. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details.
Problem remains even when I have clientaccesspolicy.xml in Inetpub folder
Jordan,
You should be able to implement my sample directly. If you want to make changes be sure you are using Visual Studio 2010 and are set up for Silverlight development. Others have implemented it directly from my sample.
Patrick,
For MOSS 2007 the client acess policy file goes on the root of the site collection. For 2010 it goes in the IIS folder. (Test by referencing the file directly.)
Check this post: http://www.ableblue.com.previewdns.com/blog/archive/2009/11/18/clientaccesspolicy-file-in-sharepoint-2010.aspx
Can you help me using this control? I have published the samples site in my web server. after that I can uses your samples like the Memorabilia. After that I try to integrate this in my sharepoint 2010 site. I have create a new page with the webpage viewer webpart and then give the URL of the html page inside “SharePoint” Directory, “http://srvtatspw001:9200/sharepoint/Default.html”. I have change the default.html to configure the init parameters, “Web=http://srvtatspw001/;List=SliderViewList”.
But I get only a gray screen with a white square in the middle.
What I’m doing wrong?
Thanks
Ricardo, I notice you are publishing on port 9200. Did you include the correct URL for the InitParams? Debug it as you would any other solution. Check the souce in the rendered page and test the URLs.
How do i deploy the web-part to take the images dynamically from a sharepoint library ???
Nice one,
But i can able to load only 40 images
Majid, the query does the work getting the images for you. If you mean a document library you will have to edit the column names to work. I used a Picture Library.