Follow Me on Instagram Subscribe via RSS Feed

PivotViewer Basics : Basic Item Adorners

January 24, 2012

After a break to review some of my favorite Silverlight 5 features in the 12 Days of Silverlight series, it’s time to get back to some PivotViewer action.  This is the 4th post in the PivotViewer Basics series focused on some of the basics of the Silverlight 5 PivotViewer.  In this post we will look at item adorners, what they are and how to use them.

In the original PivotViewer, there was a concept of custom actions.  Defining custom actions allowed you to add a button on top of the selected trading card.  In fact, you could add several items to the card.  This gave you the ability to provide limited interactivity with the trading card to the user.  The new Silverlight does away with custom actions and replaces them with item adorners. (note: if you implemented custom actions in the previous version, this is another breaking code change)

Simply put, an item adorner is XAML that is placed over a trading card.  If you define an item adorner it is displayed over a trading card when either a mouse over event happens or the item is selected.  This opens a wide range of possibilities for your trading cards.  Now you can add additional information or interactive controls without the need to leave the PivotViewer.  PivotViewer comes with a default adorner, PivotViewerDefaultItemAdorner (yep, gotta love those short names).  This adorner mimics the functionality of the custom actions in the previous version.  In this post we will take a look at this new control.

Implementing the Default Item Adorner

In order to get us up and running quicker, we are going to start with the project created in the 1st post of the series, Client-Side Collections.  You can download the code here: PVB01_ClientSideCreation.zip

Adding the default item adorner is not the most trivial thing in the world, however it’s not overly complicated.  PivotViewer exposes  an ItemAdornerStyle property where you can set a style to define your item adorner.  Let’s look at what this style will look like using the PivotViewerDefaultItemAdorner.

<Style x:Key="basicAdorner" 
          TargetType="pivot:PivotViewerItemAdorner">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <pivot:PivotViewerDefaultItemAdorner
                    IsItemSelected="{Binding IsItemSelected, 
                RelativeSource={RelativeSource TemplatedParent}}"
              CommandsRequested="basicAdorner_CommandsRequested"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

This code creates a style of a PivotViewerItemAdorner.  In the template of that control, we are adding a PivotViewerDefaultItemAdorner.  The IsItemSelected property binds that property to the base PivotViewerItemAdorner.  This let’s us know when the item is simply a mouse-over event or the item has been selected.  The CommandsRequested property allows us to create custom commands and display/interact with them on the current trading card.

Here is an example of what the CommandsRequested event handler will look like.

private void basicAdorner_CommandsRequested(object sender, 
    PivotViewerCommandsRequestedEventArgs e)
{
     e.Commands.Add(new AddCommand());
}

The PivotViewerCommandRequestedEventArgs has 3 important properties.  The first, as shown above, is a collection of IPivotViewerUICommand objects that defines what commands (aka buttons) to add to the control.  Next is an Item property that gives you access to the data object being referred to.  Finally, an IsItemSelected property tells us if the object has been selected or not.  We will look at that more in a little while.

The IPivotViewerUICommand interface allows you to define what it being displayed to the user and what do to if the user selects it.  Here is what a base implementation of the interface will look like.

public class Class1 : IPivotViewerUICommand
{

    public string DisplayName
    {
        get { throw new NotImplementedException(); }
    }

    public Uri Icon
    {
        get { throw new NotImplementedException(); }
    }

    public object ToolTip
    {
        get { throw new NotImplementedException(); }
    }

    public bool CanExecute(object parameter)
    {
        throw new NotImplementedException();
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        throw new NotImplementedException();
    }
}

As you can see there isn’t a whole lot going on here.  The DisplayName, ToolTip, and Icon properties are used for the display.  If CanExecute returns true, then the Execute method is called when the user selects it.  If you go back to our event handler above, you will see that I added an AddCommand to the item.  The AddCommand class will display a MessageBox if the user selects the command.

public class AddCommand : IPivotViewerUICommand
{

    public string DisplayName
    {
        get { return "Add Item"; }
    }

    public Uri Icon
    {
        get { return null; }
    }

    public object ToolTip
    {
        get { return "Add Item to cart."; }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        var itm = parameter as DemoItem;
        MessageBox.Show( itm.ShortName + " was added");
    }
}

The only thing left to do is to let our PivotViewer know of the new ItemAdornerStyle that we wish to use.  You can set this in code of XAML.  Here we will simply set it in our XAML.

<pivot:PivotViewer x:Name="pViewer" 
    ItemAdornerStyle="{StaticResource basicAdorner}">
...
</pivot:PivotViewer>

If you run the project, you will now see a new command on each trading card when you select it.  Clicking the Add Item button will show a MessageBox letting you know that your item has been added to our mythical shopping cart, bug list, or what ever we decide to build.

image

You may also notice that the command will show up whether the item is selected or if you simply mouse over it.  This gets us back to the IsItemSelected property of the PivotViewerCommandRequestedEventArgs. We can take advantage of this property in our event handler and only add the AddCommand if the item is actually selected.

private void basicAdorner_CommandsRequested(object sender, 
    PivotViewerCommandsRequestedEventArgs e)
{
    if (e.IsItemSelected)
    {
        e.Commands.Add(new AddCommand());
    }
}

If you re-run your application, you will notice that we are no longer showing the command on the mouse over.

Creating an Interactive Adorner

The above example shows you how to use the PivotViewerDefaultItemAdorner to mimic the custom actions of the first release.  However, this isn’t the first release, so let’s advance things just a bit to get a better feel for how this can work.

For this example, we are going to create a new command.  This new ColorCommand will allow us to change the color of the current object.  Like the AddCommand, this command is rather straight forward.  However, in the Execute method, we will assign the item’s color to the CommandColor property defined in the command.  It wasn’t addressed in the example above, but the parameter argument that is passed into the Execute method is the data object that we are currently interacting with.  Here is what the new ColorCommand looks like.

public class ColorCommand : IPivotViewerUICommand
{

    public string CommandColor { get; set; }

    public string DisplayName
    {
        get { return CommandColor; }
    }

    public Uri Icon
    {
        get { return null; }
    }

    public object ToolTip
    {
        get { return "Change color to " + CommandColor; }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        var itm = parameter as DemoItem;

        itm.Color = CommandColor;
    }
}

Now we need to modify our event handler to add the new command.  We are going to add several command in this example.  The commands we add will be based on the current item’s color.  For example, if the current item is red, then we will add a blue, green, and purple command.  By adding logic into the command creation, we are able to provide a better experience to the user.  For instance, what good does a red button have on a red object?

private void basicAdorner_CommandsRequested(object sender, 
    PivotViewerCommandsRequestedEventArgs e)
{
            
    if (e.IsItemSelected)
    {
        var itm = e.Item as DemoItem;

        if(itm.Color != "Red")
            e.Commands.Add(new ColorCommand()
                               {
                                   CommandColor = "Red"
                               });
        if (itm.Color != "Blue")
            e.Commands.Add(new ColorCommand()
                               {
                                   CommandColor = "Blue"
                               });
        if (itm.Color != "Green")
            e.Commands.Add(new ColorCommand()
                               {
                                   CommandColor = "Green"
                               });
        if (itm.Color != "Purple")
            e.Commands.Add(new ColorCommand()
                               {
                                   CommandColor = "Purple"
                               });
    }
}

Running our application now, you will notice that any selected item will have 3 commands.  Selecting the command will update the object and dynamically change the color of the trading card.  Hopefully you can see how this could be very beneficial to you in more advanced situations.

image

In the next post in this series, we are going to look at creating our own item adorners.  I have received a lot of questions on this subject and thought we would explore it some.  You can download the source code for this project here: PVB04_BasicItemAdorners.zip

Until then… Happy Pivoting…

Series Navigation<< PivotViewer Basics : Dynamic CollectionsPivotViewer Basics: Custom Item Adorners >>

Comments (2)

Trackback URL | Comments RSS Feed

  1. Peter Voutov says:

    Hi Tony,

    I’ve really enjoyed your series on both SL and PivotViewer. I was hoping you could do a post on performance optimization tips with the SL5 version of the PivotViewer. Specifically, a few issues I’ve run into:

    – with a large collection and a trading card with multiple fields, there is a lag between loading ItemsSource with new items and rendering the trading cards. one solution is to use different template sizes (e.g. show only colored rectangles for the smallest size). another is to do gradual loading of data (although, I have yet to get this to work, because ObservableCollection is not thread safe).
    – in general, I find the visual vues the pivot viewer gives to the user when it’s busy, somewhat lacking.. I wish there was some way to display a progress bar when it’s redrawing itself, but I can’t found a good way to hook into it (LayoutUpdated?)

    Any other tips and tricks would be greatly appreciated.

    Peter

  2. Phil Wetzel says:

    Thank you Tony for the great blogs. I’m beginning to get my arms around this pivotviewer. I think it will eventually get to exactly what I want, but it’s not there yet. However, it is workable. I’m using it to display my pictures and haven’t worked out a good system for updating and managing. The biggest problem is the number of files the Excel tool creates in a collection. I have over 1000 pictures in about 12 collections. That’s about 50,000 files or more. I have DSL and it takes days to upload one collection. I’m hoping in the future there will be some way to pack the pics in something like a .zap file. I’m doing my best to keep up with all Pivotviewer progress. Thanks again.

Leave a Reply