Tag Archives: PivotViewer

Customizing the PivotViewer

For those of you who haven’t seen it yet, Microsoft released a very powerful data visualization control called the PivotViewer. It leverages the deep zoom feature to perform filtering and sorting of complex data objects allowing the user to navigate through dense data in an efficient (and fun) way. The only problem is that it is a closed source library and the control implementation doesn’t allow for templates or styling.

Luckily there are a few tricks you can play to make substantial changes to the appearance and behavior of the PivotViewer if you’re willing to work with the codebehind. Xpert360 Ltd. published a blog post titled Adventures with PivotViewer┬áin which they explain how to effectively re-skin the PivotViewer. After rifling through the assemblies using Reflector I found that you can get access to much more of the core functionality by leveraging some of the classes exposed through the library’s internal namespace.

The PivotViewer control that they give you access to through the System.Windows.Pivot namespace is a wrapper around the CollectionViewer MVVM structure exposed in the Microsoft.Pivot.Internal.* namespaces. Almost all of the members that you would wish to be able to modify are exposed by obtaining references to the view, view model, and model. This can be achieved trivially by subclassing PivotViewer and overriding the OnApplyTemplate() function.

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    Grid partContainer = (Grid)this.GetTemplateChild("PART_Container");

    CollectionViewerView cvv =
        ((CollectionViewerView)(partContainer).Children[0]);
    CollectionViewerViewModel cvvm =
        (CollectionViewerViewModel)ViewBehaviors.GetViewModel(cvv);
    CollectionViewerModel cvm =
        (CollectionViewerModel)ViewBehaviors.GetModel(cvv);
}

Once you obtain these three references, the sky is the limit. One example of what I was able to use these for was changing the default view. The data that I am displaying lends itself almost exclusively to the histogram view, but the default is the grid view. Whenever the sort category was changed automatically by drilling down through a single histogram column the view was reset to grid view. This resulted in an unpleasant user experience because it required mousing all the way across the screen to keep setting the view back to histogram mode.

I was again able to leverage Reflector to find that the views are initialized into a ViewSelectionState object exposed through a property on the model. Each view has an entry in the ViewDataObjects array on the ViewSelectionState, with the grid view data object in the first index and the histogram in the second. By reversing them in the OnApplyTemplate() function I was able to change the default behavior of the PivotViewer.

cvm.ViewSelectionState.ViewDataObjects =
    cvm.ViewSelectionState.ViewDataObjects
        .Reverse()
        .ToArray();

Another useful feature I was able to add was a GridSplitter next to the filter panel. Many of our values are long and were being trimmed by the filter panel, so by adding the GridSplitter I was able to give users access to the complete values as necessary. This improvement leveraged the view reference to modify the visual tree.

FrameworkElement filterPane =
    (FrameworkElement)cvv.FindName("PART_FilterPaneContainer");
Grid filterParent = (Grid)filterPane.Parent;

GridSplitter splitter = new GridSplitter();
splitter.SetValue(Grid.ColumnProperty, 0);
splitter.Style =
    (Style)App.Current.Resources["RightVerticalGridSplitterStyle"];
BindingOperations.SetBinding(splitter, GridSplitter.VisibilityProperty,
    new Binding("Opacity")
    {
        Source = filterPane,
        // Custom implementation converting 0 to false, other to true
        Converter = new VisibilityConverter()
    });
filterParent.Children.Add(splitter);

filterPane.Margin = new Thickness(
    filterPane.Margin.Left,
    filterPane.Margin.Top,
    filterPane.Margin.Right + splitter.Width,
    filterPane.Margin.Bottom);

Please leave a post if you come up with any other useful tweaks!

Advertisements