Neue Benutzeroberflächentechnologien

Trägheit bei Mehrfingereingabe

Charles Petzold

Beispielcode herunterladen.

Benutzeroberflächen bei Computern scheinen uns am besten zu gefallen, wenn sie die digitale Natur der zugrunde liegenden Technologie verbergen und uns stattdessen das analoge Gefühl der realen Welt vorgaukeln. Dieser Trend begann damit, dass Befehlszeilen durch grafische Oberflächen ersetzt wurden, und setzte sich mit zunehmender Verwendung von Bildern, Sound und anderen Medien fort.

Die Einbindung der Mehrfingereingabe in Videoanzeigen hat das Ziel, Benutzersteuerelemente realistischer und intuitiver zu gestalten, noch weiter vorangetrieben. Ich glaube, wir haben gerade erst einen kurzen Blick auf das Potenzial der Fingereingabe-Revolution erhascht, das die Beziehung zum Computerbildschirm neu gestalten wird.

Vielleicht können wir sogar eines Tages eines der unschönsten Überbleibsel der digitalen Technologie ausmerzen: die Schaltfläche für die Lautstärkeregelung. Der einfache Drehknopf für die Lautstärkeregelung (und andere Einstellungen) an Radios und Fernsehgeräten war erfrischend simpel, wurde jedoch durch unsägliche Schaltflächen auf Fernbedienungen und an den Geräten selbst ersetzt.

Computeroberflächen bieten häufig Schieberegler für die Lautstärkeregelung an. Sie sind fast so gut wie Drehknöpfe. Dennoch sind Schaltflächen für die Regelung der Lautstärke nach wie vor im Einsatz. Sogar der Mehrfingereingabe-Bildschirm des Zune HD-Geräts verlangt von Ihnen, auf Plus- und Minuszeichen zu drücken, um den Ton lauter oder leiser zu stellen. Am besten wäre wohl ein Drehknopf, der auf Fingereingabe reagiert.

Ich finde die Idee des Drehknopfs für Mehrfingereingabe sehr gut. Wahrscheinlich habe ich mich deshalb in meinem Artikel „Finger Style: Einführung in die Multitouch-Unterstützung in Silverlight“ in der Ausgabe vom März 2010 des MSDN Magazin (msdn.microsoft.com/magazine/ee336026) auf die Simulation von Drehknöpfen konzentriert. Zudem lege ich dar, warum ich in Bezug auf Trägheit bei der Mehrfingereingabe in Windows Presentation Foundation (WPF) wieder zum guten alten Drehknopf zurückkehre.

Das Trägheitsereignis

Eine Mehrfingereingabe-Oberfläche versucht beispielsweise, die reale Welt durch Einführung der Trägheit nachzubilden. Das heißt, die Tendenz eines Objekts, dieselbe Geschwindigkeit so lange beizubehalten, bis eine andere Kraft (z. B. Reibung) eine Veränderung erzwingt. Bei Oberflächen mit Mehrfingereingabe kann die Trägheit dafür sorgen, dass ein sichtbares Objekt noch bewegt wird, während die Finger den Bildschirm bereits verlassen haben. Am häufigsten wird die Mehrfingereingabe-Trägheit bei der Listennavigation eingesetzt, und in das WPF-ListBox-Element ist sie bereits integriert.

Im herunterladbaren Beispielcode für diesen Artikel befindet sich ein kleines Programm mit dem Namen IntertialListBoxDemo. Es dient dazu, ein WPF-ListBox-Element mit mehreren Objekten zu füllen, sodass Sie einen Test auf Ihrem Mehrfingereingabe-Bildschirm durchführen können.

Sie können in Ihren WPF-Steuerelementen selbst festlegen, welches Maß an Trägheit Sie wünschen. Die Trägheitseinstellung ist am einfachsten, wenn Sie mittels Trägheit nur dieselbe Reaktion von Ihrem Programm erzeugen wollen wie eine direkte Bedienung. Der komplizierte Teil besteht darin, ein gutes Verständnis für die benötigten numerischen Werte zu entwickeln.

Wie Sie bereits in früheren Artikeln gelesen haben, wird ein WPF-Element anhand von zwei Ereignissen über den Einsatz der Mehrfingereingabe benachrichtigt: ManipulationStarting (eine gute Möglichkeit zum Ausführen von Initialisierungen) und ManipulationStarted. Nach diesen beiden Ereignissen folgen meist sehr viele ManipulationDelta-Ereignisse, mit denen die Aktivitäten eines oder mehrerer Finger in Translations-, Skalierungs- und Rotationsinformationen übertragen werden.

Wenn alle Finger das zu manipulierende Element wieder verlassen haben, tritt ein ManipulationInertiaStarting-Ereignis ein. Hier gibt Ihnen das Programm die Möglichkeit, die gewünschte Trägheit festzulegen. (Ich erkläre das gleich noch genauer.) Die Trägheit wirkt sich in Form weiterer ManipulationDelta-Ereignisse aus, bis die Bewegung des Objekts zu Ende geführt ist. Dies wird durch das Ereignis ManipulationCompleted signalisiert. Nehmen Sie keinerlei Änderungen an ManipulationInertiaStarting vor, folgt direkt ManipulationCompleted.

Da das Ereignis ManipulationDelta sowohl zur Angabe direkter Manipulationen als auch der Trägheit eingesetzt werden kann, ist das gesamte Schema ausgesprochen einfach zu verwenden. Im Grunde genommen können Sie die Trägheit mit einer einzigen Anweisung im Ereignis ManipulationInertiaStarting implementieren. Falls erforderlich, können Sie eine Unterscheidung zwischen direkter Manipulation und Trägheit mittels der Eigenschaft IsInertial von ManipulationDeltaEventArgs treffen.

Wie Sie sehen werden, haben Sie zwei Möglichkeiten, um das Maß der gewünschten Trägheit festzulegen. Beide nutzen das Prinzip der Verlangsamung: Die Geschwindigkeit des Objekts wird nach und nach reduziert, bis sie auf null gefallen ist und das Objekt sich nicht weiter bewegt. Dazu sind einige physikalische Prinzipien erforderlich, mit denen sich Programmierer nur selten beschäftigen. Deshalb folgt nun eine kurze Auffrischung.

Auffrischung: Beschleunigung

Wenn sich etwas bewegt, hat es ein Tempo bzw. eine Geschwindigkeit, die in Wegstrecke pro Zeiteinheit gemessen werden kann. Ändert sich die Geschwindigkeit innerhalb der Zeitspanne, unterliegt das Objekt einer Beschleunigung. Die negative Beschleunigung, also ein Abfall der Geschwindigkeit, wird meist als Verlangsamung bezeichnet.

Gehen wir in diesem Rahmen von der Annahme aus, dass die Beschleunigung oder Verlangsamung konstant ist. Eine konstante Beschleunigung bedeutet, dass sich die Geschwindigkeit linear verändert, mit einem gleichbleibenden Wert pro Zeiteinheit. Bei einer Beschleunigung von 0 bleibt die Geschwindigkeit konstant.

Beispielsweise werben Autohersteller damit, dass ihre PKW in 8 Sekunden von 0 auf 60 mph (Meilen pro Stunde) beschleunigen können. Zunächst steht das Auto, aber nach den 8 Sekunden fährt es mit einer Geschwindigkeit von 60 mph, wie in Abbildung 1 veranschaulicht wird.

Abbildung 1 Beschleunigung

Sekunden Geschwindigkeit
0 0 mph
1 7,5
2 15
3 22,5
4 30
5 37,5
6 45
7 52,5
8 60

Wie Sie sehen, wird die Geschwindigkeit pro Sekunde gleichmäßig erhöht. Die Beschleunigung wird als Veränderung der Geschwindigkeit in einem bestimmten Zeitraum ausgedrückt, in diesem Fall also 7,5 mph pro Sekunde.

Dieser Beschleunigungswert ist ein wenig heikel, da im Grunde zwei Zeiteinheiten beteiligt sind: Stunden und Sekunden. Wir wollen nun nur in Sekunden rechnen und die Stunden umrechnen. Da 60 mph der Angabe von 88 ft (Fuß) pro Sekunde entspricht, können wir auch sagen, dass der PKW in 8 Sekunden von 0 auf 88 ft pro Sekunde beschleunigt. Damit entspricht die Beschleunigung 11 ft pro Sekunde in der Sekunde oder, wie es häufig ausgedrückt wird, 11 ft pro Sekunde im Quadrat.

Wir können auch wieder Meilen und Stunden verwenden, aber die Einheiten geraten ins Sinnlose, da die Beschleunigung mit 27.000 mph im Quadrat berechnet wird. Das heißt, am Ende einer Stunde fährt das Auto mit 27.000 mph. Das ist natürlich ganz unmöglich. In Wahrheit verhält es sich so, dass sich die Geschwindigkeit des PKW nach Erreichen der 60 mph einpendelt und die Beschleunigung auf 0 absinkt. Das ist eine Veränderung der Geschwindigkeit, die von Ingenieuren auch als Ruck bezeichnet wird.

In diesem Beispiel unterstellen wir jedoch eine konstante Beschleunigung. Bei einer Startgeschwindigkeit von 0 und einer Beschleunigung a wird Distanz x in der Zeit t zurückgelegt:

image: equation, x equals one-half a t squared

Die Angaben ½ und Quadrat sind nötig, da die Geschwindigkeit v als erste Ableitung der Distanz unter Berücksichtigung der Zeit berechnet wird:

image: equation, v equals d x over d t equals a t

Wenn a dem Wert von 11 ft pro Sekunde im Quadrat entspricht und t den Wert 1 Sekunde aufweist, liegt eine Geschwindigkeit von 11 ft pro Sekunde vor, obwohl das Auto nur 5,5 ft zurückgelegt hat. Entspricht t hingegen 2 Sekunden, liegt die Geschwindigkeit bei 22 ft pro Sekunde, und der PKW hat insgesamt 22 ft zurückgelegt. Pro Sekunde legt das Auto eine Distanz zurück, die auf der durchschnittlichen Geschwindigkeit während dieser Sekunde basiert. Weist t den Wert von 8 Sekunden auf, beträgt die Geschwindigkeit 88 ft pro Sekunde, und das Auto hat insgesamt eine Distanz von 352 ft zurückgelegt.

Die Trägheit bei der Mehrfingereingabe lässt sich mit der Beobachtung unseres Autos vergleichen – nur umgekehrt: Zu dem Zeitpunkt, an dem die Finger vom Bildschirm entfernt werden, besitzt das Objekt eine bestimmte Geschwindigkeit. Seitens der Anwendung wird eine Verlangsamung berechnet, damit die Geschwindigkeit des Objekts linear abfällt auf den Wert 0. Je höher der Verlangsamungswert, desto schneller stoppt die Bewegung des Objekts. Bei einer Verlangsamung von 0 bewegt sich das Objekt immer mit derselben Geschwindigkeit fort, ohne jemals anzuhalten.

Zwei Möglichkeiten der Verlangsamung

Die Argumente des ManipulationDelta-Ereignisses umfassen eine Velocities-Eigenschaft des Typs ManipulationVelocities, die wiederum drei eigene Eigenschaften für die drei Arten der Manipulation aufweist:

  • LinearVelocity des Typs Vektor
  • ExpansionVelocity des Typs Vektor
  • AngularVelocity des Typs Double

Die ersten beiden Eigenschaften werden als geräteunabhängige Einheiten pro Millisekunde angegeben, die dritte in Winkeln (Grad) pro Millisekunde. Die Einheit Millisekunde ist keine intuitiv erfassbare Zeitspanne, wenn Sie also die Bedeutung dieser Werte verstehen wollen, sollten Sie diese in Sekunden umrechen (mit 1000 multiplizieren).

Anwendungen nutzen diese Geschwindigkeitseigenschaften nur selten, aber sie liefern die Startgeschwindigkeiten für die Trägheit. Wenn die Finger des Benutzers den Bildschirm verlassen, tritt ein ManipulationInertiaStarting-Ereignis ein. Die Ereignisargumente enthalten diese drei Eigenschaften, mit denen Sie die Trägheit für alle drei Manipulationsarten separat angeben können:

  • TranslationBehavior des Typs InertiaTranslationBehavior
  • ExpansionBehavior des Typs InertiaExpansionBehavior
  • RotationBehavior des Typs InertiaRotationBehavior

Jede dieser Klassen verfügt über die Eigenschaften InitialVelocity und DesiredDeceleration sowie eine dritte, je nach Klasse variierende Eigenschaft DesiredDisplacement, DesiredExpansion oder DesiredRotation.

Bei Translation und Rotation haben alle Desired-Eigenschaften den Standardwert Double.NaN. Diese besondere Konfiguration gibt an, dass es sich um keinen numerischen Wert („Not a Number“) handelt. Bei Expansion haben die Desired-Eigenschaften des Typs Vektor X- und Y-Werte für Double.NaN, das Prinzip ist jedoch dasselbe.

Konzentrieren wir uns zunächst auf die Rotationsträgheit, da ihre Wirkung aus der Realität bekannt ist (z. B. vom Drehkarussel auf dem Spielplatz), und Sie sich keine Gedanken machen müssen, dass Objekte vom Bildschirm fliegen.

Zum Zeitpunkt des Ereignisses ManipulationInertiaStarting ist das InertiaRotationBehavior-Objekt erstellt worden, und der Wert für InitialVelocity wurde vom vorherigen Ereignis ManipulationDelta festgelegt. Hat InitialVelocity den Wert 1,08, entspricht dies 1,08 Grad pro Millisekunde und 1080 Grad pro Sekunde oder drei Umdrehungen pro Sekunde und 180 Umdrehungen pro Minute.

Damit sich das Objekt weiterhin dreht, legen Sie entweder DesiredRotation oder DesiredDeceleration fest, aber nicht beide. Falls Sie versuchen, beide Werte festzulegen, wird der zuletzt eingegebene übernommen, der andere wird geändert in Double.NaN.

Die erste Möglichkeit besteht darin, DesiredRotation auf einen in Grad angegebenen Wert festzulegen. Damit geben Sie an, wie häufig sich das Objekt dreht, bevor es zum Stillstand kommt. Legen Sie beispielsweise den Wert 360 für DesiredRotation fest, führt das sich drehende Objekt nur eine weitere Umdrehung aus, verlangsamt dabei und stoppt. Der Vorteil liegt darin, dass Sie unabhängig von der Startgeschwindigkeit dieselbe Trägheitsaktivität erhalten, das Ergebnis ist also leicht vorhersehbar. Der Nachteil ist, dass das Verhalten nicht gänzlich natürlich ist.

Alternativ können Sie DesiredDeceleration auf einen Wert zu Einheiten in Grad pro Millisekunde im Quadrat festlegen. Das ist ein wenig heikel, denn ein passender Wert ist nur schwer einzuschätzen.

Wenn InitialVelocity bei 1,08 Grad pro Millisekunde liegt, und Sie legen DesiredDeceleration auf 0,01 Grad pro Millisekunde im Quadrat fest, nimmt die Geschwindigkeit um 0,01 Grad pro Millisekunde ab: Nach der ersten Millisekunde sinkt sie auf 1,07 Grad pro Millisekunde, nach der zweiten auf 1,06 Grad pro Millisekunde und so weiter, bis eine Geschwindigkeit von 0 erreicht ist. Der gesamte Prozess dauert 108 Millisekunden oder kaum länger als eine Zehntelsekunde.

Möglicherweise möchten Sie für DesiredDeceleration einen geringeren Wert festlegen, z. B. 0,001 Grad pro Millisekunde, sodass sich das Objekt für 1,08 Sekunden dreht.

Wenn Sie die Einheiten der Verlangsamung in eine etwas gefälligere Einheit umrechnen wollen, müssen Sie gut aufpassen. Eine Geschwindigkeit von 1,08 Grad pro Millisekunde entspricht 1080 Grad pro Sekunde. Aber eine Verlangsamung von 0,001 Grad pro Millisekunde im Quadrat entspricht 1000 Grad pro Sekunde im Quadrat. Um die Verlangsamung in Sekunden umzurechnen, müssen Sie zweimal mit 1000 multiplizieren, da die Zeit im Quadrat angegeben wird.

Wenn Sie nun die beiden vorigen Formeln kombinieren und t herausnehmen, erhalten Sie folgende Formel:

Image: equation, a equals v squared over 2 x

Das bedeutet, die beiden Methoden zum Festlegen der Verlangsamung sind gleichwertig und können von einer in die andere umgesetzt werden. Wenn InitialVelocity bei 1,08 Grad pro Millisekunde liegt, und Sie DesiredDeceleration auf 0,001 Grad pro Millisekunde im Quadrat festlegen, ist das gleichwertig damit, den Wert von DesiredRotation auf 583,2 Grad festzusetzen. In beiden Fällen stoppt die Rotation nach 1080 Millisekunden.

Experimentieren

Um ein Gefühl für die Rotationsträgheit zu bekommen, habe ich das in Abbildung 2 dargestellte Programm RotationalInertiaDemo entwickelt.

image: RotationalInertiaDemo Program in Action

Abbildung 2 Das Programm RotationalInertiaDemo in Aktion

Den Drehknopf auf der linken Seite drehen Sie mit dem Finger im oder entgegen dem Uhrzeigersinn. Es ist ganz einfach: Nur eine UserControl-Ableitung mit zwei Ellipsenelementen in einem Raster, die Manipulation-Ereignisse werden alle im Hauptfenster „MainWindow“ verarbeitet.

Das ManipulationStarting-Ereignis führt die Initialisierung aus, indem die Manipulation auf die Rotation beschränkt wird. Die Rotation wird mit einem Finger verändert, und es wird ein Rotationszentrum festgelegt:

args.IsSingleTouchEnabled = true;
args.Mode = ManipulationModes.Rotate;
args.Pivot = new ManipulationPivot(
             new Point(ctrl.ActualWidth / 2, 
                       ctrl.ActualHeight / 2), 50);

Die Geschwindigkeitsanzeige auf der rechten Seite ist eine Klasse mit der Bezeichnung ValueMeter, hier wird die aktuelle Geschwindigkeit des Rads angegeben. Falls Ihnen das vage bekannt vorkommt: Es handelt sich um die erweiterte Version einer ProgressBar-Vorlage aus einem Artikel, den ich vor über drei Jahren für MSDN Magazin geschrieben habe. Diese Erweiterungen umfassen in erster Linie die Anpassung der Beschriftung, sodass ich nun die Geschwindigkeit in vier verschiedenen Einheiten anzeigen kann. Mit dem GroupBox-Element in der Fenstermitte können Sie diese Einheiten auswählen.

Wenn Ihr Finger den Drehknopf dreht, zeigt der Geschwindigkeitsmesser die derzeitige Winkelgeschwindigkeit an. Diese wird von der untergeordneten Eigenschaft Velocities.AngularVelocity der ManipulationDelta-Ereignisargumente bereitgestellt. Leider musste ich feststellen, dass eine direkte Übertragung der Geschwindigkeit an ValueMeter nicht möglich ist. Die Anzeige hat zu stark gezittert. Daraufhin habe ich eine kleine ValueSmoother-Klasse geschrieben, um einen gewichteten Mittelwert aus den Werten der letzten Viertelsekunde zu berechnen. Über den ManipulationDelta-Ereignishandler wird auch ein RotateTransform-Objekt festgelegt, das die eigentliche Drehbewegung des Drehknopfs ausführt:

rotate.Angle += args.DeltaManipulation.Rotation;

Über den Schieberegler im unteren Bereich können Sie einen Wert für die Verlangsamung angeben. Der Wert des Schiebereglers wird nur gelesen, wenn der Finger vom Drehknopf entfernt wird und somit das ManipulationInertiaStarted-Ereignis auslöst:

args.RotationBehavior.DesiredDeceleration = slider.Value;

Das ist die Kernstruktur des ManipulationInertiaStarted-Ereignishandlers. Während der Trägheitsphase der Manipulation sind sehr gleichmäßige Geschwindigkeitswerte vorhanden, bei denen keine Glättung erforderlich ist. Also bestimmt der ManipulationDelta-Handler anhand seiner IsInertial-Eigenschaft, wann die Geschwindigkeitswerte direkt an die Geschwindigkeitsanzeige übermittelt werden sollen.

Begrenzung und Hüpfer

Am häufigsten wird Trägheit in Verbindung mit der Mehrfingereingabe eingesetzt, um Objekte auf dem Bildschirm zu bewegen, z. B. um eine lange Liste durchzublättern oder Elemente beiseite zu schieben. Das große Problem besteht darin, wie einfach es ist, ein Element quasi vom Bildschirm zu fegen.

Bei der Lösung dieses kleinen Problemchens werden Sie feststellen, dass WPF über ein integriertes Feature verfügt, mit dem die Trägheitsmanipulation noch realistischer wird. Ihnen ist vielleicht vorher beim ListBoxDemo-Programm aufgefallen, dass das ganze Fenster ein wenig hüpft, wenn ListBox beim Blättern zum Anfang oder zum Ende einer Liste den Endpunkt erreicht. Diese Wirkung können Sie auch in Ihren eigenen Anwendungen erzielen, sofern Sie das möchten.

Das BoundaryDemo-Programm besteht aus nur einer Ellipse, wobei MatrixTransform für die Identität auf die RenderTransform-Eigenschaft in einem Raster mit der Bezeichnung mainGrid festgelegt ist. Beim Überschreiben von OnManipulationStarting ist nur Translation aktiviert. Von der Methode OnManipulationInertiaStarting wird folgende Verlangsamung für die Trägheit festgesetzt:

args.TranslationBehavior.DesiredDeceleration = 0.0001;

Das sind 0,0001 geräteunabhängige Einheiten pro Millisekunde im Quadrat oder 100 geräteunabhängige Einheiten pro Sekunde im Quadrat oder etwa 1 Zoll pro Sekunde im Quadrat.

Die OnManipulationDelta-Überschreibung wird in Abbildung 3 dargestellt. Achten Sie auf den Fall, dass für IsInertial der Wert „True“ gilt. Der hinter dem Code stehende Gedanke ist, dass die Translationsfaktoren abgeschwächt werden, wenn die Ellipse teilweise vom Bildschirm verschwunden ist. Befindet sich die Ellipse in mainGrid, ist der Abschwächungsfaktor 0, hat die Ellipse die mainGrid-Begrenzung zur Hälfte überschritten, erhöht sich der Faktor auf 1. Der Abschwächungsfaktor wird dann auf den an die Methode (mit dem Namen totalTranslate) übergebenen Translationsfaktor angewendet, um usableTranslate zum Bereitstellen der tatsächlich auf TransformMatrix angewendeten Werte zu berechnen.

Abbildung 3 Das OnManipulationDelta-Ereignis in BoundaryDemo

protected override void OnManipulationDelta(
  ManipulationDeltaEventArgs args) {

  FrameworkElement element = 
    args.Source as FrameworkElement;
  MatrixTransform xform = 
    element.RenderTransform as MatrixTransform;
  Matrix matx = xform.Matrix;
  Vector totalTranslate = 
    args.DeltaManipulation.Translation;
  Vector usableTranslate = totalTranslate;

  if (args.IsInertial) {
    double xAttenuation = 0, yAttenuation = 0, attenuation = 0;

    if (matx.OffsetX < 0)
      xAttenuation = -matx.OffsetX;
    else
      xAttenuation = matx.OffsetX + 
        element.ActualWidth - mainGrid.ActualWidth;

    if (matx.OffsetY < 0)
      yAttenuation = -matx.OffsetY;
    else
      yAttenuation = matx.OffsetY + 
        element.ActualHeight - mainGrid.ActualHeight;

    xAttenuation = Math.Max(0, Math.Min(
      1, xAttenuation / (element.ActualWidth / 2)));

    yAttenuation = Math.Max(0, Math.Min(
      1, yAttenuation / (element.ActualHeight / 2)));

    attenuation = Math.Max(xAttenuation, yAttenuation);

    if (attenuation > 0) {
      usableTranslate.X = 
        (1 - attenuation) * totalTranslate.X;
      usableTranslate.Y = 
        (1 - attenuation) * totalTranslate.Y;

      if (totalTranslate != usableTranslate)
        args.ReportBoundaryFeedback(
          new ManipulationDelta(totalTranslate – 
          usableTranslate, 0, new Vector(), new Vector()));

      if (attenuation > 0.99)
        args.Complete();
    }
  }
  matx.Translate(usableTranslate.X, usableTranslate.Y);
  xform.Matrix = matx;

  args.Handled = true;
  base.OnManipulationDelta(args);
}

Im Ergebnis hält die Ellipse nicht einfach an, sobald sie auf die Begrenzung trifft, sondern verlangsamt sich erheblich, so als ob sie auf eine schwer durchdringbare Schicht gestoßen wäre.

Von der OnManipulationDelta-Überschreibung wird auch die Methode ReportBoundaryFeedback aufgerufen und an den nicht verwendeten Teil des Translationsvektors übergeben, also an den Vektor totalTranslate minus usableTranslate. Standardmäßig verfolgt diese Verarbeitung den Zweck, das Fenster beim Verlangsamen der Ellipse ein kleines bisschen hüpfen zu lassen und so das physikalische Grundprinzip darzustellen, dass auf jede Aktion eine gleichwertige, entgegengesetzte Reaktion folgt.

Der Effekt ist am größten, wenn die Geschwindigkeit beim Aufprall relativ hoch ist. Bewegt sich die Ellipse bereits stark verlangsamt, ist der Vibrationseffekt weniger spannend, aber das können Sie selbst im Detail bestimmen. Möchten Sie diese Wirkung gar nicht (oder nur in besonderen Fällen) erzielen, sorgen Sie einfach dafür, dass ReportBoundaryFeedback nicht aufgerufen wird. Sie können das ManipulationBoundaryFeedback-Ereignis auch selbst ausführen. Indem Sie die Handled-Eigenschaft des Ereignisarguments auf den Wert „True“ setzen, unterbinden Sie die Standardverarbeitung. Sie können sich natürlich auch für eine andere Vorgehensweise entscheiden. Ich habe im Programm eine OnManipulationBoundaryFeedback-Methode für Sie vorbereitet, mit der Sie üben können.

Charles Petzold schreibt seit Langem redaktionelle Beiträge für MSDN Magazin*. Zurzeit schreibt er den Artikel „Programming Windows Phone 7“ (Microsoft Press), der als kostenloses, herunterladbares E-Book im Herbst 2010 veröffentlicht wird. Eine Vorabausgabe ist derzeit auf seiner Website charlespetzold.com verfügbar.*

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Doug Kramer und Robert Levy