In last episode, we constructed a minimal ControlTemplate for SinglePageViewer. Although it did not expose any UI, it was fully functional in terms of keyboard shortcuts, selection, and layout. Now we’ll create a ControlTemplate with real UI. Here’s the markup (it’s long, but I’ll break out the important parts):

<SinglePageViewer xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
                  xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
                  MinZoom="50" MaxZoom="200" FontFamily="Candara, Verdana">

  <SinglePageViewer.Template>
    <ControlTemplate TargetType="{x:Type SinglePageViewer}">
      <AdornerDecorator>
        <Grid>
          <RowDefinition Height="*" />
          <RowDefinition Height="Auto" />
          <ColumnDefinition Width="*" />
          <ColumnDefinition Width="*" />
          <ColumnDefinition Width="*" />

          <Border Grid.ColumnSpan="3" Margin="5,5,5,10" Padding="5"
                  BorderBrush="#aaa" BorderThickness="1,1,1,2"
                  Background="#fff">

            <Border.BitmapEffect>
              <DropShadowBitmapEffect />
            </Border.BitmapEffect>

            <DocumentPageView DocumentViewerBase.IsMasterPage="True"
                              PageNumber="0" />
          </Border>

          <TextBlock Grid.ColumnSpan="3" Margin="0,0,0,20"
                     VerticalAlignment="Bottom" HorizontalAlignment="Center"
                     Text="{Binding Path=MasterPageNumber, RelativeSource=/TemplatedParent}" />

          <Slider Grid.Column="1" Grid.Row="1" Margin="5,5,10,5"
                  Value="{Binding Path=Zoom, RelativeSource=/TemplatedParent}"
                  Minimum="{Binding Path=MinZoom, RelativeSource=/TemplatedParent}"
                  Maximum="{Binding Path=MaxZoom, RelativeSource=/TemplatedParent}"
                  TickFrequency="{Binding Path=ZoomIncrement, RelativeSource=/TemplatedParent}" />

          <Button Grid.Column="0" Grid.Row="1"
                  Command="PreviousPage"
                  IsEnabled="{Binding Path=CanGoToPreviousPage, RelativeSource=/TemplatedParent}">
              Previous Page
          </Button>

          <Button Grid.Row="1" Grid.Column="2"
                  Command="NextPage"
                  IsEnabled="{Binding Path=CanGoToNextPage, RelativeSource=/TemplatedParent}">
              Next Page
          </Button>
        </Grid>
      </AdornerDecorator>
    </ControlTemplate>
  </SinglePageViewer.Template>

  <FlowDocument>
    <Paragraph FontSize="16">
      Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
      Curabitur sodales, sem nec scelerisque aliquam, ipsum nibh
      tristique nisl, eu dictum ipsum neque nec dolor. Nullam
      magna lacus, mollis faucibus, auctor eget, placerat sit
      amet, odio. Morbi feugiat, ligula tristique condimentum
      luctus, eros nisi vestibulum ante, a porta dolor nibh sed
      nisl. Cras a libero vitae leo eleifend tristique. Fusce
      consequat, urna in consectetuer aliquet, felis purus luctus
      nisl, at sollicitudin sem augue quis ante. Fusce convallis
      nibh quis nisl. Fusce dictum faucibus velit. Proin tempus
      orci a leo. Integer suscipit. Vestibulum nisl libero, congue
      at, mollis sed, fringilla nec, lorem. Vestibulum ante ipsum
      primis in faucibus orci luctus et ultrices posuere cubilia
      Curae; Donec ac leo ut lectus tempus vestibulum. Proin vel
      enim eget nisi rhoncus mollis. Duis dolor mauris, dictum
      adipiscing, posuere nec, blandit et, orci.
    </Paragraph>
  </FlowDocument>
</SinglePageViewer>

Here are a couple of screenshots of the markup, the first is zoomed out to a small font size, the second has a large font size:

A screenshot of the SinglePageViewer control with a custom ControlTemplate

A screenshot of the SinglePageViewer control with a custom ControlTemplate

We’ve changed the structure of our ControlTemplate a bit, and added some new elements. Here’s a summary:

  • Border: In order ot make the content look like it’s on a physical page, we’ve wrapped the DocumentPageView with a Border element. Border is used to create a thin gray border and drop shadow (using DropShadowBitmapEffect) around the content.
  • TextBlock: This element displays the current page number. This is achieved by binding the Text property SinglePageViewer’s MasterPageNumber property.
  • Slider: The horizontal slider is bound to the Zoom property on SinglePageViewer.
  • Button: We’ve created two buttons, for “Previous Page” and “Next Page.” The Command property is set on the button, which hooks up the Button to fire the appropriate command when clicked. We’ve also bound the IsEnabled property to disable the buttons when appropriate (e.g. the “Previous Page” button is disabled if we’re displaying the first page).

Now we have a functional interface, but it’s not particularly pleasing to the eye. In part 3, we’ll create a more attractive look and feel.