(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Generating simple collections for Pivot Viewer

(9 votes)
Levente Mihály
>
Levente Mihály
Joined Apr 27, 2010
Articles:   7
Comments:   16
More Articles
0 comments   /   posted on May 27, 2011
Categories:   Controls

The Silverlight PivotViewer control is an easy way to have a fast and spectacular gallery for your website/webapp. In this article I'll show how to generate collections for the PivotViewer using Linq-to-Xml and the command line tool.

PivotViewer Introduction

The control enables filtering, sorting with fluid animations, and uses Deep Zoom technology to make the loading fast. To get an idea about it, check out the Netflix pivot example and the Windows Phone Marketplace Visualizer (wp7apphub). For hosting you’ll only need a fileserver because simple collections are static and stored in xml and in (Deep Zoom) image files. For more detailed information visit the PivotViewer site on Silverlight.net.

Download PivotViewerSample with preset collection

Download source for BloggerToPivot command line converter

Data options

PivotViewer collections are stored in Collection XML (CXML) schemas and Deep Zoom images (DZC). There are three types of collections:

  • Simple collection – Maximum 3,000 items, static pre-generated collection. This type will be covered in this article.
  • Linked collection – Multiple inter-linked simple collections to overcome the size limit of Simple collections. Needs data that naturally flows into divisions, data sets can become very complex and difficult to handle.
  • Just in time collection – Collection is generated dynamically, no size limit. Bandwidth and performance can become a problem.

Properties and structure

In the CXML schema first we must define the Facet Categories. These elements will be used to filter, sort or group the collection items. They could be type of String, LongString, Number, DateTime, or Link. For each item we can add multiple values for a Facet Category, so for example we can store tags here as well, not just unique categories.

Next we can define the Items, each has a Name, an Image, and optional Description. Adding Facet Category values is also optional but each Category can have multiple values.

Facet Categories appear on the Filter Panel, the Item Name, Description and Facet values (where the Category has IsMetaDataVisible set to true) appear on the Info Panel.

Creating Simple collections programmatically

There are a number of tools for generating collections, including an Excel plug-in, and there are solutions on CodePlex as well. But there are situations when you don’t want to use Excel to generate collections, because it might not fit best with your existing data. Since the CXML is a standard XML file we can use Linq-to-Xml to manually generate the CXML and by using the command line tool to make it Deep Zoom enabled.

So, we will need to download the PivotViewer control itself, and the command line tool. The command line tool comes with a sample.cxml and sample images (in Pauthor/Docs). We can generate the Deep Zoom enabled CXML from the sample by running the following command:

Pauthor.exe /source cxml sample.cxml /target deepzoom output\sample-dz.cxml

The result will be another CXML (output/sample-dz.cxml) and a folder with the Deep Zoom images (output/sample-dz_deepzoom). The Pivot control comes with a sample app in which you can try out the generated collection. It’s located in Program files folder\Microsoft SDKs\Silverlight\v4.0\PivotViewer\Aug10\Source or you can use the attached source. (The sample in the SDK didn’t work for me out of the box, I guess the collection was removed as it returns a 404 error). You can try it by rewriting the “initialCollectionUri” parameter to localhost in PivotViewerSample.html and by copying the newly generated sample collection to the Web folder, next to the html.

Here’s a little part of the sample xml:

<Collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:p="http://schemas.microsoft.com/livelabs/pivot/collection/2009" Name="2009 AKC Dog Breeds" SchemaVersion="1.0" xmlns="http://schemas.microsoft.com/collection/metadata/2009">
    <FacetCategories>
        <FacetCategory Name="Group" Type="String" p:IsFilterVisible="true" p:IsMetaDataVisible="true" p:IsWordWheelVisible="true" />
        <FacetCategory Name="Size" Type="String" p:IsFilterVisible="true" p:IsMetaDataVisible="true" p:IsWordWheelVisible="true">
            <Extension>
                <p:SortOrder Name="Size">
                    <p:SortValue Value="Tiny" />
                    <p:SortValue Value="Small" />
                    <p:SortValue Value="Medium" />
                    <p:SortValue Value="Large" />
                    <p:SortValue Value="Extra Large" />
                </p:SortOrder>
            </Extension>
        </FacetCategory>
...
<Items>
        <Item Id="0" Name="Affenpinscher" Img="sample_images\{556274aa-0333-4a65-9b46-7676e2d0bbda}.jpg" Href="http://www.akc.org/breeds/affenpinscher/">
            <Description>The Affenpinscher is a  Tiny dog.</Description>
            <Facets>
                <Facet Name="Group">
                    <String Value="Toy" />
                </Facet>
                <Facet Name="Size">
                   <String Value="Tiny" />
               </Facet>
            </Facets>
        </Item>

So, to generate our CXML we can use Linq-to-xml. Let’s say we have items that have Tags and Creation time. First, we must define the Facet Categories, then we can add items to the xml:

XNamespace p = XNamespace.Get("http://schemas.microsoft.com/livelabs/pivot/collection/2009");
XNamespace defns = XNamespace.Get("http://schemas.microsoft.com/collection/metadata/2009");
XElement xml =
         new XElement(defns + "Collection",
             new XAttribute(XNamespace.Xmlns + "p", p),
             new XAttribute("Name", "test-collection"),
             new XAttribute("SchemaVersion", "1.0"),
             new XElement(defns + "FacetCategories",
                 new XElement(defns + "FacetCategory",
                     new XAttribute("Name", "CreatedTime"),
                     new XAttribute("Type", "DateTime"),
                     new XAttribute(p + "IsFilterVisible", "true"),
                     new XAttribute(p + "IsMetaDataVisible", "true")
                  ),
                 new XElement(defns + "FacetCategory",
                     new XAttribute("Name", "Tags"),
                     new XAttribute("Type", "String"),
                     new XAttribute(p + "IsFilterVisible", "true"),
                     new XAttribute(p + "IsWordWheelVisible", "true"),
                     new XAttribute(p + "IsMetaDataVisible", "true")
                 )),
              new XElement(defns + "Items")
             );

In the code above we defined a collection named “test-collection”, that has two Facet Categories; CreatedTime type of DateTime with filtering enabled and info panel visibility; and Tags category type of String which also has filtering enabled and will be visible on the Info panel.

Next we can cycle through our items for any data source, and add them. Note that you will need to save every item’s image to the local file system.

var xitems = xml.Element(defns + "Items");
 
foreach (var entry in Entries)
{
    var xitem = new XElement(defns + "Item",
        new XAttribute("Id", entry.ID),
        new XAttribute("Name", entry.Title),
        new XAttribute("Img", Path.Combine(imgFolder, String.Format("{0}.jpg", entry.ID))),
        new XAttribute("Href", entry.EntryUrl),
        new XElement(defns + "Description", entry.EntryText),
        new XElement(defns + "Facets",
            new XElement(defns + "Facet", new XAttribute("Name", "CreatedTime"),
                new XElement(defns + "DateTime", new XAttribute("Value", entry.Published))
                ),
                new XElement(defns + "Facet", new XAttribute("Name", "Tags"),
                    entry.Tags.Select(t =>
                        new XElement(defns + "String", new XAttribute("Value", t))
                        )
                        )
                )
        );
    xitems.Add(xitem);                
}
 
xml.Save(cxmlName)

After saving the xml, we can run the PAuthor command line tool to generate a Deep Zoom enabled collection and run it in the PivotViewerSample project.

Creating pivot collection of a blog

To have a real world example, let's have a look at how to generate pivot data from a Blogger site (blogspot.com blog). In Blogger we can export the whole blog to an xml file, including all the posts.
In my example the blog already contained more than 500 entries, so the exported xml was pretty big. I used the freely available XML Notepad 2007 application to discover the xml's structure, and to help me figure out the parsing. First, I added a class called Entry that represented an item in the PivotViewer and a blog entry at the same time.

Parsing was simple once I knew the structure. We need to take entry elements that has the first category as “post” (because settings are in entry elements too), and for the tags, we need to collect the “term” category values inside an entry:

var xml = XDocument.Load(@"blog.xml");
var ns = "{http://www.w3.org/2005/Atom}";
var tns = "{http://www.blogger.com/atom/ns#}";
var entries = from e in xml.Descendants().Elements(ns + "entry")
              orderby e.Element(ns + "published").Value descending
              where e.Elements(ns+"category").First().Attribute("term").Value == "http://schemas.google.com/blogger/2008/kind#post"
              select new Entry
              {
               Title = e.Element(ns+"title").Value,
               Published = Convert.ToDateTime(e.Element(ns+"published").Value),
               PictureUrl = getPicasaUrl(e.Element(ns+"content").Value),
               Tags = (from t in e.Elements(ns+"category")
                          select t.Attribute("term").Value).Skip(1).ToList(),
               EntryHtml = e.Element(ns+"content").Value                              
              };

The most important property is PictureUrl for PivotViewer, in my case it was always on Picasa, so I took the first Picasa image url in every entry. I left out entries without pictures from the CXML generation. The last step was to download the associated image for each Entry, and write back the path:

WebClient wc = new WebClient();
int id = 0;
foreach (var item in entries)
{
 if (!String.IsNullOrEmpty(item.PictureUrl))
 {
  item.ImgPath = Path.Combine("C:\\Temp\\pivot_images", String.Format("{0}.jpg", id) );    
  wc.DownloadFile(item.PictureUrl, item.ImgPath);
  item.ID = id;
  id++;
 }
}

After this the xml was parsed, and was ready for CXML and PAuthor.

You can get source from here.

Hosting PivotViewer and Simple collections

Hosting the PivotViewer is easy, once you have a web host space or server, you can just copy the app and the collection there. The only problem I met hosting the collection was that the server didn’t know about the CXML and Deep Zoom file types. So I needed to add the following MIME types:

  • .cxml – text/xml
  • .dzi – text/xml
  • .dzc – text/xml

In case your server doesn’t know about Silverlight (I think this is very rare now), you’ll also need to register the .xap MIME type (application/x-silverlight-app).

Summary

PivotViewer is a very useful control with rich functionality, and can offer an easy way out if you need a spectacular gallery. It has also deeper functions that this article didn’t cover, like the Just in Time collections, or the customization of the control. The good news is that we don’t have to rely on the Excel plugin, we can generate collections however we like and have it integrated with our apps.

About the Author

Levente MihalyMy name is Levente Mihaly. I've entered the .NET world in 2006, and after graduating at Budapest University of Technology (Msc in Computer Science) I started working as a .NET software developer. After meeting technologies like WinForms and ASP.NET, Silverlight quickly became my favourite platform from the early Silverlight 2 beta stages. I was always interested in mobile development and now I'm very happy since with Windows Phone 7 I can easily expand my knowledge to the mobile world. Nowadays beside following Silverlight I'm focusing on WP7 in my free time. I'm also the runner-up of the 2010 Silverlight ecoContest. You can reach me on twitter (@leventemihaly).
 


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *