Pivotのヘッダーをカスタマイズする


## はじめに
UWPでタブコントロールのようなPivotが追加されましたが、ヘッダーのカスタマイズが単純にはできませんでした。

## 対象者
コントロールの見た目を変更して自己主張したい人。

## 編集履歴
* 2016/05/15
    + [ヘッダーの高さを42より小さくした場合の影響に関してを追記](#20160515追記)

## 見た目はどこで決まる？
UWPだけでなくXaml系言語では、見た目は基本的にStyleとTemplateで定義されています。
Styleを明示的に指定しない場合、標準で定義されているStyleが自動で適用されます。
明示的に指定すればそれが適用されるので、今回はPivotHeaderItemのStyleを作ります。

## 標準のStyleはどんなもの？
generic.xamlに定義されたStyleが適用されます。

* UWP(10240)
    + C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10240.0\Generic\generic.xaml
* UWP(10586)
    + C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic\generic.xaml

MSDNでも公開されているのでこちらも参照するといいでしょう。
https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299142.aspx

## Styleを変更する
今回はPivotのヘッダー領域が大きすぎるのが気になったので、小さくすることにします。
変更したいプロパティのみ再定義すれば構いません、再定義しないプロパティは標準の値が使いまわされます。

```xml:generic.xaml
<x:Double x:Key="PivotHeaderItemFontSize">24</x:Double>
<x:Int32 x:Key="PivotHeaderItemCharacterSpacing">-25</x:Int32>
<Thickness x:Key="PivotHeaderItemMargin">12,0,12,0</Thickness>
<FontWeight x:Key="PivotHeaderItemThemeFontWeight">SemiLight</FontWeight>

<Setter Property="FontSize" Value="{ThemeResource PivotHeaderItemFontSize}" />
<Setter Property="FontFamily" Value="{ThemeResource PivotHeaderItemFontFamily}" />
<Setter Property="FontWeight" Value="{ThemeResource PivotHeaderItemThemeFontWeight}" />
<Setter Property="CharacterSpacing" Value="{ThemeResource PivotHeaderItemCharacterSpacing}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
<Setter Property="Padding" Value="{ThemeResource PivotHeaderItemMargin}" />
<Setter Property="Height" Value="48" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="IsTabStop" Value="False" />
```

```xml:PivotHeaderItemStyles.xaml
<Style x:Key="PivotHeaderItemStyle" TargetType="PivotHeaderItem">
    <Setter Property="FontSize" Value="18" />
    <Setter Property="FontWeight" Value="Normal" />
    <Setter Property="CharacterSpacing" Value="0" />
    <Setter Property="Height" Value="42" />
</Style>
```

### 2016/05/15追記
Heightを42より小さくするとPivotHeaderItemとPivotItemの間に隙間ができてしまいます。
これはヘッダーの領域にPivotHeaderItemだけでなく、Pivotを切り替えるためのボタンが含まれており、それらのボタンの高さが42であるためです。
もし高さを42より小さくしたい場合は、PivotのControlTemplateの中にあるPreviousButtonとNextButtonを調整するといいでしょう。

## ControlTemplateを変更する
標準のTemplateだと、選択しているタブと選択していないタブがわかり辛いです。
選択中のタブにはアピールしてほしいので、選択中のタブの色を変更することにしましょう。

```xml:generic.xaml
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="PivotHeaderItem">
            <Grid
                x:Name="Grid"
                Background="{TemplateBinding Background}">
                <Grid.Resources>
                    <Style x:Key="BaseContentPresenterStyle" TargetType="ContentPresenter">
                        <Setter Property="FontFamily" Value="XamlAutoFontFamily"/>
                        <Setter Property="FontWeight" Value="SemiBold"/>
                        <Setter Property="FontSize" Value="15"/>
                        <Setter Property="TextWrapping" Value="Wrap"/>
                        <Setter Property="LineStackingStrategy" Value="MaxHeight"/>
                        <Setter Property="TextLineBounds" Value="Full"/>
                        <Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
                    </Style>
                    <Style x:Key="BodyContentPresenterStyle" TargetType="ContentPresenter" BasedOn="{StaticResource BaseContentPresenterStyle}">
                        <Setter Property="FontFamily" Value="{ThemeResource PivotHeaderItemFontFamily}" />
                        <Setter Property="FontWeight" Value="{ThemeResource PivotHeaderItemThemeFontWeight}"/>
                        <Setter Property="FontSize" Value="{ThemeResource PivotHeaderItemFontSize}"/>
                    </Style>
                </Grid.Resources>
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="SelectionStates">
                        <VisualStateGroup.Transitions>
                            <VisualTransition From="Unselected" To="UnselectedLocked" GeneratedDuration="0:0:0.33" />
                            <VisualTransition From="UnselectedLocked" To="Unselected" GeneratedDuration="0:0:0.33" />
                        </VisualStateGroup.Transitions>
                        <VisualState x:Name="Disabled">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                               Storyboard.TargetProperty="Foreground" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Unselected" />
                        <VisualState x:Name="UnselectedLocked">
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetName="ContentPresenterTranslateTransform"
                                                 Storyboard.TargetProperty="X"
                                                 Duration="0" To="{ThemeResource PivotHeaderItemLockedTranslation}" />
                                <DoubleAnimation Storyboard.TargetName="ContentPresenter"
                                                 Storyboard.TargetProperty="(UIElement.Opacity)"
                                                 Duration="0" To="0" />
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Selected">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                               Storyboard.TargetProperty="Foreground" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                                               Storyboard.TargetProperty="Background" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="UnselectedPointerOver">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                               Storyboard.TargetProperty="Foreground" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                                               Storyboard.TargetProperty="Background" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="SelectedPointerOver">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                Storyboard.TargetProperty="Foreground" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                                               Storyboard.TargetProperty="Background" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="UnselectedPressed">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                               Storyboard.TargetProperty="Foreground" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                                               Storyboard.TargetProperty="Background" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="SelectedPressed">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                               Storyboard.TargetProperty="Foreground" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                                               Storyboard.TargetProperty="Background" >
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <ContentPresenter
                    x:Name="ContentPresenter"
                    Content="{TemplateBinding Content}"
                    ContentTemplate="{TemplateBinding ContentTemplate}"
                    Margin="{TemplateBinding Padding}"
                    FontSize="{TemplateBinding FontSize}"
                    FontFamily="{TemplateBinding FontFamily}"
                    FontWeight="{TemplateBinding FontWeight}"
                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                    <ContentPresenter.RenderTransform>
                        <TranslateTransform x:Name="ContentPresenterTranslateTransform" />
                    </ContentPresenter.RenderTransform>
                </ContentPresenter>
            </Grid>
        </ControlTemplate>
    </Setter.Value>
</Setter>
```

WIndows8/8.1と比べるとUWPになってxamlの構造がシンプルになった気がします。

実質Gridの子要素としてContentPresenterがあるだけなので、このどちらかに背景色を設定すればいいのですが、そのまま設定するとどんなときでも背景色が同じになってしまいます。
特定の状態のときに○○にする、というものはVisualStateで行います。

PivotHeaderItemでは以下のVisualStateが定義されています。

* Disabled
* Unselected
* UnselectedLocked
* Selected
* UnselectedPointerOver
* SelectedPointerOver
* UnselectedPressed
* SelectedPressed

今回は選択中のときに背景色を変更したいので、Selected、SelectedPointerOver、SelectedPressedの3つが対象でしょうか。
背景色を変更するためのStoryboardは元々定義されているので、それらの設定値を変更します。

```xml:PivotHeaderItemStyles.xaml
<VisualState x:Name="Selected">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                        Storyboard.TargetProperty="Foreground" >
            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                        Storyboard.TargetProperty="Background" >
            <DiscreteObjectKeyFrame KeyTime="0" Value="Red" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>
<VisualState x:Name="SelectedPointerOver">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                        Storyboard.TargetProperty="Foreground" >
            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                        Storyboard.TargetProperty="Background" >
            <DiscreteObjectKeyFrame KeyTime="0" Value="Green" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>
<VisualState x:Name="SelectedPressed">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                        Storyboard.TargetProperty="Foreground" >
            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
                                        Storyboard.TargetProperty="Background" >
            <DiscreteObjectKeyFrame KeyTime="0" Value="Blue" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>
```

## Styleを適用する
Pivotも含め、ListViewやGridViewなどのItemsControlを継承したコントロールで子要素の見た目を変更したい場合、ItemContainerStyleに子要素のStyleを設定するのですが、Pivotの場合はPivotItemがこれに該当され、PivotHeaderItemは設定するためのプロパティがありません。

StyleはKeyを指定しない場合、すべての対象(TargetType)にStyleが適用されます。
App.xamlなどに設定してしまうとすべてのPivotにStyleが設定されてしまうため、細かな制御ができません。
Styleは定義された要素の子要素にしか適用されないため、PivotのResourcesに定義します。
こうすることで任意のPivotにのみStyleを適用することができます。

```xml:MainPage.xaml
<Pivot>
    <Pivot.Resources>
        <Style TargetType="PivotHeaderItem" BasedOn="{StaticResource CustomPivotHeaderItemStyle}">
        </Style>
    </Pivot.Resources>
    <PivotItem Header="１こめ" />
    <PivotItem Header="２こめ" />
    <PivotItem Header="３こめ" />
</Pivot>
```

## 完成
選択時は赤、選択中のマウスオーバーで緑、選択中のクリックで青になるStyleが適用できました。
上のPivotにのみStyleを適用し、下のPivotには適用していません。
![2016-05-15 (2).png](https://qiita-image-store.s3.amazonaws.com/0/125332/553eb9a0-01dd-c6fb-9159-7241a7701534.png)

今回のサンプルコードは[こちら](https://bitbucket.org/Shigezo32/uwpverification/src/4ad72c4184c7e5d72f910cde90f866eb4ebe67ed/PivotHeaderCustomize/?at=master)です。