Follow Me on Instagram Subscribe via RSS Feed

PivotViewer Basics : Client-side Collections

November 21, 2011

With the release of Silverlight 5 just around the corner, I thought I would start a series on the new PivotViewer and how to get up and running with it.  We will start the series off by exploring how to create a client side collection.

One of the most anticipated features of the new PivotViewer is the ability to create client-side collections.  PivotViewer accomplishes this by utilizing the traditional ItemsSource property for data and a form of XAML data templates to define the trading card.  It is interesting to note that the fundamental core of PivotViewer is still using the DeepZoom technology.  If you are thinking that DeepZoom only handles images and not XAML, you would be correct.  Under the hood, PivotViewer is rendering the data templates to images and then adding those images to DeepZoom.  Remembering that little tidbit will help later down the road.


The Basics

First things first, let’s get a PivotViewer control into our application.  One of the great changes in the PivotViewer is that instead of 4 libraries we are down to one.  So to include the PivotViewer in your application, you simple need to add a reference to System.Windows.Controls.Pivot.

Now we can add a PivotViewer to our application like this:

<Grid x:Name="LayoutRoot" Background="White">
    <pivot:PivotViewer x:Name="pViewer"/>
</Grid>

Doesn’t get much simpler than that, now does it?

Getting your Data

In order to focus in our demo, we are going to generate some dummy data on the client.  Of course, you are most likely going to be pulling in more meaningful data (let’s hope anyway) and you can still do that in the traditional ways.

public class DemoItem : INotifyPropertyChanged
{
    public string ShortName { get; set; }

    private string _color;
    public string Color
    {
        get { return _color; }
        set 
        {
            _color = value;
            NotifyProperty("Color");
        }
    }

    private int _value;
    public int Value
    {
        get { return _value; }
        set
        {
            _value = value;
            NotifyProperty("Value");
        }
    }


    private string _data1;
    public string Data1
    {
        get { return _data1; }
        set
        {
            _data1 = value;
            NotifyProperty("Data1");
        }
    }


    private DateTime _stamp;
    public DateTime Stamp
    {
        get { return _stamp; }
        set
        {
            _stamp = value;
            NotifyProperty("Stamp");
        }
    }


    public static ObservableCollection<DemoItem> BuildData()
    {
        var data = new ObservableCollection<DemoItem>();

        for (int i = 0; i < 100; i++)
        {
            var itm = new DemoItem()
             { ShortName = i.ToString("000") };

            var mod = i % 3;

            switch (mod)
            {
                case 0:
                    itm.Color = "Blue";
                    break;
                case 1:
                    itm.Color = "Red";
                    break;
                case 2:
                    itm.Color = "Green";
                    break;
            }

            itm.Data1 = i % 2 == 0 ? "Even" : "Odd";
            itm.Stamp = DateTime.Now.AddDays(-1*i);
            data.Add(itm);
        }

        return data;
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyProperty(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, 
              new PropertyChangedEventArgs(propName));
    }

    #endregion

}

As you can see there isn’t a whole lot going on here.  The BuildData() method knocks out some data that will let us see PivotViewer in action.  I used a string for the color property so it would map to a PivotViewer property cleanly.

Now that we can generate our data, how do we get it into PivotViewer?  It’s as simple as using the ItemsSource property (yep just like a normal Silverlight control).

pViewer.ItemsSource = DemoItem.BuildData();

Setting up your PivotProperties

In the original PivotViewer properties were defined as facets.  In the new version we now have PivotProperties.  There hasn’t been any changes to the number or types of properties.  The four available types are :

  • PivotViewerStringProperty
  • PivotViewerNumericProperty
  • PivotViewerDateTimeProperty
  • PivotViewerLinkProperty

PivotViewer has a PivotProperties property (try saying that 3 times fast) that you can set via code or in XAML.  For completeness sake, let’s take a look how each would look.  Both of these examples yield the same result.

PivotViewer Properties in XAML:

<pivot:PivotViewer x:Name="pViewer">
    <pivot:PivotViewer.PivotProperties>
        <pivot:PivotViewerNumericProperty 
            Id="Value" 
            Options="None" 
            Binding="{Binding Value}"/>
        <pivot:PivotViewerStringProperty 
            Id="Data1" 
            Options="CanFilter,CanSearchText" 
            Binding="{Binding Data1}"/>
        <pivot:PivotViewerStringProperty 
            Id="Color" 
            Options="CanFilter,CanSearchText" 
            Binding="{Binding Color}"/>
        <pivot:PivotViewerDateTimeProperty 
            Id="Stamp" 
            Options="CanFilter" 
            Binding="{Binding Stamp}"/>
    </pivot:PivotViewer.PivotProperties>
</pivot:PivotViewer>

PivotViewer Properties in code behind:

pViewer.PivotProperties = new List<PivotViewerProperty>()
        {
            new PivotViewerNumericProperty()
                {
                    Binding = new Binding("Value"),
                    Id = "Value",
                    Options = PivotViewerPropertyOptions.None
                },
            new PivotViewerStringProperty()
                {
                    Binding = new Binding("Data1"),
                    Id = "Data1",
                    Options = PivotViewerPropertyOptions.CanFilter 
                    | PivotViewerPropertyOptions.CanSearchText
                },
            new PivotViewerStringProperty()
                {
                    Binding = new Binding("Color"),
                    Id = "Color",
                    Options = PivotViewerPropertyOptions.CanFilter 
                    | PivotViewerPropertyOptions.CanSearchText
                },
            new PivotViewerDateTimeProperty()
                {
                    Binding = new Binding("Stamp"),
                    Id = "Stamp",
                    Options = PivotViewerPropertyOptions.CanFilter
                }


        };

There are only a few options for a PivotProperty, so I thought it would be worth listing them.  Here is a break down of each property and how it effects how the PivotProperty is used inside of PivotViewer

PivotProperty Options

None No options are set
Private If set, it will not display property in detail pane
CanFilter Shows the property in the filter pane and the sort drop down box
CanSearchText The search box will search for this property for matches
Wrapping Text Will wrap the text within the detail pane

Trading Cards

Now we have our data and have mapped the data to the a collection of PivotProperties, what’s next?  The last set it to define what the trading card is going to look like. For this post we are going to look at a basic solution which we will expand upon in the next post. 

XAML data templates in PivotViewer take the shape of a PivotViewerItemTemplate.  PivotViewer has a ItemTemplates property that is used to map one or more templates for your trading cards.  For our case we are going to create a simple trading card that consists of a Border that is filled with the card’s Color property and put a ShortName property in the middle of the Border.  The template will look something like this:

<pivot:PivotViewerItemTemplate x:Key="DemoTemplate">
    <Border Width="300" Height="300" 
            Background="{Binding Color, 
                    Converter={StaticResource colorConverter}}">
        <TextBlock Text="{Binding ShortName}" FontSize="20" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" />
    </Border>
</pivot:PivotViewerItemTemplate>

You might notice that I added a value converter to the background.  Since we are storing the color as a string, we need to map it to a SolidColorBrush.  SL doesn’t expose all of the Color methods you find in .NET, so we have to be a bit clever in getting the color mapped.  Here is the value converter that I am using:

public class TextToSolidColorConverter : IValueConverter
{

    public object Convert(object value, 
                        Type targetType, 
                        object parameter, 
                        CultureInfo culture)
    {
        var xaml = "<SolidColorBrush " +
            "xmlns='http://schemas.microsoft.com/client/2007' " +
            "Color=\"" + value.ToString() + "\"/>";
        return XamlReader.Load(xaml);
    }

    public object ConvertBack(object value, 
                        Type targetType, 
                        object parameter, 
                        CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

With our template added as a resource, we can set the template in code with the following:

pViewer.ItemTemplates = new PivotViewerItemTemplateCollection()
    {
        (PivotViewerItemTemplate) Resources["DemoTemplate"]
    };

And that is really all there is to it.  If we run our solution, we should get something like this:

image

You can download the code here : PVB01_ClientSideCreation.zip

Next Post…

In the next post we will explore the trading cards a little bit more and touch briefly on some design tips.

Series NavigationPivotViewer Basics : Semantic Zoom >>

Comments (13)

Trackback URL | Comments RSS Feed

  1. satish says:

    Source code link is not working… can you please check?

  2. Tony Champion says:

    Thanks. I’ve corrected the links.

  3. SRK says:

    Hi Tony,
    Is it possible to add datagrid/Grid on SL5 pivotviewer tile and set ItemSource?
    I tried that. But It’s giving me Protected access violation. Is that possible??

  4. SRK says:

    I need this little urgent. Please kindly reply ASAP.

  5. Tony Champion says:

    You are going to have to look at hacking it in thru the Visual Tree. That’s how it was done in the v1. The API doesn’t support adding additional views.

  6. SRK says:

    Thanks Tony.

    May be I confused you. I don’t want to change pivotviewer view. I want to add datagrid/charts on pivotview collection tile (Tile generated in pivotviewer. I mean rectangle.). I can able add TextBox, TextBlock and other controls. But I am not able to add DataGrid/charts. It’s failing at ItemSource setting.

  7. Tony Champion says:

    Gotcha. Truth be told, I haven’t tried that yet. However, it is worth looking into. I would hope that it would work. If not, then a work around needs to be found.

  8. SRK says:

    Please let me know. If that works for you.

  9. SRK says:

    Did that worked for you?

  10. SRK says:

    After installing Silverlight 5 latest release. The error message converted as follows,

    Line: 56
    Error: Unhandled Error in Silverlight Application Code: 4008
    Category: RuntimeError
    Message: Layout cycle detected. Layout could not complete.
    MethodName:

    Then i decreased the size of the collection then, it worked. It’s the problem with my collection size not control.

  11. Tony Champion says:

    Gotcha. Have you tried to load your collection in chunks? Adding smaller sets to let it process it?

  12. VK says:

    Great tutorial! Thanks!

  13. Nick says:

    Hi Tony,

    Thanks for the great tutorial! I’ve abandoned CXML and started binding directly to entities retrieved through a RIA service.

    Is there a way to continue to use deep zoom image stacks + these client-side collections? I’ve tried to use the PivotViewerMultiSizeImage control in the ItemTemplate, but it doesn’t seem to accept a .dzi file anywhere, and if I give it images that I’ve scaled down myself, it seems to get stuck on one zoom level. Code here:

    It didn’t seem to render without specifying a Width/Height for the MultiSizeImage control, but then it doesn’t zoom. If you’ve got any ideas, I’d really appreciate it!

Leave a Reply