Sorting a Pivot Viewer 2.0’s Group Items

by jasonrshaver 23. January 2012 13:54

Let’s just say you want to make a PivotViewer for the Periodic Table of Elements.  And lets just say you want it to look something like you remember it in your middle school science book.  You get the data, add a ‘Group’ value to control the columns, but the vertical order is all wrong. 

Here is a quick way to fix this.  I don’t think anyone documented the method for doing this before, so if you have any questions, let me know!

And add the following to your PivotViewer XAML

<pv:PivotViewer x:Name="myPivotViewer" ItemsSource="{Binding}" BorderThickness="0" ViewChanged="myPivotViewer_ViewChanged">

And in code behind, add:

  1. private System.Windows.Controls.Pivot.PivotViewerView _OldView = null;
  2.  
  3. private void myPivotViewer_ViewChanged(object sender, EventArgs e)
  4. {
  5.     if (_OldView != null)
  6.     {
  7.         _OldView.PivotViewerViewUpdating -=
  8.             new EventHandler<PivotViewerViewUpdatingEventArgs>
  9.                 (View_PivotViewerViewUpdating);
  10.     }
  11.     _OldView = myPivotViewer.View;
  12.     _OldView.PivotViewerViewUpdating +=
  13.         new EventHandler<PivotViewerViewUpdatingEventArgs>
  14.             (View_PivotViewerViewUpdating);
  15. }
  16.  
  17. private void View_PivotViewerViewUpdating(object sender
  18.     , PivotViewerViewUpdatingEventArgs e)
  19. {
  20.     e.CollectionView.SortDescriptions.Add(
  21.         new SortDescription("AtomicNumber"
  22.             , ListSortDirection.Ascending));
  23. }

It is very important to make sure you are cleaning up your PivotViewerViewUpdating event reference as shown!

And Ta-Da, you have the following work of art:

clip_image002

Sorry for making such a quick example without any downloadable source, but I figured it was good to get this information out there!

Tags: ,

Blog

PivotViewer V2 Series, Part 4: Tradecards

by jasonrshaver 3. November 2011 09:52

Other articles in the PivotViewer V2 Series:

  • Part 1: Intro
  • Part 2: Creating a Project
  • Part 3: Receiving OData
  • Part 4: Trade Cards
  • Part 5: Pivot Panel
  • Part 6: Links and Interactions
  • Part 7: Pivot Properties
  • Part 8: Multi-facacited Pivot Properties
  • Part 9: Large Data Sets
  • Part 10: Teaching Your Users
  • Part 11: Exposing Your Own Data
  • Part 12: Advanced Sorting
  • Part 13: Advanced Trade Cards
  • Part 14: Working with Excel

This entry builds upon the previous parts.  You can jump right to this point by downloading the following ZIP file, PivotViewerSeries_Part3.zip.

Creating Visuals

So, we have data put into the PivotViewer, but we are missing some exciting visuals.  To create all of the visuals you will see here, we just need to make some changes to MainPage.xaml’s PivotViewer control.

Trade Cards

The visuals each data item in a collection is call a “Trade Card”.  One of the exciting features for PivotViewer 2.0 is that trade cards can be much more dynamic than the previous CXML method of the past.  For example, we can show different trade cards depending on how zoomed in we are.  Or we can show more information as needed. 

Creating a Trade Card

First we need to create a default trade card for our Netflix data.  To do this, we are just going to create a new DataTemplate and attaching it to our PivotViewer control:

  1. <pivot:PivotViewer ItemsSource="{Binding Titles}">
  2.     <pivot:PivotViewer.ItemTemplates>
  3.         <pivot:PivotViewerItemTemplate>
  4.             <Border BorderBrush="Black" BorderThickness="1">
  5.                 <Image Source="{Binding Path=BoxArt.SmallUrl
  6.                             , Converter={StaticResource ImageConverter}
  7.                             , Mode=OneTime}"
  8.                        Width="65" Height="89" />
  9.             </Border>
  10.         </pivot:PivotViewerItemTemplate>
  11.     </pivot:PivotViewer.ItemTemplates>
  12. </pivot:PivotViewer>

And we will also need to add the ImageConverter that will take our image’s URL string and convert it to a Uri object:

  1. using System;
  2. using System.Globalization;
  3. using System.Windows.Data;
  4. using System.Windows.Media;
  5. using System.Windows.Media.Imaging;
  6.  
  7. namespace PivotViewerSeries.Silverlight
  8. {
  9.     public class ImageConverter : IValueConverter
  10.     {
  11.         public object Convert(object value
  12.             , Type targetType
  13.             , object parameter
  14.             , CultureInfo culture)
  15.         {
  16.             ImageSource Output = null;
  17.             if (value != null)
  18.                 Output = new BitmapImage(new Uri(value.ToString()));
  19.             else
  20.                 Output = new BitmapImage();
  21.             return Output;
  22.         }
  23.  
  24.         public object ConvertBack(object value
  25.             , Type targetType
  26.             , object parameter
  27.             , CultureInfo culture)
  28.         {
  29.             throw new NotImplementedException();
  30.         }
  31.     }
  32. }

And finally, reference that converter in our App.xaml:

  1. <Application.Resources>
  2.     <vm:ViewModelLocator x:Key="Locator"
  3.                         d:IsDataSource="True" />
  4.     <this:ImageConverter x:Key="ImageConverter" />
  5. </Application.Resources>

Now when we run our application, we will get the following result.  Be aware, that depending on your connection, it may take a bit for data to load from Netflix and for the images to download.

image

Using Multiple Trade Cards

To use different trade cards at different zoom levels, we can add more PivotViewerItemTemplate objects to the ItemTemplates collection, setting the MaxWidth property each time to tell the object how wide a template can go before showing the next larger one. 

It is important that the aspect ratio for each trade card must be the same, otherwise you will start to see some weird sizing issues. 

First, lets update our small trade card with the MaxWidth:

  1. <!-- Small Trade Card -->
  2. <pivot:PivotViewerItemTemplate MaxWidth="100">
  3.     <Image Source="{Binding Path=BoxArt.SmallUrl
  4.               ,Converter={StaticResource ImageConverter}}"
  5.        Width="65" Height="89" />
  6. </pivot:PivotViewerItemTemplate>

And create a new medium sized trade card that includes the movie title:

  1. <!-- Medium Trade Card -->
  2. <pivot:PivotViewerItemTemplate MaxWidth="500">
  3.     <Border BorderBrush="Black" BorderThickness="0">
  4.         <StackPanel Orientation="Vertical">
  5.             <TextBlock Text="{Binding Name}"
  6.                       Width="176"
  7.                       FontSize="14"
  8.                       FontWeight="Bold"
  9.                       TextWrapping="Wrap"
  10.                       HorizontalAlignment="Center"/>
  11.             <Image Source="{Binding Path=BoxArt.MediumUrl
  12.               ,Converter={StaticResource ImageConverter}}"
  13.        Width="176" Height="240" />
  14.         </StackPanel>
  15.     </Border>
  16. </pivot:PivotViewerItemTemplate>

Which, when zoomed in enough will look like this:

image

And finally, lets create a large trade card that can give us some more information:

  1. <!-- Large Trade Card -->
  2. <pivot:PivotViewerItemTemplate>
  3.                             <Viewbox Width="1200" Height="1636" MinHeight="1636" MinWidth="1200">
  4.     <Grid Width="600" Height="818" >
  5.         <Grid.RowDefinitions>
  6.             <RowDefinition Height="614" />
  7.             <RowDefinition Height="204" />
  8.         </Grid.RowDefinitions>
  9.         <Grid.ColumnDefinitions>
  10.             <ColumnDefinition Width="450" />
  11.             <ColumnDefinition Width="150" />
  12.         </Grid.ColumnDefinitions>
  13.         <Image Grid.Row="0"
  14.               Grid.Column="0"
  15.               Source="{Binding Path=BoxArt.LargeUrl
  16.               ,Converter={StaticResource ImageConverter}}"
  17.               Width="450" Height="614" />
  18.         <StackPanel Grid.Row="0"
  19.                    Grid.Column="1"
  20.                    Orientation="Vertical"
  21.                    Margin="2">
  22.             <TextBlock Text="{Binding Rating
  23.                             , StringFormat='MPAA Rating: {0}'}" />
  24.             <TextBlock Text="{Binding AverageRating
  25.                             , StringFormat='NetFlix Rating: {0} / 5'}" />
  26.             <TextBlock Text="{Binding ReleaseYear
  27.                             , StringFormat='Release Year: {0}'}" />
  28.             <TextBlock Text="{Binding Runtime
  29.                             , StringFormat='Length: {0} seconds'}" />                                    
  30.         </StackPanel>
  31.         <StackPanel Grid.Row="1"
  32.                    Grid.Column="0"
  33.                    Grid.ColumnSpan="2"
  34.                    Orientation="Vertical"
  35.                    Margin="10">
  36.             <TextBlock Text="{Binding Name}"
  37.                       FontSize="36"
  38.                       FontWeight="Bold"
  39.                       TextWrapping="Wrap"/>
  40.             <TextBlock Text="{Binding Synopsis}"
  41.                       FontSize="16"
  42.                       TextWrapping="Wrap"/>
  43.         </StackPanel>
  44.         </Grid>
  45.     </Viewbox>
  46. </pivot:PivotViewerItemTemplate>

Which results in something like the following:

image

If you find your project ‘skipping’ a template, it is likely that your templates are too close in size to each other.  For PivotViewer to take the time to optimize and repaint all the cards, it need to make sure it is worth it, and a 50 or 100 pixel change is viewed as not being worth wild. 

Tags: , ,

Blog

PivotViewer V2 Series, Part 3: Receiving OData

by JasonRShaver 31. October 2011 17:19

Other articles in the PivotViewer V2 Series:

  • Part 1: Intro
  • Part 2: Creating a Project
  • Part 3: Receiving OData
  • Part 4: Tradecards

Connecting To an OData Collection

Lets get some data for PivotViewer to play with.  The easiest protocol for getting data for PivotViewer currently is OData.  This, by far, not the only way to consume data, but PivotViewer is most useful with large data collections and OData is a good way to navigate those feeds (when available). 

A good feed to play with for now is the Netflix OData Catalog API.

Right click on the PivotViewerSeries.Silverlight project and select Add Service Reference:

image

Connecting ViewModel to Data

To get the data from the ‘cloud’ to our PivotViewer, we need to make a stop at the ViewModel first.  Load up ViewModel\MainViewModel.cs and add the following code:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Data.Services.Client;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using GalaSoft.MvvmLight;
  8. using PivotViewerSeries.Silverlight.NetflixOData;
  9.  
  10. namespace PivotViewerSeries.Silverlight.ViewModel
  11. {
  12.     public class MainViewModel : ViewModelBase
  13.     {
  14.         private Uri _DataUri
  15.             = new Uri("http://odata.netflix.com/Catalog/");
  16.  
  17.         private IEnumerable<Title> _Titles;
  18.         public IEnumerable<Title> Titles
  19.         {
  20.             get
  21.             {
  22.                 if (_Titles == null)
  23.                     return new ObservableCollection<Title>();
  24.                 return _Titles;
  25.             }
  26.             set
  27.             {
  28.                 if (_Titles == value)
  29.                     return;
  30.                 _Titles = value;
  31.                 RaisePropertyChanged("Titles");
  32.             }
  33.         }
  34.  
  35.         public MainViewModel()
  36.         {
  37.             var MyContext = new NetflixCatalog(_DataUri);
  38.             var MyData = new DataServiceCollection<Title>(MyContext);
  39.             MyData.LoadCompleted += (o1, e1) =>
  40.             {
  41.                 // This block of code runs when we get results.
  42.  
  43.                 if (e1.Error != null)
  44.                     throw e1.Error;
  45.  
  46.                 if (MyData.Continuation != null)
  47.                 {
  48.                     MyData.LoadNextPartialSetAsync();
  49.                     Titles = MyData;
  50.                 }
  51.                 else
  52.                     Titles = MyData;
  53.             };
  54.             var MyQuery = from c in MyContext.Titles.Take(1000)
  55.                           select c;
  56.             MyData.LoadAsync(MyQuery);
  57.         }
  58.     }
  59. }

This block of code create a collection to hold our list of NetFlix titles.  Then, when our ViewModel is created, we asynchronously load our collection of data via LINQ.

Of special note here is that the DataServiceCollection will automatically page the collection in groups of 500.  When that happens, we get a chance to process the current incomplete set, and call LoadNextPartialSetAsync to get the next set of results. 

Connecting PivotViewer to ViewModel

The last step here is to attached our list of titles to our PivotViewer.  This is much easier to do now with PivotViewer 2.0.  We just have to bind to the ItemsSource property:

  1. <UserControl x:Class="PivotViewerSeries.Silverlight.MainPage"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.    xmlns:pivot="clr-namespace:System.Windows.Controls.Pivot;assembly=System.Windows.Controls.Pivot"
  7.    mc:Ignorable="d"
  8.    d:DesignHeight="300" d:DesignWidth="400"
  9.    DataContext="{Binding Main, Source={StaticResource Locator}}">
  10.  
  11.     <Grid x:Name="LayoutRoot" Background="White">
  12.         <pivot:PivotViewer ItemsSource="{Binding Titles}">
  13.          
  14.         </pivot:PivotViewer>
  15.     </Grid>
  16. </UserControl>

 

Conclusion

When we run our new application, we should get something like the following:

image

What we are seeing is 1000 NetFlix titles.  Pretty impressive huh?  What, you don’t believe me.  Well, join me on the next step to get some visuals.

Part 4: Tradecards

Tags: , , ,

Blog

PivotViewer V2 Series, Part 2: Creating a Project

by jasonrshaver 31. October 2011 16:35

Other articles in the PivotViewer V2 Series:

  • Part 1: Intro
  • Part 2: Creating a Project
  • Part 3: Receiving oData

Getting Started

The first step to using PivotViewer is creating new Silverlight 5 project.  Open Visual Studio and click File –> New –> Project.  Select the Silverlight Application project from under the Visual C# –> Silverlight node:

image

When the New Silverlight Application dialog appears, select the following options, as well as choosing a name that suits you.  I like to make the change highlighted:

image

Setting Up an MVVM Framework

While we can move forward without the overhead of an MVVM framework, part of what makes PivotViewer 2.0 so exciting is how easy it is to integrate with ViewModels.  Right click on the PivotViewerSeries.Silverlight project and select Add Library Package Reference.  Click Online on the left and type MVVM Light in the search box in the upper right corner.  Then click Install on the MvvmLight package:

image

At this point, some magic happens.  Once it is finished you can close the Add Library Package Reference window.  There are really only two bits of magic here that you need to know about. 

App.xaml Locator Resource

The first bit of magic is to know that the NuGet package changed your App.xaml to look something like this:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            x:Class="PivotViewerSeries.Silverlight.App"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:vm="clr-namespace:PivotViewerSeries.Silverlight.ViewModel"
            mc:Ignorable="d">
  <Application.Resources>
    <vm:ViewModelLocator x:Key="Locator"
                        d:IsDataSource="True" />
  </Application.Resources>
</Application>

ViewModelLocator.cs

The second bit of magic is that a ViewModelLocator.cs file gets created in the newly created ViewModel folder.  This Locator object gets created when the application loads up and will be used by our Silverlight controls to load our ViewModels.  For each ViewModel, we will be adding a property pointing to it. 

Connecting MainPage.xaml To MainViewModel.cs

If you open MainPage.xaml and add the bolded DataContext to the UserControl, you have finished setting up our MainPage to use MainViewModel.  Simple huh?

<UserControl x:Class="PivotViewerSeries.Silverlight.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d"
   d:DesignHeight="300" d:DesignWidth="400"
  DataContext="{Binding Main, Source={StaticResource Locator}}">

    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</UserControl>

Adding Our PivotViewer Control

The last step to creating a PivotViewer project is to add the pivot viewer.  First we need to add a reference to the Pivot assembly.  Right click on your PivotViewerSeries.Silverlight project and select Add Reference and select the System.Windows.Controls.Pivot assembly:

image

Add the following to the XAML namespaces in MainPage.xaml:

xmlns:pivot="clr-namespace:System.Windows.Controls.Pivot;assembly=System.Windows.Controls.Pivot"

And finally, inside the LayoutRoot Grid control, add the PivotViewer control:

<pivot:PivotViewer>
   
</pivot:PivotViewer>

Now, If you run your application, you should see an empty PivotViewer:

image

Conclusion

You should now have a Silverlight Project complete with MVVM framework and working PivotViewer.  Attached is a ZIP file containing a copy of my project up to this point.

Up next, Part 3: Receiving oData

Tags: , , , , ,

Blog

PivotViewer V2 Series, Part 1: Intro

by JasonRShaver 30. October 2011 15:51

As part of being a Program Manager at Microsoft, I am responsible for talk with and helping customers for a few technologies.  Of following set:

  • Silverlight Toolkit
  • Silverlight Data & Data-Binding
  • Silverlight Parser
  • Silverlight Line of Business
  • PivotViewer (v1 and v2)

I find that 90% of my customer emails want information/help/advice on PivotViewer.  While using the new version is really simple, I find there is still very little good documentation for how to use it and some of the best practices around it.  To correct that, I am kicking off a new series of blog posts all about using PivotViewer. 

Why am I doing this now?  Well I am headed back to Chicago to pack-up my house and move my ‘stuff’ to Redmond and while I am sitting there watching my life get put into boxes and loaded onto a truck, I figured this would be a good time to complete some work on this subject.  Also, I can then just point customers at work to my blog instead of having to type up the same thing 10 times a week =)

What You Will Need

Install all of the above (in the order shown is a good idea) and off we go:

PivotViewer V2 Series, Part 2: Creating a Project

Tags: , ,

Blog

About the author

I am a software developer working for Microsoft in Redmond, WA.  In addition, my wife and I own TTXOnline, what is likely the 3rd largest table tennis store in the US.

Month List

Page List