Dieser Artikel wurde maschinell übersetzt.

UI-Grenzen

Seitenübergänge in Windows Phone 7

Charles Petzold

Das Codebeispiel herunterladen

Charles PetzoldRegel der drei gibt es in mehreren unterschiedlichen Disziplinen. In der Komödie, z. B. die Regel drei Diktat, die ein Witz zu beginnen, durch die Gewinnung des Publikums Interesse, dann heben Sie Erwartung und schließlich werden Sie etwas weniger lustig als erwartet.

In der Wirtschaft bezieht sich auf die Existenz von drei Hauptkonkurrenten auf einem bestimmten Markt die Regel der drei. In der Programmierung, es ist eher wie eine drei-Strike-Regel: Wenn Sie ein Codeabschnitt ähnliche drei Mal angezeigt wird, sollte es in einer gemeinsamen Schleife oder Methode umgestaltet werden. (Ich hörte ursprünglich diese Regel als "drei oder mehr? Verwendung eines für. " Oder vielleicht habe ich bis, dass Jingle mich.)

Dies bringt uns zum Thema Text-Layout. Manchmal müssen Programmierer ein Dokument anzeigen, die auf einem Bildschirm passen zu lang ist. Vielleicht beinhaltet der unangenehmsten Ansatz, den Schriftgrad verkleinern, bis das Dokument passt. Eine traditionellere Lösung ist eine Bildlaufleiste. Aber in den letzten Jahren – und vor allem auf kleinere Geräte wie Tabletten und Telefone – hat es ein Trend zu paginieren des Dokuments und lassen den Benutzer durch die Seiten leicht zu schlagen, als ob ein richtiger gedruckten Buch oder einer Zeitschrift lesen.

Anzeigen eines paginierten Dokuments gehört auch eine Regel von drei: für die meisten Fluid Seitenübergänge muss die Benutzeroberfläche unterstützen drei unterschiedliche Seiten – die aktuelle Seite, die nächste Seite und der vorherigen Seite. Wie die Seiten weiterleiten, wird die aktuelle Seite der vorherigen Seite, und die nächste Seite wird die aktuelle Seite. Rückwärts gehen, wird die aktuelle Seite der nächsten Seite, und die vorherige Seite wird die aktuelle Seite. In diesem Artikel werde ich beschreiben, wie diese Technik in einer Weise, die flexibel genug für drei verschiedene Seitenübergänge implementiert – eine Seite-Folie, ein Flip-3D-wie und eine 2D Seitenumblättern.

Zurück zu Gutenberg

In der letzten Ausgabe dieser Rubrik (msdn.microsoft.com/magazine/hh205757), ich zeigte einige ziemlich einfache Paginierung-Logik in einem Windows-Telefon-7-Programm mit dem Namen EmmaReader, so genannt, weil es Sie Jane Austens Roman "Emma" (1815) lesen kann. Das Programm verwendet eine nur-Text-Datei, die von der berühmten Project Gutenberg-Website heruntergeladen (bei gutenberg.org), die stellt mehr als 30.000-Bücher zur Verfügung.

Paginierung ist ein nicht-trivialen Prozess, der eine spürbare Zeitspanne erfordern kann. Aus diesem Grund paginiert EmmaReader nur bei Bedarf Fortschreiten des Benutzers durch das Buch. Nachdem Sie eine Seite erstellen, speichert das Programm Informationen, der angibt, wo in dem Buch dieser Seite beginnt und endet. Diese Informationen können es dann um rückwärts durch das Buch Benutzerseite zu lassen.

Für den Code in diesem Artikel ich wählte den Roman von George Eliot, "Middlemarch" (1874), und herunterladbare Windows Telefon 7 Projekt MiddlemarchReader genannt. Dieses Programm stellt einen weiteren Schritt näher an eine verallgemeinerte e-Book-Reader für Windows-Telefon-7 basiert auf Project Gutenberg-nur-Text-Dateien. Sie werden nicht in der Lage, diese e-Book-Reader für aktuelle Bestseller verwenden, aber es wird sicherlich lassen Sie die Klassiker der englischen Literatur zu erkunden.

Project Gutenberg-nur-Text-Dateien teilen jeden Absatz in mehreren aufeinander folgenden 72-Komikfiguren. Absätze werden durch eine Leerzeile getrennt. Dies bedeutet, dass jedes Programm, das in das Format wünscht eine Project Gutenberg-Datei zur Paginierung und Präsentation diese separaten Zeilen in einzeiligen Absätzen zu verketten muss. In MiddlemarchReader erfolgt dies in der GenerateParagraphs-Methode der PageProvider-Klasse. Diese Methode wird jedes Mal, wenn das Buch geladen wird aufgerufen, und die Absätze in eine einfache generische List-Objekt vom Typ String speichert.

Dieser Absatz-Verkettung-Logik zerfällt in zwei Fällen: Wenn ein ganzen Absatz eingerückt ist, das Programm wird nicht verketten diese Richtung, so dass jede Zeile einzeln verpackt ist. Das entgegengesetzte Problem tritt auf, wenn ein Buch Poesie anstatt Prosa enthält: die Anwendung fälschlicherweise verkettet diese Zeilen. Diese Probleme sind nicht zu vermeiden, ohne Prüfung der Semantik der tatsächliche Text, der ohne menschliches Zutun ziemlich schwierig ist.

Ein neues Kapitel

Die meisten Bücher sind auch in Kapitel aufgeteilt, und so dass ein Benutzer zu einem bestimmten Kapitel springen ist ein wichtiges Merkmal des ein e-Book-Reader. In EmmaReader ich ignoriert Kapiteln, aber enthält die PageProvider-Klasse der MiddlemarchReader eine GenerateChapters-Methode, die bestimmt, wo jedes Kapitel beginnt. Eine Project Gutenberg-Datei begrenzt im allgemeinen Kapitel mit drei oder vier Leerzeilen, je nach dem Buch. Für "Middlemarch," stellen Sie drei Leerzeilen eine Linie angibt, den Kapiteltitel. Die GenerateChapters-Methode verwendet diese Funktion um zu bestimmen, den bestimmten Absatz beginnt jedes Kapitel.

Ein Buch in Kapitel unterteilen hilft, das Problem der Paginierung in ein e-Book-Reader zu lindern. Genommen Sie an, ein Leser ist am Ende eines Buches und beschließt, die Schriftgröße ändern. (Weder EmmaReader noch MiddlemarchReader haben diese Funktion, aber ich spreche theoretisch). Ohne Kapitel Divisionen die gesamte buchen bis zu, dass Punkt Formatierungstrennzeichen werden muss. Aber wenn das Buch eingeteilt ist, muss nur das aktuelle Kapitel Formatierungstrennzeichen werden. Zurück in die Tage des mechanischen Typs half Aufteilung eines Buches in Kapitel der Schriftsetzer in genau der gleichen Weise, als Linien hatte eingefügt oder gelöscht werden.

Das MiddlemarchReader-Programm speichert Informationen über das Buch in isoliertem Speicher. Die Hauptklasse für diese Informationen beibehalten wird AppSettings aufgerufen. AppSettings verweist ein BookInfo-Objekt, das eine Auflistung von ChapterInfo-Objekten, jeweils eines für jedes Kapitel enthält. Diese ChapterInfo-Objekte enthalten den Titel des Kapitels und eine Auflistung von PageInfo-Objekten für dieses Kapitel. Der PageInfo-Objekte geben an, wo jeder Seite beginnt basierend auf einer ParagraphIndex, die Liste der Zeichenfolgen für jeden Absatz verwiesen wird und eine CharacterIndex in die Zeichenfolge indiziert. Das erste PageInfo-Objekt für ein Kapitel ist in der GenerateChapters-Methode erstellt, die ich zuvor beschrieben. Nachfolgende PageInfo-Objekte erstellt und als das Kapitel schrittweise paginiert ist, während der Benutzer dieses Kapitel liest. Auch in BookInfo gespeichert ist der Benutzer aktuelle Seite, als ein Kapitelindex und ein Seitenindex innerhalb dieses Kapitels identifiziert.

"Middlemarch" hat 86 Kapitel, doch die Logik in der GenerateChapters-Methode im PageProvider findet 110 Kapitel, einschließlich Titel für die Aufteilung der "Middlemarch" in acht "Bücher" und Material an den Anfang und das Ende der Datei. Formatiert für das Telefon, ist der Roman etwa 1.800 Seiten oder etwa 20 Seiten pro Kapitel.

Abbildung 1 zeigt das Inhalte-Bedienfeld in MainPage.xaml. Es gibt zwei Steuerelemente, die das einzellige Grid belegen, aber nur einer von ihnen ist zu jeder Zeit sichtbar. Beide Steuerelemente enthalten Bindungen an ein Objekt des Typs MiddlemarchPageProvider als Ressource definiert. Das BookViewer-Steuerelement werde ich kurz erläutern. ListBox zeigt eine bildlauffähige Liste der Kapitel, und Sie sie mit einer Schaltfläche auf das Programm ApplicationBar aufrufen. Abbildung 2 zeigt die Liste der Kapitel in MiddlemarchReader, in der Mitte gescrollt.

Abbildung 1 das Inhalts-Bedienfeld in MainPage.xaml

<phone:PhoneApplicationPage.Resources>
  <local:MiddlemarchPageProvider x:Key="pageProvider" />
</phone:PhoneApplicationPage.Resources>
...
<Grid x:Name="ContentPanel" Grid.Row="1">
  <local:BookViewer x:Name="bookViewer"
                    PageProvider="{StaticResource pageProvider}"
                    PageChanged="OnBookViewerPageChanged">
    <local:BookViewer.PageTransition>
      <local:SlideTransition />
    </local:BookViewer.PageTransition>
  </local:BookViewer>

  <ListBox Name="chaptersListBox"
           Visibility="Collapsed"
           Background="{StaticResource PhoneBackgroundBrush}"
           ItemsSource="{Binding Source={StaticResource pageProvider}, 
                                 Path=Book.Chapters}"
           FontSize="{StaticResource PhoneFontSizeLarge}"
           SelectionChanged="OnChaptersListBoxSelectionChanged">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <Grid Margin="0 2">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
          </Grid.ColumnDefinitions>
                            
          <TextBlock Grid.Column="0"
                     Text="&#x2022;"
                     Margin="0 0 3 0" />
                        
          <TextBlock Grid.Column="1" 
                     Text="{Binding Title}"
                     TextWrapping="Wrap" />
        </Grid>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Grid>

Ich würde nicht diese Überschriften und Titel mit Großbuchstaben anzeigen gewählt haben, aber das ist, wie sie in der ursprünglichen Datei und das Programm Blind zieht erscheinen sie aus basiert ausschließlich auf diesen Strecken mindestens drei Leerzeilen vorangestellt wird.

The Scrollable List of Chapters

Abbildung 2 bildlauffähige Liste der Kapitel

Das Problem der Rücken-Seite

Wie EmmaReader paginiert MiddlemarchReader on-Demand, wie der Benutzer das Buch liest und die Seiteninformationen werden gespeichert, so dass der Benutzer rückwärts Seite kann. Kein Problem gibt. MiddlemarchReader kann auch der Benutzer zu einem bestimmten Kapitel springen. Kein Problem, entweder, weil bereits das Programm weiß, wo jedes Kapitel beginnt.

Aber genommen Sie an, der Benutzer springt an den Anfang eines Kapitels und dann entscheidet, rückwärts bis zur letzten Seite der vorherigen Kapitel Seite. Wenn im vorherige Kapitel noch nicht paginiert, muss dieses ganze Kapitel paginiert werden, dieser letzte Seite zeigen. Der Absatz möglicherweise kurz, oder könnte es ziemlich lang, je nach das Buch und das Kapitel. Das ist ein Problem.

Obwohl MiddlemarchReader bei Bedarf paginiert wird, geht es eigentlich ein wenig darüber hinaus. Wenn der Benutzer eine Seite, die nächste Seite und der vorherigen Seite liest sind bereit für paging vorwärts oder zurück. Erhalten diese zusätzlichen Seiten ist normalerweise kein Problem. Aber was soll das Programm tun, wenn der Benutzer auf den Anfang eines Kapitels springt? Sollte das Programm die letzte Seite des vorherigen Kapitel für die Möglichkeit der paging-rückwärts bereit sein zu erhalten? Das ist Verschwendung: In den meisten Fällen, der Benutzer wird nur Seite nach vorne, so warum stören?

Natürlich wird es ein wenig kompliziert. Ich habe bereits erwähnt, dass die PageProvider-Klasse verantwortlich ist für die Analyse der Project Gutenberg-Originaldatei und teilen es in den Absätzen und Kapiteln, aber es auch verantwortlich für die Paginierung ist. Der MiddlemarchPageProvider-Klasse, auf die in Abbildung 1 ist winzig. Es wird von PageProvider abgeleitet und einfach die "Middlemarch"-Datei zugreift, so dass sie von PageProvider verarbeitet werden kann.

Ich wollte das BookViewer-Steuerelement als wenig Kenntnisse über die Interna der PageProvider wie möglich haben. Als daher die PageProvider-Eigenschaft des BookViewer vom Typ IPageProvider (eine von PageProvider implementierte Schnittstelle), ist im angezeigt Abbildung 3.

Abbildung 3 Die IPageProvider-Schnittstelle

public interface IPageProvider
{
  void SetPageSize(Size size);

  void SetFont(FontFamily fontFamily, double FontSize);

  FrameworkElement GetPage(int chapterIndex, int pageIndex);

  FrameworkElement QueryLastPage(int chapterIndex, out int pageIndex);

  FrameworkElement GetLastPage(int chapterIndex, out int pageIndex);
}

Hier ist, wie es funktioniert: The BookViewer Objekt informiert PageProvider die Größe jeder Seite und die Schriftart und den Schriftgrad für die TextBlock-Elemente erstellen, die zu verwendende umfassen die Seite. Die meiste Zeit, erhält BookViewer Seiten durch Aufrufen von GetPage. Wenn GetPage null zurückgegeben wird, den Kapitelindex oder Seitenindex liegt außerhalb des Bereichs, und diese Seite ist nicht vorhanden. Dies deutet BookViewer, dass es hinter dem Ende eines Kapitels gegangen ist und muss, um das nächste Kapitel vorzudringen. Abbildung 4 MiddlemarchReader am Anfang eines Kapitels zeigt. (Jedes Kapitel in "Middlemarch" beginnt mit einer Inschrift, einige von ihnen geschrieben von George Eliot sich.)

The BookViewer Control Displaying a Page

Abbildung 4 Das BookViewer-Steuerelement eine Seite anzeigen

Wenn BookViewer an den Anfang eines Kapitels navigiert, definiert IPageProvider zwei Methoden zum Abrufen von die letzte Seite des vorherigen Kapitel. QueryLastPage ist die schnelle Methode. Wenn im vorherige Kapitel hat bereits paginiert, und die letzte Seite verfügbar ist, dann die Methode gibt die Seite zurück und legt den Seitenindex. Wenn die Seite nicht verfügbar ist, gibt QueryLastPage null zurück. Dies ist die Methode, die BookViewer die vorherige Seite zu erhalten, wenn der Benutzer an den Anfang eines Kapitels wechselt aufruft.

Wenn QueryLastPage Null und der Benutzer dann Seiten zurück auf die fehlenden Seite zurückgibt, hat BookViewer keine Berufungsmöglichkeiten aber zu nennen GetLastPage. Falls erforderlich, wird GetLastPage ein ganzes Kapitel nur um die letzte Seite erhalten paginieren.

Warum nicht Paginieren des vorhergehenden Abschnitts in einem zweiten Thread der Ausführung, nachdem der Benutzer an den Anfang eines Kapitels wechselt? Vielleicht wird der zweite Thread mit der Zeit abgeschlossen, die der Benutzer entscheidet, Seite zurück. Obwohl das sicherlich eine plausible Lösung ist, müssten die Paginierung-Logik, ein wenig umstrukturiert werden, weil es StackPanel- und TextBlock-Objekte erstellen ist und diese nicht von den primären Thread verwendet werden.

Die BookViewer-Übergänge

Wie ich, im allgemeinen erwähnt habe, ist BookViewer drei Seiten auf einmal jonglieren. Das Steuerelement von UserControl abgeleitet ist und Abbildung 5 zeigt die XAML-Datei. Die drei Border-Elemente mit Namen von pageHost0, pageHost1 und pageHost2 dienen als Eltern der Seiten aus PageProvider gewonnen. (Diese Seiten sind eigentlich StackPanel-Elementen mit einem TextBlock für jeden Absatz auf der Seite.) Die anderen drei Border-Elemente mit Namen beginnen mit PageContainer bieten einen weißen Hintergrund und ein wenig Spielraum von weiß um die Grenzen der Seite. Diese sind auch die Elemente für Seitenübergänge manipuliert. Ein GestureListener (im Silverlight für Windows Phone Toolkit, zum Herunterladen von CodePlex bei bit.ly/cB8hxu) bietet eine Touch-Eingabe.

Abbildung 5 XAML-Datei für BookViewer

<UserControl x:Class="MiddlemarchReader.BookViewer"
             xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=
               Microsoft.Phone.Controls.Toolkit">

  <UserControl.Resources>
    <Style x:Key="pageContainerStyle" TargetType="Border">
      <Setter Property="BorderBrush" Value="Black" />
      <Setter Property="BorderThickness" Value="1" />
      <Setter Property="Background" Value="White" />
    </Style>

    <Style x:Key="pageHostStyle" TargetType="Border">
      <Setter Property="Margin" Value="12, 6" />
      <Setter Property="CacheMode" Value="BitmapCache" />
    </Style>
  </UserControl.Resources>

  <Grid x:Name="LayoutRoot">
    <toolkit:GestureService.GestureListener>
      <toolkit:GestureListener GestureBegin="OnGestureListenerGestureBegin"
                               GestureCompleted=
                                 "OnGestureListenerGestureCompleted"
                               Tap="OnGestureListenerTap"
                               Flick="OnGestureListenerFlick" 
                               DragStarted="OnGestureListenerDragStarted"
                               DragDelta="OnGestureListenerDragDelta"
                               DragCompleted="OnGestureListenerDragCompleted" />
    </toolkit:GestureService.GestureListener>

    <Border Name="pageContainer0" Style="{StaticResource pageContainerStyle}">
      <Border Name="pageHost0" Style="{StaticResource pageHostStyle}" />
    </Border>

    <Border Name="pageContainer1" Style="{StaticResource pageContainerStyle}">
      <Border Name="pageHost1" Style="{StaticResource pageHostStyle}" />
    </Border>

    <Border Name="pageContainer2" Style="{StaticResource pageContainerStyle}">
      <Border Name="pageHost2" Style="{StaticResource pageHostStyle}" />
    </Border>
  </Grid>
</UserControl>

Wenn BookViewer erstellt wird, die drei PageHost-Objekte in ein Array namens PageHosts gespeichert. Eine Feld mit dem Namen PageHostBaseIndex ist auf 0 festgelegt. Wie der Benutzer Seiten über ein Buch, PageHostBaseIndex 1, dann 2 geht, dann auf 0 zurück. Als die Seiten rückwärts PageHostBaseIndex geht von 0 zu 2, dann 1, dann zurück auf 0. PageHosts [PageHostBaseIndex] ist jederzeit der aktuellen Seite. Die nächste Seite ist:

pageHosts[(pageHostBaseIndex + 1) % 3]

Die vorherige Seite ist:

pageHosts[(pageHostBaseIndex + 2) % 3]

Mit dieser Regelung nach eine bestimmte Seite in einem Seite-Host festgelegt wurde bleibt es es, bis sie ersetzt wird. Das Programm muss nicht Seiten von einer Seite Host auf einen anderen zu übertragen.

Dieses Schema ermöglicht auch recht einfach Seitenübergänge: benutzen Sie einfach Canvas.SetZIndex, so dass PageHosts [PageHostBaseIndex] über die anderen beiden ist. Aber das ist wahrscheinlich nicht die Art, wie, die Sie zwischen den Seiten wechseln möchten. Es ist eher fad und sogar etwas gefährlich. Die Ungenauigkeit der Fingereingabe ist, so dass der Benutzer möglicherweise versehentlich zwei Seiten voraus anstelle von einer bewegen, und nicht einmal zu realisieren. Aus diesem Grund sind noch dramatischer Seitenübergänge wünschenswert.

BookViewer große Flexibilität im Zusammenhang mit Seitenübergängen erzielt, indem definieren eine PageTransition-Eigenschaft vom Typ PageTransition, eine abstrakte Klasse gezeigt Abbildung 6.

Abbildung 6 Der PageTransition-Klasse

public abstract class PageTransition : DependencyObject
{
  public static readonly DependencyProperty FractionalBaseIndexProperty =
    DependencyProperty.Register("FractionalBaseIndex",
      typeof(double),
      typeof(PageTransition),
      new PropertyMetadata(-1.0, OnTransitionChanged));

  public double FractionalBaseIndex
  {
    set { SetValue(FractionalBaseIndexProperty, value); }
    get { return (double)GetValue(FractionalBaseIndexProperty); }
  }

  public virtual double AnimationDuration 
  {
    get { return 1000; }
  }

  static void OnTransitionChanged(DependencyObject obj, 
                                  DependencyPropertyChangedEventArgs args)
  {
    (obj as PageTransition).OnTransitionChanged(args);
  }

  void OnTransitionChanged(DependencyPropertyChangedEventArgs args)
  {
    double fraction = (3 + this.FractionalBaseIndex) % 1;
    int baseIndex = (int)(3 + this.FractionalBaseIndex - fraction) % 3;
    ShowPageTransition(baseIndex, fraction);
  }

  public abstract void Attach(Panel containerPanel,
                              FrameworkElement pageContainer0,
                              FrameworkElement pageContainer1,
                              FrameworkElement pageContainer2);

  public abstract void Detach();

  protected abstract void ShowPageTransition(int baseIndex, double fraction);
}

PageTransition definiert eine einzelne Abhängigkeitseigenschaft mit dem Namen FractionalBaseIndex, die BookViewer für die Einstellung basierend auf Fingereingabe verantwortlich ist. Wenn der Benutzer den Bildschirm tappt, wird FractionalBaseIndex durch eine DoubleAnimation aus PageHostBaseIndex PageHostBaseIndex plus 1 erhöht. Animationen sind auch ausgelöst, wenn der Benutzer den Bildschirm schnippt. Wenn die Benutzer-zieht, die ein Finger auf dem Bildschirm, FractionalBaseIndex "manuell" festgelegt ist auf den Abstand hat der Benutzer seinen Finger als Prozentsatz der gesamten Breite des Bildschirms gezogen.

Die PageTransition-Klasse trennt FractionalBaseIndex in eine ganze Zahl-Leistungsregistrierung und einen Bruchteil der abgeleiteten Klassen. Die ShowPageTransition wird von abgeleiteten Klassen in einer Weise implementiert, das Merkmal der bestimmten Übergang. Der Standardwert ist SlideTransition, gezeigt Abbildung 7, die Folien der Seiten hin und her. Abbildung 8 zeigt eine Seite in den Blick geschoben wird. Die-Klasse fügt TranslateTransform Objekte auf der Seite Container während des Aufrufs von Attach und entfernt diese während trennen.

Abbildung 7 Der SlideTransition-Klasse

public class SlideTransition : PageTransition
{
  FrameworkElement[] pageContainers = new FrameworkElement[3];
  TranslateTransform[] translateTransforms = new TranslateTransform[3];

  public override double AnimationDuration
  {
    get { return 500; }
  }

  public override void Attach(Panel containerPanel,
                              FrameworkElement pageContainer0, 
                              FrameworkElement pageContainer1, 
                              FrameworkElement pageContainer2)
  {
    pageContainers[0] = pageContainer0;
    pageContainers[1] = pageContainer1;
    pageContainers[2] = pageContainer2;

    for (int i = 0; i < 3; i++)
    {
      translateTransforms[i] = new TranslateTransform();
      pageContainers[i].RenderTransform = translateTransforms[i];
    }
  }

  public override void Detach()
  {
    foreach (FrameworkElement pageContainer in pageContainers)
      pageContainer.RenderTransform = null;
  }

  protected override void ShowPageTransition(int baseIndex, double fraction)
  {
    int nextIndex = (baseIndex + 1) % 3;
    int prevIndex = (baseIndex + 2) % 3;

    translateTransforms[baseIndex].X = -fraction * 
      pageContainers[prevIndex].ActualWidth;
    translateTransforms[nextIndex].X = translateTransforms[baseIndex].X + 
      pageContainers[baseIndex].ActualWidth;
    translateTransforms[prevIndex].X = translateTransforms[baseIndex].X – 
      pageContainers[prevIndex].ActualWidth;
  }
}

A Page Sliding into View

Abbildung 8 eine Seite Abgleiten in die Ansicht

Können Sie die anderen zwei Seitenübergänge, Bar-Menü der Anwendung: FlipTransition ähnelt SlideTransition, außer dass es die PlaneProjection-Transformation für ein 3D-Look verwendet. Die CurlTransition zeigt die Seite, als ob die oberen rechten Ecke zurück gezogen ist. (Ich ursprünglich schrieb den CurlTransition-Code, um von der unteren rechten Ecke locken, und konnte es einfach durch Anpassen der horizontalen Koordinaten zu konvertieren. Sie können es zurück zu den rechten unteren Curl ändern, indem Sie die CurlFromTop-Konstante auf False festlegen und Sie neu compiliert.)

Da MiddlemarchReader den Benutzer an den Anfang eines Kapitels springen kann, ist es nicht mehr möglich, tatsächlichen Seitenzahlen an der Spitze des Programms anzeigen. Stattdessen wird automatisch basierend auf Textlängen Prozentsätze.

Angst vor der Paginierung

In Betrieb beginnt MiddlemarchReader, look and feel wie eine echte e-Book-Reader. Jedoch, sie leiden noch immer unter die schwache Leistung der Paginierung-Logik. In der Tat ist die erste Seite des ersten Kapitels der "Middlemarch" ein wenig verzögert, wenn das Programm auf einem Windows 7-Telefon-Gerät ausgeführt wird, weil das Kapitel beginnt mit einem Absatz, die für mehrere Seiten geht. Paging zurück, nachdem springen mit einem neuen Absatz manchmal eine Sekunde oder so erfordert, und ich nicht noch mutig genug habe, um Benutzer ausgewählten Schriftarten oder Schriftgrößen in das Programm eingeführt, da diese Features von Repagination erfordern.

Gibt es eine Möglichkeit, die Paginierung beschleunigen? Vielleicht. Zweifellos wäre eine solche Verbesserung, elegant, clever und schwieriger, als ich erwarten.

Charles Petzold ist ein langjähriger Redakteur zu MSDN Magazin*.*Seinem jüngsten Buch "Programming Windows Phone 7" (Microsoft Press, 2010), steht als kostenloser Download auf bit.ly/cpebookpdf.