Visually Located

XAML and GIS

Basic styling of the Silverlight 5 PivotViewer

Normally when people ask me how to create a custom style for a control in Silverlight or WPF I quickly reply

Use Expression Blend!

If they do not have Expression Blend, then the next answer is

Get the default style from MSDN and change as needed.

So, when someone asked how to go about styling the new PivotViewer control, my immediate response was going to be “Use Expression Blend”. But, we’re in a odd place right now. There is now RTM of Express Blend. There is only the Expression Blend Preview for Silverlight 5. As this is not a RTM release, and PivotViewer is new, I thought I would test it out first. So, open a Silverlight 5 project in Blend Preview and add the PivotViewer Control. Right click on the PivotViewer –> Edit Template –> Edit a Copy…

image

To my surprise, the following xaml was generated

<UserControl.Resources>
    <ControlTemplate x:Key="PivotViewerControlTemplate1" TargetType="sdk:PivotViewer"/>
</UserControl.Resources>

WHAT?!?!? An empty template! I must have done that wrong. I must have done PivotViewer –> Edit Template –> Create empty… Yup, that must be it! Try again, and again an empty template. Blend, you have failed me! How could you? I used to think that Blend was the all powerful Oz when you need to style a control. But it failed!

OK, I’ll just go to step two. Microsoft is always great when it comes to documentation. Go to MSDN, and… No templates?!?! Ok, Silverlight 5 had the longest Beta program! Was it a year? I don’t recall when the first beta came out, maybe around MIX? Yes I think that’s it. Granted, the PivotViewer control was not released with the first Beta, but it was with the second. So this is one time when the team dropped the ball when it comes to the docs.

So, nothing from the all powerful Blend, nothing from MSDN, now what? Like any .Net developer I keep a copy of Reflector close by. Open up the PivotViewer assembly and check out the resources. Sure enough, there it is! If you’ve played with PivotViewer before, you know that this control is not just a simple control. This thing is a beast! It is an entire application disguised as a control. Because this thing is so huge, the styles used are contained in a 6000+ line xaml file! Maybe this is why it is not on MSDN, but the basic style of the actual PivotViewer control is only 170 lines of xaml. That’s nothing! Here is the default style for PivotViewer

<!-- Default style for the PivotViewer -->
<Style TargetType="sdk:PivotViewer">
    <Style.Setters>
        <Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" />
        <Setter Property="Background" Value="{StaticResource BackgroundBrush}" />
        <Setter Property="BorderBrush" Value="{StaticResource BorderBrush}" />
        <Setter Property="AccentColor" Value="{StaticResource AccentColor}" />
        <Setter Property="SecondaryBackground" Value="{StaticResource SecondaryBackgroundBrush}" />
        <Setter Property="ControlBackground" Value="{StaticResource ControlBackgroundBrush}" />
        <Setter Property="SecondaryForeground" Value="{StaticResource SecondaryForegroundBrush}" />
        <Setter Property="PrimaryItemValueBackgroundColor" Value="{StaticResource PrimaryItemValueBackgroundColor}" />
        <Setter Property="SecondaryItemValueBackgroundColor" Value="{StaticResource SecondaryItemValueBackgroundColor}" />
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="sdk:PivotViewer">
                    <Grid x:Name="PART_Container" Background="{TemplateBinding Background}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="30"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
 
                        <!-- PivotViewer border -->
                        <Rectangle Grid.RowSpan="2" Stroke="{TemplateBinding BorderBrush}"/>
 
                        <!-- ControlBar -->
                        <Grid Grid.Row="0" x:Name="PART_ControlBar">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
 
                            <!-- ControlBar background -->
                            <Rectangle Grid.ColumnSpan="5" Fill="{TemplateBinding Background}" Stroke="{StaticResource BorderBrush}"/>
 
                            <!-- Container for AppliedFiltersView -->
                            <ItemsControl x:Name="PART_AppliedFiltersBreadcrumb" 
                                  Grid.Column="0" Margin="5,0,5,0"
                                  Style="{StaticResource AppliedFiltersBreadcrumbStyle}">
                                <ToolTipService.ToolTip>
                                    <ToolTip x:Name="PART_AppliedFiltersToolTip" ContentTemplate="{StaticResource AppliedFiltersToolTipTemplate}" />
                                </ToolTipService.ToolTip>
                            </ItemsControl>
 
                            <!-- Container for SortPropertySelector -->
                            <ComboBox x:Name="PART_SortPropertySelector" Style="{StaticResource SortCategoryDropdownStyle}"
                                      AutomationProperties.Name="SortPropertySelector"
                                      Grid.Column="1" VerticalAlignment="Center" 
                                      Height="22"
                                      Margin="0,0,5,0" />
 
                            <!--Container for ViewSelectionView -->
                            <ListBox x:Name="PART_ViewSelector" Style="{StaticResource ViewSelectorStyle}"
                                     Grid.Column="2" VerticalAlignment="Center" 
                                     Width="60" Height="22"
                                     Margin="0,0,5,0" />
                            
                            <!-- Container for ZoomSliderView -->
                            <Grid x:Name="PART_ZoomSliderView" 
                                  Grid.Column="3" VerticalAlignment="Center" Margin="0,0,5,0"
                                  Width="146" Height="22">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="18"/>
                                    <ColumnDefinition />
                                    <ColumnDefinition Width="18"/>
                                </Grid.ColumnDefinitions>
 
                                <Rectangle Stroke="{TemplateBinding BorderBrush}" Margin="0" Grid.ColumnSpan="3" Fill="{TemplateBinding Background}"/>
                                <Button x:Name="PART_ZoomOutButton" Grid.Column="0"
                                        Style="{StaticResource Style_Button_noBorder}">
                                    <Path x:Name="path" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" Width="8" Height="2.5" Data="M16,6 L16,10 0,10 0,6 z" Fill="{Binding (con:FrameworkElementExtensions.Brushes)[ControlForeground], RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" Margin="0" />
                                </Button>
                                <con:ClickDragSlider x:Name="PART_ZoomSlider"
                                                     Width="100"
                                                     SmallChange="0"
                                                     Grid.Column="1" 
                                                     Style="{StaticResource Style_ZoomSlider}" 
                                                     BorderBrush="{Binding ControlBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                                <Button x:Name="PART_ZoomInButton" Grid.Column="2"
                                        Style="{StaticResource Style_Button_noBorder}">
                                    <Path Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" Width="8" Height="8" Data="M6,0 L10,0 10,6 16,6 16,10 10,10 10,16 6,16 6,10 0,10 0,6 6,6 z" Fill="{Binding (con:FrameworkElementExtensions.Brushes)[ControlForeground], RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" Margin="0" />
                                </Button>
                            </Grid>
                        </Grid>
 
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemAdornerStyle">
            <Setter.Value>
                <Style TargetType="sdk:PivotViewerItemAdorner">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="sdk:PivotViewerItemAdorner">
                                <Grid Background="Transparent">
                                    <VisualStateManager.VisualStateGroups>
                                        <VisualStateGroup x:Name="CommonStates">
                                            <VisualState x:Name="Normal"/>
                                            <VisualState x:Name="MouseOver">
                                                <Storyboard>
                                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ButtonBorder">
                                                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                                    </DoubleAnimationUsingKeyFrames>
                                                </Storyboard>
                                            </VisualState>
                                            <VisualState x:Name="Disabled"/>
                                        </VisualStateGroup>
                                        <VisualStateGroup x:Name="ItemStates">
                                            <VisualState x:Name="Default"/>
                                            <VisualState x:Name="IsSelected">
                                                <Storyboard>
                                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="FocusedItemBorder">
                                                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                                    </DoubleAnimationUsingKeyFrames>
                                                </Storyboard>
                                            </VisualState>
                                        </VisualStateGroup>
                                    </VisualStateManager.VisualStateGroups>
 
                                    <Border x:Name="ButtonBorder"
                                            Margin="-1"
                                            BorderThickness="4"
                                            BorderBrush="{Binding (con:FrameworkElementExtensions.Brushes)[ControlHoverBackground], RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}"
                                            Opacity="1"/>
                                    <Border x:Name="FocusedItemBorder"
                                            Margin="-1"
                                            BorderThickness="4"
                                            BorderBrush="{Binding (con:FrameworkElementExtensions.Brushes)[FocusBorderBrush], RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}"
                                            Opacity="1"/>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="DetailPaneStyle">
            <Setter.Value>
                <Style TargetType="sdk:PivotViewerDetailPane">
                    <Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="Background" Value="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="AccentColor" Value="{Binding AccentColor, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="Background" Value="{Binding SecondaryBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="ControlBackground" Value="{Binding ControlBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="SecondaryForeground" Value="{Binding SecondaryForeground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="PrimaryItemValueBackgroundColor" Value="{Binding PrimaryItemValueBackgroundColor, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="SecondaryItemValueBackgroundColor" Value="{Binding SecondaryItemValueBackgroundColor, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="FilterPaneStyle">
            <Setter.Value>
                <Style TargetType="sdk:PivotViewerFilterPane">
                    <Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="Background" Value="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="AccentColor" Value="{Binding AccentColor, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="SecondaryBackground" Value="{Binding SecondaryBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="ControlBackground" Value="{Binding ControlBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                    <Setter Property="ClearButtonBackground" Value="{StaticResource ClearButtonCircleBackground}" />
                    <Setter Property="SecondaryForeground" Value="{Binding SecondaryForeground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=sdk:PivotViewer}}" />
                </Style>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

As you can see, the default style is nothing much. It’s mainly just the title bar. The DetailPaneStyle helps apply some color based style like Background, Foreground, etc. to the panel that appears when you drill into a trading card. This panel shows up on the right. The FilterPaneStyle does the same but for the panel on the left that shows the different properties that you can on. Being able to set the color is nice, but come on I want to do more. Surely I can use the ability to set this styles in Expression Blend with the Edit Additional Styles feature. Oh, that’s disabled with the right click menu, but it’s not disabled in the Object menu! Oh, that doesn’t work at all. Crap! Blend you suck! I want a divorce!

Luckily the styles for these two controls are also in that 6000+ line file. So, if you do want to style the left pane (also called the Facets) or the right details pane, you have everything needed.

If you are looking to get down and dirty with the Facets, you’ll want to pay attention to the Accordion styles. The PivotViewer uses a CustomAccordion control, and it uses a lot of styles that are supplied in the file. You’ll want to alter the AccordionItemStyle, FacetCategoryHeaderTemplate, and the FacotCategoryTemplate styles. 

As it’s currently impossible to find these styles, I’ve supplied them here. This file comes complete with all of the comments from the team that developed it! WooHoo! I also went ahead and fixed some of the styling. For some reason this team REALLY loved ancestor binding and used it everywhere. They even used it where TemplateBinding should be used.