Controlling a Silverlight PivotViewer control from JavaScript with ScriptableMember

The Silverlight PivotViewer control offers a really compelling experience for exploring and sorting through visual data like photographs. It includes a number of built-in controls, but the other day a customer complained that he could not just hand the control to his Web developers for them to integrate it into Web pages – they had to be Silverlight developers in order to maintain a “shell” around the PivotViewer control, in order to do things like change the current collection or filters.

It turns out it is very easy to expose Silverlight methods to the Web page so that they can be called from JavaScript. This allows HTML controls like buttons or links to call methods in a Silverlight application to control it.

In the case of the PivotViewer, it does not expose any methods by default, but it is very easy to change that by exposing your own methods.

What you will do is create a very-bare bones Silverlight application with a single PivotViewer control in it. In this case I have extended the default PivotViewer class (in order to add some custom actions), but it would work without extending the control.

   1: <UserControl x:Class="PivotViewerJS.MainPage"
  2:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  4:     xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
  5:     xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  6:     xmlns:local="clr-namespace:PivotViewerJS"
  7:     mc:Ignorable="d"
  8:     d:DesignHeight="300" d:DesignWidth="400">
  9: 
 10:     <Grid x:Name="LayoutRoot" Background="White">
 11:         <local:PivotViewerControl x:Name="PivotViewerControl" ItemActionExecuted="PivotViewerControl_ItemActionExecuted" />
 12:     </Grid>
 13: </UserControl>

Then in the code-behind for your main page, you will add the methods you want to expose to the Web page. In my example, the LoadCollection() method will let you change the collection to display, along with the default filter. As you can see in the MainPage_Loaded() method, I use the HtmlPage.RegisterScriptableObject() method to expose my Silverlight application to JavaScript under the name “PivotViewer”. The methods that you actually want exposed will have to be decorated with the ScriptableMember attribute.

   1: public partial class MainPage : UserControl
  2: {
  3:   public MainPage()
  4:    {
  5:        InitializeComponent();
  6:       Loaded += new RoutedEventHandler(MainPage_Loaded);
  7:   }
  8: 
  9:    void MainPage_Loaded(object sender, RoutedEventArgs e)
 10:   {
 11:        // Register scriptable object
 12: 
 13:         HtmlPage.RegisterScriptableObject("PivotViewer", this);
 14:        
 15:         // Load the collection
 16: 
 17:         string collection = App.Current.Host.InitParams["collection"].ToString();
 18:      PivotViewerControl.LoadCollection(collection, string.Empty);
 19:     }
 20: 
 21:    /*
 22:    * These methods will be accessible from JavaScript
 23:   */
 24: 
 25:  [ScriptableMember]
 26:   public void LoadCollection(string uri, string state)
 27:     {
 28:        //MessageBox.Show(uri + " " + state);
 29:         PivotViewerControl.LoadCollection(uri, state);
 30:   }

Once this is done, you will add an onLoad parameter to your Silverlight plug-in HTML markup in order to initialize a JavaScript variable to access the Silverlight host:

   1: <param name="onLoad" value="pluginLoaded" />

And a couple buttons to call JavaScript functions that will call into the Silverlight control:

   1: <input type="button" value=" MSDN Magazine " onclick="loadMagazines()" />
  2: <input type="button" value=" Foire au Vin " onclick="loadVins()" />

And now for the JavaScript code!

The pluginLoaded() function will be called once your Silverlight application is loaded, and all you need to do is position a global variable to keep track of the Silverlight host. The two other function are called when the HTML buttons are clicked, and as you can see, they can directly call the LoadCollection() method we defined in the Silverlight application!

   1: var slCtl = null;
  2: function pluginLoaded(sender, args) {
  3:     slCtl = sender.getHost();
  4: }
  5: function loadMagazines() {
  6:     slCtl.Content.PivotViewer.LoadCollection("https://az7446.vo.msecnd.net/msdn-magazine/msdnmagazine.cxml", "%24facet0%24=Issue%20Date&%24view%24=2");
  7: }
  8: function loadVins() {
  9:     slCtl.Content.PivotViewer.LoadCollection("https://tcontepub.blob.core.windows.net/pivot/CatalogueBorneCollection/CatalogueBorne_Collection.cxml", "");
 10: }

In my example, I am simply changing the Pivot collection to another one, and setting a default filter.

Hope this helps!