MSDN Magazin > Home > Ausgaben > 2008 > August >  Schreiben!: Erstellen Sie mit Silverlight 2 Web...
Schreiben!
Erstellen Sie mit Silverlight 2 Webanwendungen, in denen Sie zeichnen können
Julia Lerman

Themen in diesem Artikel:
  • InkPresenter-Steuerelement in Silverlight
  • Freihandeingabe in einer Webanwendung
  • Handschrifterkennung
  • Erstellen von transparenten und Bildhintergründen
In diesem Artikel werden folgende Technologien verwendet:
Silverlight 2, Expression Blend, Visual Studio 2008
Dieser Artikel basiert auf Vorabversionen von Visual Studio 2008 SP1, Silverlight 2 und Expression Blend. Änderungen der Informationen in diesem Artikel sind vorbehalten.
Silverlight ist eine der interessantesten neuen Webtechnologien, die in den vergangenen Jahren von Microsoft entwickelt wurden. So wichtig ist sie, dass Silverlight™ und die zahlreichen Möglichkeiten der Technologie im Mittelpunkt der jährlichen MIX-Konferenz zu stehen scheinen. Silverlight 1.0 wurde 2007 veröffentlicht. Jede neue Version von Silverlight 2 vor der endgültigen Version wartete mit beeindruckenden neuen Features auf. Eines der überaus nützlichen Features von Silverlight, das bisher nicht die verdiente Aufmerksamkeit erhielt, ist das InkPresenter-Steuerelement. Das InkPresenter-Steuerelement ermöglicht es Internetnutzern, von ihrem Browser aus direkt in Silverlight-Anwendungen zu zeichnen.
Da Silverlight mit verschiedenen Betriebssystemen und in verschiedenen Browsern verwendet werden kann, gilt dies auch für InkPresenter. Damit entfallen eine Reihe von Einschränkungen für Browser, Betriebssystem und Hardware. Die hier vorgestellte Beispielanwendung verwendet die Microsoft® .NET Framework-Programmierungsfeatures von Silverlight 2 in Kombination mit InkPresenter. Benutzer sind damit in der Lage, eine vordefinierte Sammlung von Bildern zu kommentieren, Handschrifterkennung durchzuführen, die Anmerkungen und erkannten Text in einer serverseitigen Datenbank zu speichern, Anmerkungen für ein ausgewähltes Bild abzurufen und Bilder auf der Grundlage des zugeordneten Texts zu filtern. Die Datenbank und die Funktion der Handschrifterkennung werden von einem WCF-Dienst (Windows® Communication Foundation) zur Verfügung gestellt. Abbildung 1 zeigt die fertige Anwendung.
Abbildung 1 Die fertige Anwendung (zum Vergrößern auf das Bild klicken)
Beachten Sie, dass alles, was in diesem Artikel beschrieben wird, auch in Silverlight 1.0 erreicht werden kann. Bevor Silverlight 2 zur Verfügung stand, habe ich sogar eine ähnliche Anwendung in Silverlight 1.0 mit den gleichen Features geschrieben. Wenn Ihnen jedoch auf der Clientseite kein .NET-Code zur Verfügung steht, müssten Sie für mehr Features von .NET-Webdiensten Gebrauch machen.

Einführung in den InkPresenter
Das InkPresenter-Steuerelement, das diese Anwendung ermöglicht, ist ein Container für eine Sammlung von Strichen. Jeder Strich besteht aus einer Sammlung von Stiftpunkten. Beachten Sie, dass es in Silverlight noch eine Stroke-Eigenschaft gibt, die zur Shape-Klasse gehört. Diese beiden dürfen nicht verwechselt werden.
Stellen Sie sich vor, Sie verwenden Stift und Papier. Jedes Mal, wenn Sie mit dem Stift das Papier berühren, beginnen Sie einen Strich. Sie führen den Stift dann auf dem Papier weiter und zeichnen z. B. einen Kreis oder schreiben ein Wort. Wenn Sie den Stift vom Papier nehmen, signalisiert dies das Ende dieses Strichs. Wenn Sie den Stift erneut ansetzen, beginnen Sie einen neuen Strich.
Striche besitzen Zeichnungsattribute, die Eigenschaften wie die Farbe und Breite des Strichs sowie einige andere Attribute definieren. Auch die einzelnen Punkte in einem Strich besitzen Eigenschaften: X- und Y-Koordinaten für die Position und einen PressureFactor. Die PressureFactor-Eigenschaft wird für Computer mit einem Digitalisierungsgerät interpretiert und ermöglicht es Ihnen, den Effekt des Strichs programmgesteuert festzulegen, in Abhängigkeit davon, wie hart der Benutzer mit dem Tablettstift aufdrückt. Abbildung 2 zeigt die Klassenhierarchie.
Abbildung 2 Die InkPresenter-Klasse (zum Vergrößern auf das Bild klicken)
Wie andere visuelle Elemente in Silverlight können auch das InkPresenter-Objekt und seine untergeordneten Objekte als XAML dargestellt werden. Abbildung 3 zeigt drei kleine Striche in einem InkPresenter. Abbildung 4 zeigt die XAML-Darstellung eines Strichs in der Strichsammlung. Obwohl die Striche klein sind, sammelt das Digitalisierungsgerät recht viele Daten. Bei Ausführung desselben Tests mit einer Maus würden viel weniger Daten gesammelt werden, da die Stiftpunkte nur Doubles wären und Punkte in weniger häufigen Intervallen erfasst würden.
Abbildung 3 Einige Striche
<StrokeCollection>
<StrokeCollection xmlns="http://schemas.microsoft.com/client/2007">
  <Stroke>
    <Stroke.DrawingAttributes>
      <DrawingAttributes Color="#FF000000"         OutlineColor="#00000000" Width="3" Height="3" />
    </Stroke.DrawingAttributes>
    <Stroke.StylusPoints>
      <StylusPoint X="81.4583358764648" Y="96.5833282470703" />
      <StylusPoint X="81.4583358764648" Y="96.5833282470703" />
      <StylusPoint X="81.0833358764648" Y="96.4166717529297" />
      <StylusPoint X="81.0833358764648" Y="96.4166717529297" />
      <StylusPoint X="81.0833358764648" Y="96.4166717529297" />
      <StylusPoint X="81.0833358764648" Y="96.4166717529297" />
      <StylusPoint X="81.0833358764648" Y="96.4166717529297" />
      <StylusPoint X="80.4583358764648" Y="96.8333282470703" />
      <StylusPoint X="80.4583358764648" Y="96.8333282470703" />
      <StylusPoint X="80" Y="97.2916717529297" />
      <StylusPoint X="80" Y="97.2916717529297" />
      <StylusPoint X="79.625" Y="97.75" />
      <StylusPoint X="79.625" Y="97.75" />
      <StylusPoint X="79.625" Y="97.75" />
      <StylusPoint X="79.625" Y="97.75" />
      <StylusPoint X="79.625" Y="96.5416717529297" />
      <StylusPoint X="79.8333358764648" Y="95.7083358764648" />
      <StylusPoint X="80.25" Y="94.7916641235352" />
      <StylusPoint X="80.7916641235352" Y="93.5416641235352" />
      <StylusPoint X="81.5" Y="92.125" />
      <StylusPoint X="82.4166641235352" Y="90.4583358764648" />
      <StylusPoint X="83.4583358764648" Y="88.5833358764648" />
      <StylusPoint X="84.75" Y="86.5416641235352" />
      <StylusPoint X="86.1666641235352" Y="84.3333358764648" />
      <StylusPoint X="87.7083358764648" Y="82.1666641235352" />
      <StylusPoint X="89.25" Y="79.9166641235352" />
      <StylusPoint X="90.75" Y="77.9583358764648" />
      <StylusPoint X="92" Y="76.0833358764648" />
      <StylusPoint X="93.1666641235352" Y="74.8333358764648" />
      <StylusPoint X="94" Y="73.625" />
      <StylusPoint X="94.7083358764648" Y="73.1666641235352" />
      <StylusPoint X="95.125" Y="73.1666641235352" />
      <StylusPoint X="95.125" Y="73.1666641235352" />
      <StylusPoint X="95.125" Y="73.1666641235352" />
      <StylusPoint X="94.7083358764648" Y="73.5" />
    </Stroke.StylusPoints>

  </Stroke>
...
</StrokeCollection>
Beim Arbeiten mit dem InkPresenter geht es tatsächlich um das Erstellen von und Interagieren mit seinen Strichen. Der InkPresenter geht dabei jedoch nicht standardmäßig vor. Er stellt Ereignisse und Methoden zur Verfügung, und er ermöglicht es Ihnen, Strichsammlungen hinzuzufügen und zu entfernen sowie auf die Striche zuzugreifen, um mit ihnen zu interagieren. Ihre Aufgabe ist es jedoch, die Maus- oder Stiftaktivitäten innerhalb der Begrenzungen eines InkPresenter-Steuerelements programmgesteuert zu verfolgen und die Striche zu erstellen.

Tablet PC nicht erforderlich
Vor Windows Presentation Foundation (WPF) und Silverlight mussten sich Entwickler auf das Tablet PC SDK verlassen, wenn sie benutzerdefinierte Programme erstellen wollten, die sich die Tablet PC-Zeichnungsfeatures zunutze machten. Das SDK ist eine Gruppe von COM-APIs mit .NET-Wrappers, die die Entwicklung in .NET, Visual Basic® 6.0 und C++ ermöglichen. Das erforderliche Betriebssystem war Windows XP Tablet PC Edition.
Ab Version 1.7 des Tablet PC SDK war für das kritische Steuerelement, InkOverlay, zur Ausführung in einem System nicht mehr die volle Vertrauenswürdigkeit erforderlich. Es wurde möglich, freihandaktivierte Windows Forms-Steuerelemente zu erstellen und in Webseiten einzubetten, genau wie bei ActiveX®-Steuerelementen. Obwohl ActiveX-Steuerelemente nur in Internet Explorer® verwendet werden konnten, war dies ein erster Schritt für Entwicklern, um Zeichnungs- und andere Freihandfunktionen im Web zu ermöglichen.
In Windows Vista® wurde die Tablet PC-Funktionalität als ein wesentlicher Bestandteil in das Betriebssystem integriert, und die Entwicklungs-APIs für Tablet-Features wurden in das InkCanvas-Objekt von WPF eingebunden. Dies bedeutet, dass jeder Computer, auf dem .NET Framework 3.0 installiert ist, Tablet-Funktionalität unterstützen kann, selbst wenn es sich nicht um einen Tablet PC handelt. Es muss jedoch beachtet werden, dass die Verwendung eines Stifts auf einem Digitalisiertablett zu einer wesentlich höheren Auflösung (um Größenordnungen) führt als die Verwendung einer Maus.
Die Freihandfunktionen in Silverlight sind eine Teilmenge der Funktionen, die in WPF verfügbar sind. Ein Hauptunterschied besteht jedoch darin, dass InkCanvas in WPF eine Eigenschaft namens „InkPresenter“ besitzt, mit der die Freihandeingabe in InkCanvas angezeigt wird, während Sie in Silverlight mit dem InkPresenter direkt arbeiten müssen, da InkCanvas nicht verfügbar ist.
Besonders wichtig ist, dass freihandaktivierte Websites in mehr Umgebungen verwendet werden können, da Silverlight nicht auf Windows oder Internet Explorer beschränkt ist.

InkPresenter 101
Mit Silverlight Tools für Visual Studio® 2008 wird ein Silverlight XAML-Designer in Visual Studio 2008 eingebettet. Da die Entwurfsoberfläche selbst jedoch schreibgeschützt ist, gestaltet sich die InkPresenter-Entwurfsarbeit in einigen Fällen in Expression Blend™ 2.5 bequemer. Da die beiden Anwendungen sehr gut integriert sind, treten hierbei keine Probleme auf, und wenn Sie genügend Erfahrungen mit Expression Blend gesammelt haben, macht das Entwerfen außerdem viel Spaß.
Ich persönlich beginne mein Projekt am liebsten in Visual Studio, weil die Standardprojektvorlagen sehr hilfreich sind. Wenn später Entwurfsarbeit in XAML erforderlich ist, öffne ich das Projekt in Expression Blend. Dies lässt sich ganz einfach in der Visual Studio-IDE ausführen. Klicken Sie dazu mit der rechten Maustaste im Projektmappen-Explorer auf eine XAML-Datei, und wählen Sie die Option zum Öffnen der Datei in Expression Blend aus. Wenn Ihnen Expression Blend 2.5 nicht zur Verfügung steht, können Sie XAML per Hand direkt in Visual Studio 2008 bearbeiten und die Ergebnisse Ihrer Änderungen sofort sehen.
Beim Erstellen eines Silverlight-Projekts haben Sie die Möglichkeit, Visual Basic oder C# zu verwenden. Obwohl meine Visual Basic-Fähigkeiten besser sind, habe ich mich bei diesem Projekt für C# entschieden, da ich einen Vorrat an Javascript-Code aus Projekten in Silverlight 1.0 zur Verfügung hatte, der sich einfach in C# portieren ließ.
Nach dem Erstellen des Projekts in Visual Studio besteht die erste Aufgabe darin, das Projekt in Expression Blend zu öffnen, damit Sie mit dem Entwerfen beginnen können. Nachdem Sie die XAML-Datei im Expression Blend-Designer geöffnet haben, können Sie der Entwurfsoberfläche das InkPresenter-Steuerelement hinzufügen. Um das InkPresenter-Steuerelement zu finden, müssen Sie die Expression Blend-Objektbibliothek öffnen, indem Sie unten in der Toolbox auf das Symbol >> klicken. Aktivieren Sie dann das Kontrollkästchen „Alles anzeigen“, um das InkPresenter-Steuerelement sowie einige andere weniger gebräuchliche Steuerelemente anzuzeigen. Sie können auch das Suchfeld verwenden, um nach Steuerelementen zu suchen.
Ziehen Sie den InkPresenter auf die XAML-Entwurfsoberfläche. Geben Sie dem Steuerelement anschließend mithilfe des Eigenschaftenfensters einen Namen. Ich habe „inkP“ ausgewählt, was später in diesem Artikel oft im Code verwendet wird.
Wenn der InkPresenter in der Entwurfsoberfläche ausgewählt ist, können Sie seine Begrenzungen sehen. Ist er nicht ausgewählt, scheint er zu verschwinden. Das InkPresenter-Steuerelement ist ein Container, der Freihandeingaben rendern kann. Es besitzt zwar eine Background-Eigenschaft, jedoch weder eine Fill- und Stroke-Eigenschaft (für Rahmen) noch viele der anderen Eigenschaften, die Sie in anderen Steuerelementen finden. Daher benötigen Sie ein weiteres Steuerelement zur Darstellung der visuellen Begrenzungen.
Ein einfaches Beispiel: Fügen Sie dem Canvas ein Rechteck hinzu, dessen Platzierung und Abmessungen denen des InkPresenter entsprechen. Standardmäßig besitzt ein Rechteck einen schwarzen Rahmen (die Stroke-Eigenschaft) mit einer StrokeThickness von 1. Das Rechteck, das ich „inkBorder“ genannt habe, und InkPresenter sollten im Canvas gleichgeordnet sein. Die z-Reihenfolge des InkPresenter muss höher als das (über dem) Rechteck liegen, andernfalls wird der InkPresenter durch das Rechteck verdeckt.
Da es schwierig ist, das InkPresenter-Steuerelement auf der Entwurfsoberfläche zu finden, sollten Sie es im Bereich „Objekte und Zeitachsen“ auswählen, wenn dies erforderlich ist. Dies ist die einfachste Möglichkeit zum Auswählen des Steuerelements. Durch diese Aktion wird das Steuerelement auf der Entwurfsoberfläche hervorgehoben. Wenn Sie die Projektmappe jetzt testen würden (durch Drücken von F5), wäre der Rechteckrahmen sichtbar. Wenn Sie jedoch versuchen, darin mit einer Maus oder einem Tablettstift zu zeichnen, geschieht noch nichts.

Hinzufügen von Ereignissen und Hintergründen
Wie bereits erwähnt, ist der InkPresenter lediglich ein Container für Strichsammlungen. Eigenständig kann er keine Striche erstellen. Diese Aufgabe lösen Sie programmgesteuert, indem Sie auf Ereignisse im InkPresenter reagieren. Die wichtigsten Ereignisse für das Erfassen von Strichen sind MouseLeftButtonDown, MouseMove und MouseLeftButtonUp. Wenn der InkPresenter ein MouseLeftButtonDown-Ereignis empfängt, müssen Sie einen neuen Strich im Speicher erstellen und der Strichsammlung des InkPresenter hinzufügen. Während die Maus im InkPresenter bewegt wird und MouseMove-Ereignisse erstellt werden, müssen Sie diesem Strich Stiftpunkte hinzufügen. Wenn der Benutzer das MouseLeftButtonUp-Ereignis auslöst, entweder durch Lösen des Tablettstifts vom Digitalisiergerät oder durch Loslassen der Maus, müssen Sie den Strich abschließen.
In Expression Blend 2.5 lassen sich Ereignisse sehr einfach im Eigenschaftenfenster mit Steuerelementen verbinden. Wählen Sie das inkP-Steuerelement aus, und klicken Sie dann oben im Eigenschaftenfenster auf das Ereignissymbol, um die Ereignisse des Steuerelements anzuzeigen. Doppelklicken Sie als Nächstes auf das Textfeld für jeden der drei oben genannten Ereignishandler. Für jeden Ereignishandler wird dem Codebehind des XAML-Steuerelements in Visual Studio eine entsprechende Methode hinzugefügt. Die Integration zwischen Visual Studio und Expression Blend funktioniert in beiden Richtungen. Visual Studio erfordert, dass Sie jede Änderung bestätigen, sobald sie vorgenommen wird. Achten Sie daher auf das blinkende Visual Studio-Symbol in der Taskleiste.
Wechseln Sie nach dem Erstellen der drei Handler zu Visual Studio, um den Code in Abbildung 5 hinzuzufügen, der es InkPresenter ermöglicht, Strichdaten zu sammeln und anzuzeigen, während der Benutzer mit dem Steuerelement interagiert. Dieser Code führt die zuvor beschriebenen Aufgaben durch: Erstellen eines neuen Strichs und Hinzufügen von Stiftpunkten. Der Zugriff auf die Stiftpunkte erfolgt über die MouseEventArgs der MouseLeftButtonDown- und MouseMove-Ereignisse.
System.Windows.Ink.Stroke newstroke;

void inkP_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
  inkP.CaptureMouse();
  newStroke = new System.Windows.Ink.Stroke();
  newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkP));
  inkP.Strokes.Add(newStroke);
}
void inkP_MouseMove(object sender, MouseEventArgs e)
{
  if (newStroke != null)
  {
    newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkP));
  }
}
void inkP_MouseLeftButtonUp (object sender, MouseEventArgs e)
{
  newStroke = null;
  inkP.ReleaseMoustCapture();
}
Das Puzzle ist noch nicht ganz vollständig. Der InkPresenter muss eine Background-Eigenschaft besitzen, um die Mausereignisse empfangen zu können. Der Hintergrund ähnelt der Fill-Eigenschaft anderer Steuerelemente, jedoch ohne die zusätzliche Anpassung, die Sie für Fill durchführen können. Je nach Entwurfsszenario könnten Sie den Hintergrund kreativ gestalten. Legen Sie jedoch vorerst für die Background-Eigenschaft einfach „Transparent“ fest.
Sie können diesen Hintergrund in Expression Blend mithilfe des Eigenschaftenfensters für das InkPresenter-Steuerelement einstellen, indem Sie für den Hintergrund einen einfarbigen Pinsel auswählen und dann für den Alphawert 0 festlegen. Sie können jedoch auch einfach „Background="Transparent"“ direkt in das XAML eingeben.
Im Folgenden finden Sie das XAML für die beiden Steuerelemente nach der Verknüpfung der Ereignisse und der Zuordnung der Background-Eigenschaft:
<Rectangle Margin="20,30,35,24" 
  x:Name="inkBorder" Stroke="#FF000000"/>
<InkPresenter Margin="20,30,35,24" 
  x:Name="inkP" 
  MouseLeftButtonDown=
    "inkP_MouseLeftButtonDown" 
  MouseLeftButtonUp=
    "inkP_ MouseLeftButtonUp" 
  MouseMove="inkP_MouseMove" 
  Background="Transparent" 
  Opacity="1"/>
Wenn Sie das Projekt jetzt in Expression Blend oder Visual Studio ausführen, können Sie die Striche im InkPresenter sehen, wie sie gezeichnet wurden (siehe Abbildung 6).
Abbildung 6 Striche im InkPresenter

InkPresenter mit Stil
Wie bereits erläutert, ist der InkPresenter ein Container, ähnelt jedoch mehr einem Canvas als anderen visuellen Elementen, wie z. B. einem Rechteck. Zur visuellen Aufwertung müssen Sie InkPresenter mit anderen Elementen kombinieren, andernfalls würden Sie Silverlight Unrecht tun. Öffnen Sie daher das vorhandene XAML in Expression Blend, um das Ganze ein wenig interessanter zu gestalten. Verwenden Sie hierzu das Rechteck, das Sie in einem früheren Schritt hinzugefügt haben, um den Rahmen für den InkPresenter zu erstellen.
Zunächst wollen wir die Ecken des Rahmenrechtecks abrunden. Wählen Sie das Rechteck aus, und ändern Sie die RadiusX- und RadiusY-Eigenschaften in 25. Die Ränder des Rechtecks sind jetzt schön abgerundet, die Ecken des InkPresenter ragen jedoch über den visuellen Rahmen hinaus und akzeptieren Freihandeingaben. Die Lösung besteht darin, die Begrenzung des InkPresenter zu ändern oder zu beschneiden, damit sie mit dem visuellen Rahmen übereinstimmt. Sie können das Beschneidungsfeature in Silverlight verwenden, um InkPresenter eine neue Form zu geben.
In Expression Blend lässt sich ein Element leicht so beschneiden, dass es der Form eines anderen Elements entspricht. Erstellen Sie zuvor jedoch eine Kopie des inkBorder-Rechtecks für die spätere Verwendung. Klicken Sie im Fenster „Objekte und Zeitachsen“ mit der rechten Maustaste auf das neue Rechteck. Wählen Sie im Kontextmenü „Pfad“ und dann „Beschneidungspfad erstellen“ aus. In Expression Blend wird daraufhin ein Fenster angezeigt, in dem Sie aufgefordert werden, das Objekt auszuwählen, das durch den Pfad beschnitten wird, das heißt, das Objekt, das die Form des Rechtecks annehmen wird. Wählen Sie den InkPresenter aus. Folgendes geschieht: InkPresenter hat jetzt die Form des Rechtecks und das neue Rechteck ist verschwunden. In dem Moment, als das Rechteck zum Beschneidungspfad für InkPresenter wurde, hörte es auf, als Objekt zu existieren. Nun ist klar, warum Sie das Rechteck kopiert haben.
Das sich ergebende XAML sieht aus wie in Abbildung 7 dargestellt. Führen Sie das Projekt aus, um den neuen Rand von InkPresenter zu testen. Es sieht aus wie in Abbildung 8 dargestellt. Da Sie in Silverlight eine beliebige Form als Beschneidungspfad verwenden können, ist es möglich, eine beliebige Form für InkPresenter zu erstellen. Abbildung 9 zeigt ein Beispiel für eine willkürlich ausgewählte Form, die zum Beschneiden von InkPresenter verwendet wird.
<Rectangle x:Name="inkBorder" Width="346" Height="234"
  Stroke="#FF000000" Canvas.Top="25" Canvas.Left="25" 
  RadiusX="25" RadiusY="25"/>
<InkPresenter x:Name="inkP"
  Width="607" Height="408" Canvas.Left="25" Canvas.Top="34"
  MouseLeftButtonDown="inkP_MouseLeftButtonDown" 
  MouseLeftButtonUp="inkP_MouseLeftButtonUp" 
  MouseMove="inkP_MouseMove" 
  Background="Transparent"
  Clip="M0.5,25.5 C0.5,11.692881 11.692881,0.5 25.5,0.5 L581.5, 0.5 C595.30712,0.5 606.5,11.692881 606.5,25.5 L606.5, 382.5 C606.5, 396.30712 595.30712,407.5 581.5,407.5 L25.5,407.5 C11.692881, 407.5 0.5,396.30712 0.5,382.5 z" >
</InkPresenter>
Abbildung 8 Beschneiden der Ränder des InkPresenter
Abbildung 9 Diese willkürlich gezeichnete Form wurde zum Beschneiden des InkPresenter verwendet (zum Vergrößern auf das Bild klicken)

Erreichen des Silverlight-Look
Silverlight ermöglicht es Ihnen, mithilfe von Transparenz interessante visuelle Ebenen zu erstellen. Der InkPresenter kann auch das Aussehen einer halbtransparenten Oberfläche annehmen. Um diesen Effekt zu erzielen, sollten Sie dem Canvas zunächst ein Hintergrundbild hinzufügen. Ich habe den Hintergrund von der Website Silverlight.net verwendet (siehe silverlight.net). Sie können den Hintergrund ganz einfach festlegen, indem Sie das Bild auf den Canvas ziehen und für seine Stretch-Eigenschaft „Fill“ einstellen. Die z-Reihenfolge des Bilds muss unbedingt das erste Steuerelement sein, das in der Entwurfsoberfläche aufgelistet wird. Andernfalls wird es über dem Rechteck und dem InkPresenter positioniert und verdeckt beide.
Ändern Sie als Nächstes das Rechteck, um ihm einen schwarzen Hintergrund zu verleihen. Hierzu können Sie zum Beispiel den Füllbereichspinsel im Eigenschaftenfenster auswählen und für seine R-, G- und B-Werte 0 einstellen. Ändern Sie den Opacity-Wert des Rechtecks in 10 Prozent, um Halbtransparenz zu erzielen.
Es ist zwar möglich, die Hintergrundfarbe im InkPresenter und gleichzeitig Transparenz festzulegen, diese Transparenz wirkt sich jedoch ebenso auf die Freihandeingabe aus. Ich lasse den Hintergrund des InkPresenter am liebsten vollständig transparent und verwende ein anderes Steuerelement, um den Effekt zu erzielen. Interessant ist es, mit der Änderung des Alphawerts der Hintergrundfarbe des Steuerelements zu experimentieren und dies mit dem Effekt zu vergleichen, den die Änderung des Opacity-Werts des Steuerelements zur Folge hat. Sie können die Transparenz der Freihandeingabe direkt beeinflussen, wenn Sie die Alpha-Eigenschaft der Color- und OutlineColor-Eigenschaften des Zeichnungsattributs verwenden. Damit erzielen Sie genau die gleiche Wirkung wie mit der DrawingAttribute.Transparency-Eigenschaft im InkCanvas von WPF und im Tablet PC SDK. Abbildung 10 zeigt den InkPresenter kombiniert mit einem halbtransparenten Rechteck, wodurch ein hübscher visueller Effekt für den Zeichenhintergrund erzielt wird.
Abbildung 10 Erstellen eines halbtransparenten Hintergrunds

Hinzufügen eines Hintergrundbilds oder Videos im InkPresenter
Der halbtransparente Hintergrund ist für einige Szenarios eine sehr attraktive Lösung, es ist jedoch auch möglich, Bilder und sogar Videos als Hintergrund zu verwenden. Beim Erstellen eines solchen Hintergrunds wird die eigentliche Background-Eigenschaft des InkPresenter nicht geändert. Bilder oder Videos werden als untergeordnete Elemente des InkPresenter-Objekts hinzugefügt. Wenn das untergeordnete Element nicht über die Eigenschaften "Höhe", „Breite“, „Links“ oder „Oben“ verfügt, erbt es die Eigenschaften des übergeordneten InkPresenter.
Versuchen Sie es einmal: Fügen Sie der XAML-Entwurfsoberfläche mithilfe von Expression Blend ein Bildelement hinzu, und ziehen Sie dieses Bild dann in den InkPresenter. Sie können das XAML auch direkt hinzufügen:
<InkPresenter x:Name="inkP"
  Width="607" Height="426" Canvas.Left="25" Canvas.Top="34"
  MouseLeftButtonDown="inkP_MouseLeftButtonDown" 
  MouseLeftButtonUp="inkP_MouseLeftButtonUp" 
  MouseMove="inkP_MouseMove" 
  Background="Transparent">
  <Image Source="Assets/Leaves.jpg" Stretch="Fill" />
</InkPresenter>
Nun ist es möglich, direkt im Bild zu zeichnen. Sie könnten auch den Opacity-Wert des Bilds verringern, um es halbtransparent zu machen (siehe Abbildung 11).
Abbildung 11 Erstellen von Halbtransparenz mit der Opacity-Eigenschaft (zum Vergrößern auf das Bild klicken)
Ebenso einfach ist es, ein Video hinzuzufügen. Statt eines Bildes verwenden Sie ein MediaElement. Expression Blend behandelt Videos anders als Bilder. Sie können das Video zwar mit Drag & Drop aus dem Silverlight-Projekt auf die XAML-Entwurfsoberfläche ziehen, beim Ausführen des Projekts wird die Datei jedoch nicht gefunden. Stattdessen müssen Sie das Video innerhalb des Webhostprojekts platzieren. Dann muss das Quellattribut für das MediaElement auf die URL der Datei verweisen. Das MediaElement füllt nicht automatisch den InkPresenter aus, wie dies beim Bild der Fall war. Sie müssen die Größe manuell anpassen oder „Stretch="Fill"“ direkt in das XAML eingeben. Hier ein Beispiel für ein Video, das im Hintergrund eines InkPresenter wiedergegeben wird:
<InkPresenter x:Name="inkP" . . .   >
  <MediaElement Height="246" x:Name="Butterfly_wmv" Width="345"
    Source="http://localhost:52476/MSDNMagAnnotationClient_Web/Assets/Butterfly.wmv"
    Stretch="Fill"/>
</InkPresenter>
In der Silverlight-Dokumentation finden Sie weitere Informationen zur Verwendung von und Interaktion mit MediaElements.

Grundlagen des Strichentwurfs
Standardmäßig erstellen die Standardzeichnungsattribute des Strichs einen schwarzen Strich mit einer Höhe und Breite von 3. Der Wert repräsentiert DIPs (Device Independent Pixels, geräteunabhängige Pixel) und kann nicht auf einen Wert eingestellt werden, der kleiner ist als 2.
Im Code können Sie Methoden und Ereignishandler erstellen, die sich auf die verschiedenen Strichattribute auswirken, z. B. penWidth und penColor. So ist es z. B. möglich, den Wert einer Variablen namens „currentColor“ durch ein Click-Ereignis eines Steuerelements zu ändern und dann currentColor im inkP_MouseLeftButtonDown-Ereignis zu verwenden, wenn ein neuer Strich erstellt wird.
Um dies auszuprobieren, fügen Sie der Klasse eine Variablendeklaration hinzu, und legen Sie als Standardwert Schwarz fest:
System.Windows.Media.Color currentColor =        Colors.Black;
Im folgenden Beispiel wurde eine einzelne Methode erstellt, die als MouseLeftButtonDown-Ereignis für eine beliebige Anzahl farbiger Rechtecke verwendet werden kann. Die Methode bestimmt die Farbe des Rechtecks und verwendet sie dann als Wert für die currentColor-Variable:
private void ChangeColor(object sender, MouseButtonEventArgs e)
{
  Rectangle rec = (Rectangle)sender;
  SolidColorBrush scb = (SolidColorBrush)rec.Fill;
  currColor = scb.Color;
}
Fügen Sie in der inkP_MouseLeftButtonDown-Methode Code hinzu, um für die Zeichnungsattribute die currentColor-Variable festzulegen:
newStroke.DrawingAttributes.Color = currentColor;
Abschließend benötigen Sie noch eine Möglichkeit, die Änderung auszulösen. Fügen Sie zwei Rechtecke hinzu, wobei Sie für die Fill-Eigenschaft des einen Schwarz und des anderen Rot festlegen. Jedes Rechteck benötigt ein MouseLeftButtonDown-Ereignis, um die ChangeColor-Methode aufzurufen. Dieses XAML erstellt zwei Rectangle-Objekte, die als Kreise dargestellt werden:
<Rectangle MouseLeftButtonDown="ChangeColor" 
  Width="24" Height="22" Fill="#FF000000" 
  Stroke="#FF000000" RadiusX="25" RadiusY="25"
  Canvas.Left="-10" Canvas.Top="282"/>
<Rectangle MouseLeftButtonDown="RedInk"
  Width="24" Height="22"
  Fill="#FFCE0C0C" Stroke="#FF000000" 
  RadiusX="25" RadiusY="25"
  Canvas.Left="25" Canvas.Top="282"/>
Sie könnten auch Code schreiben, der die Strichsammlung analysiert, um die Farbe der vorhandenen Striche zu ändern.
Eines der Standardzeichnungsattribute für das Stroke-Objekt ist OutlineColor. Wenn Sie vorhaben, auf einem mehrfarbigen Hintergrund zu zeichnen (z. B. einem Bild), ist es hilfreich, eine einheitliche Konturfarbe für die Freihandeingabe festzulegen. Sie können dem inkP_MouseLeftButtonDown-Ereignis Code hinzufügen, um die OutlineColor von newStroke festzulegen:
newStroke.DrawingAttributes.OutlineColor = 
  Colors.White;
Abbildung 12 veranschaulicht das Konzept.
Abbildung 12 Freihandeingabe mit Kontur (zum Vergrößern auf das Bild klicken)

Handschrifterkennung
Zu den interessantesten Features von Freihandeingaben in Anwendungen gehört die Handschrifterkennung. Dieses Feature war zwar von Anfang Bestandteil des Tablet PC SDK und steht in WPF zur Verfügung, in Silverlight ist es jedoch nicht enthalten. Sie können jedoch Handschrifterkennung in Ihren Silverlight-Anwendungen verwenden, indem Sie die Strichdaten an einen ASP.NET-Webdienst oder einen WCF-Dienst senden. Diese führen dann die Erkennung durch und geben die Ergebnisse zurück.
Microsoft Research verwendete Handschriftbeispiele von mehr als einer Million Personen, um die Algorithmen für die Handschrifterkennung zu erstellen. Vielleicht überrascht es Sie, dass die Erkennung bei Schreibschrift besser funktioniert als bei Druckbuchstaben. Es gibt Handschrifterkennungsmodule für viele Sprachen einschließlich verschiedener asiatischer Sprachen.
Das Modul analysiert die Strichsammlung. Es kann feststellen, ob die Striche ein Wort, einen Satz, einen Absatz oder eine Zeichnung repräsentieren. Das Erkennungsmodul ist in der Lage, einzelne Wörter innerhalb eines Strichsatzes zu identifizieren, der aus mehreren Wörtern besteht, und dann das Wort als eine Einheit zu erkennen. Anschließend gibt es unter Verwendung seiner Beispiele und Algorithmen eine Auswahl an Möglichkeiten für dieses Wort zurück. Wenn Sie eine Gruppe von Wörtern (z. B. einen Satz) senden, gibt es eine Gruppe von Wörtern sowie eine Reihe von Alternativen zurück. In der Vergangenheit war die Erkennung auf Tablet PCs beschränkt. Heute kann sie mit jeder Kombination aus Computer und Browser durchgeführt werden, die Silverlight unterstützt!

Die InkAnalyzer-Klasse
System.Windows.Ink.InkAnalyzer führt die Handschrifterkennung durch. Es ist erstaunlich einfach, diese Klasse zu verwenden. Sie übergeben einfach eine Sammlung von Strichen an ein InkAnalyzer-Objekt, rufen seine Analyze-Methode auf und bestimmen mithilfe einer booleschen Eigenschaft namens „Successful“, ob die Analyse erfolgreich war. Wenn Successful wahr ist, gibt die GetRecognizedString-Methode die am besten passende Variante zurück, während die GetAlternates-Methode ein Array von alternativen Zeichenfolgen zurückgibt.
Obwohl die InkAnalyzer-Klasse nicht Teil der Silverlight-API ist, können Sie mithilfe von Web- oder WCF-Diensten die Handschrifterkennung in Ihren Silverlight-Anwendungen ausführen. Der Webserver kann die WPF-APIs hosten und die Funktionalität zum Durchführen der Erkennung bereitstellen. Dies erfordert allerdings einige Konvertierungen.
Zuerst müssen Sie auf die APIs in Abbildung 13 verweisen, und zwar in jeder Komponente, die Handschrifterkennung durchführt. Die ersten beiden APIs sind über die Schnittstelle „Verweise hinzufügen“ in Visual Studio direkt verfügbar. Die letzten beiden werden mit dem Tablet PC SDK installiert und befinden sich unter „Programme\Reference Assemblies\Microsoft\Tablet PC\v1.7“. Achten Sie beim Codieren auf mehrdeutige Verweise zwischen den IACore- und IAWinFX-Namespaces. Abschließend müssen Sie außerdem auf die Datei „IALoader.dll“ verweisen, die mit dem Tablet PC SDK installiert wird. Sie finden diese Datei unter „C:\Programme\Microsoft SDKs\Windows\v6.0\Bin“, wenn Sie Windows Vista ausführen.
API Function
PresentationCore.dll Enthält die System.Windows.Ink-APIs.
WindowsBase.dll Enthält Funktionen, die von Sammlungen verwendet werden.
IAWinFX.dll Fügt System.Windows.Ink die InkAnalyzer-Klasse und Funktionalität hinzu.
IACore.dll Stellt die InkAnalyzerBase-Klasse und Funktionalität über System.Windows.Ink.AnalysisCore bereit.
Wenn die Striche vom InkPresenter an einen Dienst übertragen werden sollen, müssen sie serialisiert werden. Diese Objekte sind jedoch nicht serialisierbar. Daher müssen Sie eine zeichenfolgenbasierte XAML-Darstellung der Strichsammlung erstellen. Die Zeichenfolge kann dann serialisiert und an den Dienst gesendet werden.
In Silverlight 1.0 war es erforderlich, Javascript zu verwenden, um diese Zeichenfolgendarstellung zu erstellen. In Silverlight 2 können Sie LINQ to XML verwenden. (Wenn Sie mit Visual Basic arbeiten, profitieren Sie außerdem vom Vorteil der XML-Literale.)
Der in Abbildung 14 gezeigte Code analysiert das Strichsammlungsobjekt, liest die Zeichnungsattribute, die Stiftpunkte und die dazugehörigen Details und verwendet LINQ to XML zum Erstellen der XAML-Darstellung. Dieser Code wird für zahlreiche Zwecke verwendet, einschließlich der Handschrifterkennung, obwohl die Zeichnungsattribute von den Erkennungsfunktionen ignoriert werden. Wenn Sie diese Methode ausschließlich für Erkennungzwecke erstellen, können Sie den Code, der die Zeichnungsattribute erfasst, weglassen.
public XElement StrokestoXAML(StrokeCollection mystrokes)
{
  //this method uses LINQ to XML
  //be sure to add the namespace to each element in order to load back
  //into a new StrokeCollection later with the XAMLReader

  string xmlnsString = "http://schemas.microsoft.com/client/2007";

  XNamespace xmlns = xmlnsString;
  XElement XMLStrokes = new XElement(xmlns + "StrokeCollection",
      new XAttribute("xmlns", xmlnsString));

  //create stroke, then add to collection element      
  XElement mystroke;
  foreach (Stroke s in mystrokes)
  {
    mystroke = new XElement(xmlns + "Stroke",
      new XElement(xmlns + "Stroke.DrawingAttributes",
        new XElement(xmlns + "DrawingAttributes",
           new XAttribute("Color", s.DrawingAttributes.Color),
           new XAttribute("OutlineColor",
                          s.DrawingAttributes.OutlineColor),
           new XAttribute("Width", s.DrawingAttributes.Width),
           new XAttribute("Height", s.DrawingAttributes.Height))));

    //create points separately then add to mystroke XElement
    XElement myPoints = new XElement(xmlns + "Stroke.StylusPoints");
    foreach (StylusPoint sp in s.StylusPoints)
    {
      XElement mypoint = new XElement(xmlns + "StylusPoint",
        new XAttribute("X", sp.X.ToString()),
        new XAttribute("Y", sp.Y.ToString()));
      //add the new point to the points collection of the stroke
      myPoints.Add(mypoint);
    }
    //add the new points collection to the stroke
    mystroke.Add(myPoints);
    //add the stroke to the collection
    XMLStrokes.Add(mystroke);
  }
  return XMLStrokes;
}
Beachten Sie die Verwendung des Namespace beim Erstellen des XAML. Der XMLReader in Silverlight 2 erfordert, dass dieser Namespace im Stamm eines XAML-Elements verwendet wird, um das XAML zu einem späteren Zeitpunkt in ein Objekt zu laden.

Analyse auf der Serverseite
Jetzt kann die Zeichenfolge als Parameter an einen Dienstvorgang übergeben werden, der die Ergebnisse als Zeichenfolge zurückgibt. Die vom Vorgang verwendete Methode muss eine Strichsammlung aus der XAML-Zeichenfolge neu erstellen. Dann kann die Strichsammlung an den InkAnalyzer gesendet werden. Auf dem Server können Sie nicht mehr auf die Silverlight-APIs zugreifen. Stattdessen verwenden Sie die WPF-APIs.
In WPF steht zwar auch eine XAMLReader.Load-Methode zur Verfügung, die Strichsammlung in WPF unterscheidet sich jedoch geringfügig von der Strichsammlung in Silverlight. Deshalb wird das Schema nicht erkannt und XAMLReader.Load schlägt fehl. Sie können die XAML-Zeichenfolge stattdessen mithilfe von LINQ to XML problemlos analysieren, die Daten aus einzelnen Stroke-Elementen auslesen und dann neue Stroke-Objekte in WPF erstellen.
Die Unterschiede zwischen der Strichsammlung in WPF und Silverlight sind minimal. So fehlt z. B. den Stroke.DrawingAtrributes in WPF die Konturfarbe, und die Stiftpunkte in einem WPF-Strich basieren auf einem geräteunabhängigen Koordinatensystem anstatt auf den Pixelwerten, die in Silverlight verwendet werden. Trotz dieser Unterschiede befinden sich die Stroke-, Strichsammlungs- und StylusPoint-Klassen in Silverlight und WPF in den gleichen Namespaces.
Die CreateWPFStrokeCollectionfromXAML-Methode in Abbildung 15 verwendet LINQ to XML, um eine Sammlung von Stroke-Elementen zu erstellen, und durchläuft dann diese Elemente, um für jedes ein neues Stroke-Objekt zu erstellen. Die Methode verwendet LINQ erneut, um eine Sammlung der Stiftpunkte zu erstellen, und erstellt dann die StylusPointsCollection für jeden Strich. Beachten Sie, dass es hierbei nicht zu einer vollständigen Neuerstellung der Strichsammlung kommt, da Sie keine Zeichnungsattribute benötigen (z. B. Farbe), um die Erkennung durchzuführen. Diese beiden Methoden stützen sich auf die PresentationCore- und WindowsBase-APIs, die bereits erläutert wurden. Nach dem Erstellen der Strichsammlung kann diese an eine Methode übergeben werden, die die Analyse durchführt.
private StrokeCollection CreateWPFStrokeCollectionfromXAML
  (string XAMLStrokes)
{
  //because namespace was used to create this
  // (for Silverlight to reuse the XAML),
  //you need to insert the namesace into the Descendent's parameter

  var xmlElem = XElement.Parse(XAMLStrokes);
  XNamespace xmlns = xmlElem.GetDefaultNamespace();
  StrokeCollection objStrokes = new StrokeCollection();
  //Query the XAML to extract the Strokes
  var strokes = from s in xmlElem.Descendants(xmlns+ "Stroke") select s;
  foreach (XElement strokeNodeElement in strokes)
  {
    //query the stroke to extract its StylusPoints
    var points = from p 
      in strokeNodeElement.Descendants(xmlns + "StylusPoint") select p;
    //create Stylus points collection from point element values
    StylusPointCollection pointData =
      new System.Windows.Input.StylusPointCollection();
    foreach (XElement pointElement in points)
    {
      double Xpoint = Convert.ToDouble(pointElement.Attribute("X").Value);
      double Ypoint = Convert.ToDouble(pointElement.Attribute("Y").Value);
      pointData.Add(new StylusPoint(Xpoint, Ypoint));
    }
    //create a new Stroke from the StylusPointCollection
    System.Windows.Ink.Stroke newstroke = new
       System.Windows.Ink.Stroke(pointData);
    //add the new stroke to the StrokeCollection
    objStrokes.Add(newstroke);
  }
  return objStrokes;
}
Die Methode in Abbildung 16 kann in einem WCF- oder ASMX-Webdienst verwendet werden, sodass eine Strichsammlung von einem InkPresenter akzeptiert und eine Zeichenfolge zurückgegeben wird, die die am besten passende Variante repräsentiert. Diese Funktionalität beruht auf den IAWinFX- und IACore-Assemblys.
public string RecognizeStrokes(string XAMLStrokes)
{ 
  try
  {
    //custom method to create WPF StrokeCollection from the string-based XAML
    var strokeColl = CreateWPFStrokeCollectionfromXAML(XAMLStrokes);
    var IA = new System.Windows.Ink.InkAnalyzer();
    IA.AddStrokes(strokeColl);
    var status = IA.Analyze();
    if (status.Successful)
      return IA.GetRecognizedString();
    else
      return "Not Recognized";
  }
  catch (Exception ex)
  {
    //trap and display errors at design time, not in production code  
    return "error:" + ex.Message;
  }
}

Verknüpfen des Diensts mit dem Silverlight-Client
Wenn die RecognizeStrokes-Methode in einen ASMX- oder WCF-Dienst eingebunden ist, können Sie problemlos die Webdienstmethode von der Silverlight-Anwendung aus aufrufen. Ich habe einen WCF-Dienst verwendet, um die Handschrifterkennung in meiner Projektmappe zu ermöglichen. Wenn Sie Hilfe bei der Arbeit mit WCF benötigen, finden Sie eine einfache QuickStart-Anleitung auf der Website Silverlight.net. Hier erfahren Sie, wie Sie einen WCF-Dienst erstellen, auf den Silverlight zugreifen kann.
In Windows-basierten Anwendungen ist es durchaus üblich, die Erkennung dynamisch während der Freihandeingabe durchzuführen. In einer verteilten Anwendung hingegen ist es sinnvoller, es dem Benutzer zu überlassen, die Erkennung explizit anzufordern. Daher benötigen Sie auf der Entwurfsoberfläche ein Steuerelement, um den Prozess zu starten. Erstellen Sie ein Steuerelement in XAML, z. B. eine Schaltfläche. Sie benötigen einen Ereignishandler für das Click-Ereignis der Schaltfläche, das die GetXAMLfromStrokes-Methode auslöst. Das sich ergebende XAML wird anschließend zur Erkennung an einen Webdienst gesendet. In meinem Webdienst trägt der Vorgang den Namen Recognize. Außerdem benötigen Sie ein TextBlock-Steuerelement zur Anzeige der zurückgegebenen Zeichenfolge. Dieses Steuerelement habe ich RecoText genannt.
Beim Hinzufügen von WCF-Dienstverweisen zu einer Silverlight-Anwendung implementiert der Proxy nur asynchrone Aufrufe für die Dienstvorgänge. Deshalb benötigen Sie eine Methode zum Behandeln von RecognizeCompleted (siehe Abbildung 17). Wenn die Anmerkung mehrere Zeilen umfasst, wird sie mit harten Zeilenumbrüchen erkannt, deshalb habe ich für RecoText statt eines TextBox-Elements ein ScrollViewer-Steuerelement verwendet (siehe Abbildung 18).
private void RecognizeButtonHandler(object sender, RoutedEventArgs e)
{
  StrokeCollection sc = inkP.Strokes;
  XElement sXML = StrokestoXAML(sc,true);
  string ss = sXML.ToString();
  GetRecoString(ss);

  //the next two lines are to test the validity of XAML created
  //(this would make a great unit test for this application)
  //StrokeCollection sc2 = new StrokeCollection();
  //sc2 = (StrokeCollection)System.Windows.Markup.XamlReader.Load(ss);
}

private void GetRecoString(string inkStrokes)
{
  Binding binding = new BasicHttpBinding();
  EndpointAddress endpoint = new
    EndpointAddress("http://myserver/SilverlightInkService.svc");
  var svc = new ServiceReference1.SilverlightInkServiceClient(binding,
    endpoint);
  svc.RecognizeCompleted += new
    EventHandler<ServiceReference1.RecognizeCompletedEventArgs>
    (svc_RecognizeCompleted);
  svc.RecognizeAsync(inkStrokes);
}

private void svc_RecognizeCompleted(object sender, 
  ServiceReference1.RecognizeCompletedEventArgs e)
{
  RecoText.Text = e.Result.ToString();
}
Abbildung 18 Verwenden eines ScrollViewer-Steuerelements für erkannten Text
Um die Testerfahrung zu verbessern, könnten Sie noch eine Schaltfläche auf der XAML-Seite hinzufügen, mit der die Freihandeingabe und der erkannte Text gelöscht werden, damit Sie mit verschiedenen Anmerkungen experimentieren können. Wie der Name schon sagt, entfernt InkPresenter.Strokes.Clear die Striche aus dem InkPresenter.

Persistente Speicherung von Anmerkungen mithilfe von Diensten
Die persistente Speicherung von Anmerkungen ist eine weitere wichtige Verwendungsweise der Webdienste in dieser Projektmappe. Ganz gleich, ob es sich um handschriftlichen Text, eine Zeichnung oder ein anderes Markup handelt – in den meisten Fällen soll die Strichsammlung in einer Datenbank oder einem Datenspeicher anderen Typs aufbewahrt werden. Es ist auch möglich, die Anmerkungsinformationen mithilfe von IsolatedFileStorage (in Silverlight verfügbar) auf dem Computer des Benutzers zu speichern. In diesem Artikel werde ich mich jedoch auf serverseitige Persistenz konzentrieren.
In den meisten Fällen speichern Sie die gesamte Strichsammlung, die jederzeit einem anderen InkPresenter hinzugefügt werden kann. Wie bereits erwähnt, besteht die einfachste Methode darin, die XAML-Zeichenfolgendarstellung mithilfe der GetXAMLfromStrokes-Methode zu erstellen, da das Strichsammlungsobjekt serialisiert werden muss.
Wenn die XAML-Zeichenfolge vorliegt, verläuft der restliche Teil der Aufgabe genauso wie bei der Speicherung anderer Texte in einer Datenbank. Beachten Sie, dass das XAML im Fall von Zeichnungen ziemlich umfangreich werden könnte. Diese Möglichkeit müssen Sie einkalkulieren, wenn Sie das Datenbankfeld definieren, in dem das XAML und die Client- und Diensteinstellungen gespeichert werden, um die Übertragung großer Datenmengen zu unterstützen.
In WCF könnten Sie auch JSON-Serialisierung (JavaScript Object Notation) verwenden, ein Format mit größerer Komprimierung als serialisierte XML-Daten. Es ist möglich, den XML-Datentyp in SQL Server® 2005 und höher zu nutzen. Allerdings sollten SQL Server-Typen wie nvarchar oder gar nvarchar(MAX) für voraussichtlich große Zeichnungen ausreichen, es sei denn, Sie planen, die Vorteile des XML-Datentyps zu nutzen (der abgefragt und indiziert werden kann, Schemaunterstützung bietet und Datenänderungen ermöglicht).
In der Beispielanwendung können Benutzer aus einer Reihe verschiedener Bilder wählen. Die Anmerkungen für jedes Bild werden in einer Datenbank mit dem eindeutigen Dateinamen des Bilds gespeichert. Wenn ein Bild ausgewählt ist, kann seine Anmerkung aus der Datenbank abgerufen und angezeigt werden.

Speichern und Abrufen von Anmerkungen
Im Fall der Erkennung wurde nur eine einzelne Zeichenfolge an den Dienst und vom Dienst übergeben. Beim Speichern und Abrufen der Daten übergeben Sie jedoch das XAML und möglicherweise andere Metadaten in Bezug auf die Anmerkung, z. B. das Erstellungsdatum, den Benutzer oder Verweise auf ein Bild oder Video, zu dem die Anmerkung gehört. WCF verwaltet diese verschiedenen Komponenten einer Nachricht mit DataContracts. Mithilfe einer Beispielanmerkung, die zu einer Bilddatei gehört, erstelle ich einen Vorgang, der den Pfad eines Bilds zusammen mit dem XAML speichern kann, und rufe dann das XAML für einen angegebenen Dateipfad ab.
Dieser WCF-Dienst verwendet eine Kombination aus Schnittstellen und Attributen, um den Vorgang und die Datenverträge zu definieren. In Abbildung 19 sehen Sie, dass drei separate Dienstvorgänge definiert werden: StoreImageXAML, RetrieveImageXAML und Recognize. StoreImageXAML akzeptiert einen Parameter des Typs ImageXAMLComposite, der von der ImageXAMLComposite-Klasse als DataContract definiert wird. Die beiden anderen Vorgänge sind viel weniger komplex, da sie nur eine einzelne Zeichenfolge akzeptieren und zurückgeben. Die Dienstklasse implementiert diese drei Methoden, die Hilfsmethoden für die Erkennung und Datenbankinteraktion aufrufen. Die Methoden, die mit der Datenbank arbeiten, verwenden LINQ to SQL (siehe Abbildung 20).
namespace SilverlightInkWCFService
{
  [ServiceContract]
  public interface ISLInk
  {
    [OperationContract]
    void StoreImageXAML(ImageXAMLComposite value);

    [OperationContract]
    string RetrieveImageXAML(string imageName);

    [OperationContract]
    string Recognize(string XAMLString);
  }

  [DataContract]
  public class ImageXAMLComposite
  {
    string imagePath;
    string xamlString;

    [DataMember]
    public string XAMLString
    {
      get { return xamlString; }
      set { xamlString = value; }
    }

    [DataMember] 
    public string ImagePath
    {
      get { return ImagePath; }
      set { ImagePath = value; }
    }
  }
}
public void StoreImageXAML(ImageXAMLComposite value)
{
  //ExistingRows, InsertRow and updateRow use LINQ to the SQL 
  //SubmitChanges
  if (ExistingRows(value.ImagePath) == true)
    insertRow(value.ImagePath, value.XAMLString);
  else
    updateRow(value.ImagePath, value.XAMLString);
}

public ImageXAMLComposite RetrieveImageXAML(string imagePath)
{
  //getXAML method performs a LINQ to SQL query 
  return getXAML(imagePath);
}

public string Recognize(string XAMLString)
{
  return RecognizeStrokes(XAMLString);
}

Letzte Schritte
Im vorliegenden Artikel wurden alle wichtigen Schritte für das Erstellen und die Interaktion mit einem InkPresenter, die Durchführung der Handschrifterkennung sowie die Speicherung und das Abrufen der XAML-Darstellung der Anmerkungen über Dienste behandelt. Wenn Sie weder über einen Tablet PC verfügen noch Windows Vista ausführen, können Sie die Anwendung trotzdem verwenden, sogar mit einer Maus, allerdings mit Abstrichen bei der Auflösung.
Eine weitere interessante Möglichkeit besteht darin, Videos zu verwenden. Obgleich Sie Silverlight-Animationen und Trigger zur Wiedergabe von Strichen verwenden können, ist die Koordination des Timings mit einem Video eine Herausforderung, macht jedoch Spaß.
Sie können Silverlight, das SDK, Silverlight Tools Beta 2 für Visual Studio 2008 und Expression Blend 2.5 June 2008 Preview von folgender Website herunterladen: go.microsoft.com/fwlink/?LinkId=122132.
Besonderer Dank gilt Stefan Wick von Microsoft, der Unterstützung in den Notebook, Tablet PC und UMPC Development MSDN®-Foren anbietet. Seine Beratung war für diesen Artikel sehr wichtig.


Julia Lerman ist .NET-Beraterin und entwickelt seit mehr als 20 Jahren Software. Sie ist in der .NET-Community als Referentin auf Konferenzen, Autorin, Microsoft .NET MVP und Leiterin der .NET-Benutzergruppe bekannt. Ihr demnächst veröffentlichtes Buch trägt den Titel Programming Entity Framework. Julia bloggt auf thedatafarm.com/blog.

Page view tracker