RSS

Add, Remove & Change Cxml Items in PivotViewer

04 Sep

The first thing that most people will do with PivotViewer v2 in SL5RC is look at an existing static collection that they’ve made. The mechanism for loading an existing cxml file in Silverlight 5 has changed since the EAP releases, but is still very straight forward. Assuming that you already have a PivotViewer control called pv in your xaml, this is all you need (I’m using one of Tony Champion‘s test collections)…

var collection = new CxmlCollectionSource(new Uri("http://labs.championds.com/MIX10/MIX10Collection.cxml"));
collection.StateChanged += (sender, e) =>
{
  if (e.NewState == Loaded)
  {
    pv.PivotProperties = (IList)collection.ItemProperties;
    pv.ItemTemplates = collection.ItemTemplates;
    pv.ItemsSource = collection.Items;
  }
}

Now, Silverlight 5 promised some dynamic control over these collections, but we need to do a little work first. Firstly, the Items property of CxmlCollectionSource is of type ReadOnlyObservableCollection<PivotViewerItem>. This stops us adding and removing any items. What we need to do is construct an ObservableCollection<PivotViewerItem> for our items and then bind that to the control. We’ll keep a reference to this new collection so that we don’t have to keep casting the ItemsSource. This changes our code to:

public partial class MainPage : UserControl
{
  private ObservableCollection<PivotViewerItem> _items;

  public MainPage()
  {
    InitialiseComponent();

    Loaded += LoadCollection();
  }

  private void LoadCollection()
  {
    var collection = new CxmlCollectionSource(new Uri("http://labs.championds.com/MIX10/MIX10Collection.cxml"));

    collection.StateChanged += (sender, e) =>
    {
      if (e.NewState == Loaded)
      {
        _items = new ObservableCollection<PivotViewerItem>(collection.Items);

        pv.PivotProperties = collection.ItemProperties;
        pv.ItemTemplates = collection.ItemTemplates;
        pv.ItemsSource = _items;
      }
    }
  }

Removing Items

Now that we have our Observable Collection, removing items is trivially simple. Assuming you add a Button to your xaml, this is how we would remove the currently selected item:

private void btnRemove_Click(object sender, RoutedEventArgs e)
{
  if (pv.SelectedIndex >= 0)
    _items.RemoveAt(pv.SelectIndex);
}

Adding Items

Adding new items is harder than removing them. There are a number of required properties that need to be specified and then there are the optional ones that the collection’s designer has specified. First off, we’ll create an extension method that will make it easier to find properties on a PivotViewer:

public static class PivotViewerExtensions
{
  public static PivotViewerProperty FindProperty(this PivotViewer pv, string displayName)
  {
    var query = pv.PivotProperties.AsQueryable().OfType<PivotViewerProperty>();

    return query.Where(q => q.DisplayName == displayName).FirstOrDefault();
  }
}

Next we’ll add a Factory class that will help us create a PivotViewerItem with the required properties. The ‘visualCollectionSource’ parameter specifies the xml file for a single DeepZoom image (either .dzi or .xml):

public static class PivotViewerItemFactory
{
  public static PivotViewerItem Create(PivotViewer parentViewer, string id, string name, string description, string visualCollectionSource)
  {
    var item = new PivotViewerItem(id);

    item.Add(parentViewer.FindProperty("Name"), new List<string> { name });
    item.Add(parentViewer.FindProperty("Description"), new List<string> { description });
    item.Add(parentViewer.FindProperty("VisualCollectionSource"), new List<string> { visualCollectionSource });

    return item;
  }
}

Finally, we need to write some client code that will create a ‘standard’ PivotViewerItem and then add any other properties we want:

var newItem = PivotViewerItemFactory.Create(pv, (_data.Count + 1).ToString(), "New Name", "New Description", "http://labs.championds.com/MIX10/MIXCollectionTest_files2/lcy4icqv.3r5.xml");

// Now add the optional properties...
newItem.Add(pv.FindProperty("Code"), new List<string> { "A New Code" });
newItem.Add(pv.FindProperty("Room"), new List<string> { "A New Room" });
newItem.Add(pv.FindProperty("Speakers"), new List<string> { "A New Speaker" });
newItem.Add(pv.FindProperty("Video"), new List<PivotViewerHyperlink> {  new PivotViewerHyperlink("My video", new Uri("http://a.com/b.wmv")) });

// Finally, add our new item to the collection...
_data.Add(newItem);

Changing Items

The issue we have here is that PivotViewerItem gives us no way to access or change the value of any PivotViewerProperty values. What we have to do is remove the property we want to change and then re-add it with the new value. Again we’ll create a convenient extension method for this:

public static class PivotViewerItemExtensions
{
  public static void Replace(this PivotViewerItem item, PivotViewer pivotViewer, string displayName, IList newValues)
  {
    var property = pivotViewer.FindProperty(displayName);

    if (property == null)
      throw new PropertyNotFoundException(displayName);

    item.Remove(property);

    item.Add(property, newValues);
  }

  public class PropertyNotFoundException : Exception
  {
    public readonly string DisplayName;

    public PropertyNotFoundException(string displayName)
      : base("Failed to find a property with Displayname of " + displayName)
    {
      DisplayName = displayName;
    }
  }
}

Calling this from client code then only requires us to know the name of the property and the new value:

if (pv.SelectedIndex >= 0)
  _data[pv.SelectedIndex].Replace(pv, "Room", new List<string> { "A New Room" });

You can download the complete source code here and follow me @GoodCoffeeCode for more blog posts on PivotViewer.

 
4 Comments

Posted by on September 4, 2011 in PivotViewer

 

Tags: , , ,

4 responses to “Add, Remove & Change Cxml Items in PivotViewer

  1. João Macedo Pinto

    February 9, 2012 at 4:47 pm

    Hello,

    Following your other post, the xaml template for a deepzoom PivotViewerItemTemplate would be:

    So if every PivotViewerItem have to have a private facet “VisualCollectionSource” why don’t they need a facet “VisualImageId”? Does this one gets created automatically or is binded to the Id property of the PivotViewerItem?

    Thanks for your help, great article

     
  2. João Macedo Pinto

    February 9, 2012 at 5:16 pm

    Ok, I figured it out. You have to create another private facet in the PivotViewerItem factory, named “VisualImageId” and bind it to the Id property of the afore mentioned item, so that the PivotViewerMultiScaleSubImageHost container works correctly.

    Ps: The xaml template I pasted in the previous post didn’t show, I don’t know why (maybe because of the tags it had)

     
  3. Rich

    June 18, 2012 at 3:54 am

    Hi There,
    So would you follow this strategy if you wanted to use sharepoint 2010 user profile fields – for example ‘Skills’ – most skills are added into one column separated by a semi colon – would you use a string / split type method to allow all a persons skills to be able to be filtered upon?

    Any ideas would be awesome!
    Rich

     

Leave a reply to Rich Cancel reply