Copy & Paste mit Silverlight 4

In den Eingabefeldern einer Silverlight Anwendung lässt sich Copy and Paste ganz einfach über die bekannten Tastenkombinationen verwenden.

Tastenkombination Bedeutung
Strg+A Gesamten Text markieren
Strg+X Markierten Text ausschneiden
Strg+C Markierten Text kopieren
Strg+V Text einfügen

Seit Silverlight 4 besteht nun zusätzlich die Möglichkeit mittels statischer Methoden der statischen Klasse Clipboard im Namesraum System.Windows in Silverlight-Anwendungen auch programmatisch auf die Zwischenablage zuzugreifen.

Da dies eine sicherheitskritische Aktion ist, erhält der Benutzer einen entsprechenden Hinweis darauf und muss entscheiden ob er das zulassen möchte oder nicht.

Entscheidet sich ein Benutzer gegen den Zugriff auf die Zwischenablage, wird eine SecurityException (Namensraum System.Security) geworfen – diese sollte man in seinem Code auch möglichst behandeln, damit die Anwendung nicht einfach beendet wird.

Beim Betätigen der Tastenkombinationen zum Einfügen, Ausschneiden oder Kopieren hat man dieses Problem nicht, denn diese Aktionen werden ja vom Benutzer initiiert und sind demnach in Ordnung. Beim programmatischen Zugriff hingegen, könnte ohne den obigen Hinweis an den Benutzer, ein böswilliges Programm z.B. Informationen aus den Eingabefeldern rauskopieren, ohne dass der Benutzer davon etwas mitbekommt. Aus diesem Grund sollte man sich genau überlegen, ob man in seiner Anwendung unbedingt per Programmcode auf die Zwischenablage zugreifen muss.

Hinzu kommt, dass bei den Vorgängen zum Umgang mit der Zwischenablage auch einiges an Zusatzarbeit auf den Entwickler zukommt – die Standard-Tastenkombinationen funktionieren hingegen ohne weiteres Zutun. In dem unten aufgeführten Quellcode habe ich es mir einfach gemacht und berücksichtige nur ein einziges Eingabefeld (tbName). Dazu habe ich drei Schalter angelegt, die jeweils den Text aus dem Eingabefeld ausschneiden oder kopieren bzw. in das Eingabefeld einfügen. Möchte man diese Aktionen für mehrere Eingabefelder verwenden ist natürlich zusätzliche Logik erforderlich.

Drucken mit Silverlight 4

In Silverlight 4 gibt es die Klasse PrintDocument. Damit ist es möglich in Silverlight auf sehr einfache Weise zu drucken. Zum Testen habe ich ein Image und einen Schalter zum Drucken des Image erstellt.

Nachfolgend der XAML-Code

Drückt der Benutzer den Schalter Drucken, soll das Image auf dem Drucker ausgegeben werden. Dazu wird in der Ereignisbehandlungsroutine für den Button ein neues PrintDocument erzeugt und für das PrintPage-Ereignis die Methode p_PrintPage registriert. Gestartet wird der Druck mit der Methode Print.

Jetzt möchte ich aber auch noch den Schalter mit ausdrucken. Sowohl das Bild als auch der Schalter befinden sich in einem StackPanel mit dem Namen LayoutRoot. Um nun das StackPanel komplett zu drucken, ändere ich die p_PrintPage Methode

Wenn man sieht wird nun anstelle des Images das StackPanel verwendet – das ist alles.
Lässt man die Anwendung laufen, wird das Bild und der Schalter ausgedruckt.

LinearGradientBrush – Lineare Farbverläufe mit XAML

Mit dem nachfolgenden XAML-Code wird ein einfacher diagonaler Farbverlauf – ausgehend von der oberen linken Ecke zur rechten unteren Ecke – gezeichnet. Dies ist das Standardverhalten eines LinearGradientBrush, wenn StartPoint und EndPoint nicht angegeben werden. In diesem Beispiel soll der Farbverlauf oben links mit der Farbe grün beginnen und unten rechts mit der Farbe rot beendet werden.

Die ausführlichere Schreibweise mit Angabe von StartPoint und EndPoint macht das Gleiche wie o.a. Beispielcode.


Entlang der Achse wird vom Punkt 0,0 zum Punkt 1,1 der Farbverlauf gezeichnet.

Während das Füllen eines Rechtecks mit einer bestimmten Farbe ganz einfach über Fill gelöst werden kann

<Rectangle Width=“200″ Height=“150″ Fill=“BlueViolet“ />

ist die Verwendung und Definition eines linearen Farbverlaufes doch etwas komplexer. Dazu wird innerhalb von <Rectangle.Fill> der lineare Farbverlauf definiert. Als erstes wird ein LinearGradientBrush – also ein Pinsel zum Zeichnen von linearen Farbverläufen – definiert. Über StartPoint und EndPoint wird dann festgelegt in welcher Richtung der Farbverlauf gezeichnet werden soll. Weiterhin ist anzugeben mit welcher Farbe begonnen und mit welcher beendet werden soll. Dies erfolgt über GradientStop. Es lassen sich auch durchaus mehr als zwei GradientStops verwenden. Ein einzelner GradientStop macht keinen Sinn, da dies einem normalen Füllen entsprechen würde. Welche Bedeutung Offset hat – dazu später mehr. An dieser Stelle reicht es lediglich zu wissen, dass die GradientStops erforderlich sind. Ich möchte mich jetzt erst mal auf StartPoint und EndPoint konzentrieren.

Einen Farbverlauf von links nach rechts erreicht man durch nachfolgenden Code


Hierbei wird lediglich der Endpunkt auf 1,0 (was der oberen rechten Ecke entspricht) geändert. Mit diesem Wissen bewaffnet ist es ein Einfaches einen Farbverlauf in einer gewünschten Richtung umzusetzen. Wenn sie sich die nachfolgende Grafik ansehen – was müssen sie als StartPoint und EndPoint angeben, um einen Farbverlauf von rechts oben nach links unten zu zeichnen?

Sofern sie als StartPoint 1,0 und EndPoint 0,1 angenommen haben, lagen sie richtig.


Als nächstes komme ich auf GradientStop zurück. Mit GradientStop wird über Color=“Green“ beispielsweise die zu verwendende Farbe grün eingestellt. Was hat es nun mit der Angabe Offset auf sich? Nun, mit dem Offset wird angegeben, ab welcher Stelle auf der Achse ein Farbwert gelten soll.

Für grün wurde der Offset 0.0 gewählt – das ist die Stelle mit dem türkisfarbenden Kreis und für rot der Offset 1.0 – dargestellt mit dem blauen Kreis. D.h. der Farbverlauf erfolgt auf der gesamten Achse von grün nach rot.

Jetzt möchte ich aber sicherstellen, dass nach 75% über die Achse, die Farbe rot gelten soll. Um dies umzusetzen, ändere ich den Offset für die Farbe rot auf 0.75. Wie unten zu sehen ist, verschiebt sich der blaue Kreis, der den Offset 0.75 kennzeichnet. An dieser Stelle gilt somit die Farbe rot.

Der Offset kann auch über den Endpunkt hinaus gesetzt werden. Damit ist die Farbe rot am Endpunkt 0,1 noch nicht erreicht.


Mit StartPoint und EndPoint lassen sich auch weitere Effekte erreichen und die Werte entsprechend der eigenen Wünsche angepasst werden.

Bereits weiter oben wurde schon angesprochen, dass sich auch mehrere GradientStops verwenden lassen. Im nächsten Beispiel möchte ich einen Farbverlauf mit mehreren Farben verwenden.


Damit erfolgt der lineare Farbverlauf von unten nach oben über die angegebenen Farben. Die Offsets legen fest, ab wo auf der Achse eine Farbe gelten soll.

Man sieht, dass man mit dem LinearGradientBrush auf sehr einfache Weise tolle Effekte hinbekommen kann. Neben den bisher gezeigten weichen Farbübergängen lassen sich auch direkte Farbübergänge realisieren. In dem nachfolgenden Beispiel wird eine deutsche Flagge über GradientStops definiert und von unten nach oben gezeichnet. Hierbei fällt auf, dass die Farbe rot zweimal als GradiantStop auftaucht. Dies ist notwendig, da die Farbe rot von Offset 0.33 bis Offset 0.66 gelten soll. Würde man das nicht so tun, wäre ein weicher Farbübergang das Ergebnis.



Mit SpreadMethod=“Repeat“ lassen sich Farbverläufe auch wiederholen.


Eine weitere Möglichkeit ist das Spiegeln von Farbverläufen. Dazu wird für SpreadMethod der Wert Reflect verwendet.


Man hat noch jede Menge weitere Möglichkeiten beim Erzeugen von linearen Farbverläufen. So lässt sich beispielsweise auch der Alpha-Kanal für Farben eines Farbverlaufs verwenden. Damit sind dann auch Farbverläufe mit Transparenz möglich.

Silverlight Canvas

Das Canvas-Element (Canvas=Leinwand) ermöglicht eine freie Anordnung von Elementen. Das bedeutet, dass die Elemente über entsprechende X- und Y-Koordinaten zu positionieren sind, da das Canvas über keinerlei Layoutlogik verfügt. Per Vorgabe hat ein Element auf einem Canvas die Position 0,0 im Elternelement.

In dem nachfolgenden Codeabschnitt wird ein Button innerhalb eines Canvas positioniert

<Grid x:Name=“MyGrid“ Width=“300″ Height=“100″ Background=“Azure“>
<
Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Button x:Name=“Btn1″ Content=“Klick mich an“></Button>
</
Canvas>
</
Grid>

Wie zu erkennen ist, wurden für den Button keine Angaben zur Position oder Größe angegeben. Damit erhält der Button die Position 0,0 während Höhe und Breite in Abhängigkeit vom Content (dem Text „Klick mich an“) eingerichtet wird. Dabei enthalten die Eigenschaften Width und Height den Wert NaN (Not a Number). Dies hat den Vorteil, dass bei Änderungen des Content eine Größenänderung automatisch passiert. Dazu aber später mehr.

Fügt man nun einen weiteren Button hinzu – auch diesmal ohne Angabe von Position und Größe – erkennt man, dass der neu hinzugefügte Schalter den anderen überlagert.

<Grid x:Name=“MyGrid“ Width=“300″ Height=“100″ Background=“Azure“>
<Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Button x:Name=“Btn1″ Content=“Klick mich an“></Button>
<
Button x:Name=“Btn2″ Content=“Button 2″ Foreground=“Chocolate“></Button>
</
Canvas>
</
Grid>


Das bedeutet also, dass Positionsangaben verwendet werden müssen, da ansonsten alle Elemente an Position 0,0 liegen und sich dadurch überlagern. Allerdings gibt es für den Button keine Eigenschaft Left oder Top – wie kann man nun die Position festlegen? Die Antwort: Im Canvas. Mittels Attached Properties (angehängte Eigenschaften) lässt sich die Position der Buttons festlegen.

<Button x:Name=“Btn2″ Canvas.Top=“27″ Content=“Button 2″ Foreground=“Chocolate“></Button>

Damit wird für den zweiten Button festgelegt, dass sich sein oberer Rand 27 Pixel unterhalb des Canvas-Außenrahmen befindet und somit nicht mehr den ersten Schalter überdeckt. Im nachfolgenden Beispiel wird mit Canvas.Left der linke Ursprung festgelegt und damit der zweite Schalter gegenüber dem ersten um 15 Pixel eingerückt.

<Button x:Name=“Btn2″ Canvas.Top=“27″ Canvas.Left=“15″ Content=“Button 2″ Foreground=“Chocolate“></Button>

Unter Umständen kann es aber auch beabsichtigt sein, dass Elemente durch andere Elemente überlagert werden sollen. Beispielsweise, wenn man einen Text auf einem Kreis wie im nachfolgenden Beispiel ausgeben möchte.

Mit dem nachfolgenden XAML-Code wird dies erreicht. Was hier auch auffällt ist, dass die Ellipse außerhalb des Canvas weiter gezeichnet wird.

<Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Button x:Name=“Btn1″ Canvas.Top=“2″ Content=“Klick mich an“></Button>
<
Button x:Name=“Btn2″ Canvas.Top=“27″ Canvas.Left=“15″ Content=“Button 2″ Foreground=“Chocolate“></Button>
<
Ellipse x:Name=“Ellipse“ Canvas.Top=“35″ Canvas.Left=“100″ Width=“50″ Height=“50″ Fill=“Black“></Ellipse>
<
TextBlock x:Name=“Tb1″ Canvas.Left=“112″ Canvas.Top=“50″ Foreground=“White“>Text</TextBlock>
</
Canvas>

In diesem Beispiel wird der Text über dem Kreis ausgegeben – so war es ja auch beabsichtigt. Durch die Angabe der Reihenfolge im XAML-Code wird auch die Reihenfolge bei der Ausgabe festgelegt. Dies gilt es auch zu beachten, denn wenn beispielsweise erst der Text und erst dann der Kreis in XAML definiert wird, überlagert der Kreis den Text und dieser ist damit auch nicht sichtbar.

Damit man nun nicht jedes Mal den kompletten XAML-Code komplett umschmeißen muss, wenn sich irgendwas ändert, kann man auch mittels Canvas.ZIndex die Reihenfolge ändern. Dies ist insbesondere dann sinnvoll, wenn programmatische Änderungen vorgenommen werden.

<TextBlock x:Name=“Tb1″ Canvas.Left=“112″ Canvas.Top=“50″ Foreground=“White“ Canvas.ZIndex=“2″>Text</TextBlock>
<
Ellipse x:Name=“Ellipse“ Canvas.Top=“35″ Canvas.Left=“100″ Width=“50″ Height=“50″ Canvas.ZIndex=“1″ Fill=“Black“></Ellipse>

Damit wird der Text wieder über der Ellipse positioniert und ist damit sichtbar.

Alle anderen Elemente besitzen den ZIndex 0, d.h. hier wird über die Reihenfolge der XAML-Definitionen die Darstellung der restlichen Elemente festgelegt.

Jetzt möchte ich noch mal kurz auf den schwarzen Kreis – oder besser gesagt Ellipse – zu sprechen kommen. Wie bereits weiter oben schon mal angemerkt, sieht man, dass über die Ränder des Canvas hinaus weiter gezeichnet wird. Das kann beabsichtigt sein. Was aber nun, wenn nicht über die Ränder des Canvas hinaus gezeichnet, sondern ein Element entsprechend beschnitten werden soll. Auch dies lässt sich mit einer kurzen Anweisung in XAML realisieren.

<Canvas.Clip>
<
RectangleGeometry Rect=“0,0,250,75″/>
</
Canvas.Clip>

Damit erreicht man, dass die Ellipse an den Rändern des Canvas abgeschnitten wird.

Dies betrifft aber nicht nur die Ellipse. Vielmehr werden alle Elemente entsprechend beschnitten, wenn über den Rand des Canvas hinaus gezeichnet wird.

<Grid x:Name=“MyGrid“ Width=“300″ Height=“100″ Background=“Azure“>
<
Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Canvas.Clip>
<
RectangleGeometry Rect=“0,0,250,75″/>
</
Canvas.Clip>
<
Button x:Name=“Btn1″ Canvas.Top=“-10″ Content=“Klick mich an“></Button>
<
Button x:Name=“Btn2″ Canvas.Top=“27″ Canvas.Left=“-15″ Content=“Button 2″ Foreground=“Chocolate“></Button>
<
TextBlock x:Name=“Tb1″ Canvas.Left=“225″ Canvas.Top=“64″ Foreground=“Black“ Canvas.ZIndex=“2″>Text</TextBlock>
<
Ellipse x:Name=“Ellipse“ Canvas.Top=“-10″ Canvas.Left=“210″ Width=“50″ Height=“50″ Canvas.ZIndex=“1″ Fill=“Black“></Ellipse>
</
Canvas>
</
Grid>

Möchte man beispielsweise erlauben, dass bis zu 5 Pixel über die Ränder gezeichnet werden darf, lässt sich das Clipping auch dementsprechend verändern.

<Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Canvas.Clip>
<
RectangleGeometry Rect=“-5,-5,260,85″/>
</
Canvas.Clip>
<
Button x:Name=“Btn1″ Canvas.Top=“-10″ Content=“Klick mich an“></Button>
<
Button x:Name=“Btn2″ Canvas.Top=“27″ Canvas.Left=“-15″ Content=“Button 2″ Foreground=“Chocolate“></Button>
<
TextBlock x:Name=“Tb1″ Canvas.Left=“225″ Canvas.Top=“64″ Foreground=“Black“ Canvas.ZIndex=“2″>Text</TextBlock>
<
Ellipse x:Name=“Ellipse“ Canvas.Top=“-10″ Canvas.Left=“210″ Width=“50″ Height=“50″ Canvas.ZIndex=“1″ Fill=“Black“></Ellipse>
</
Canvas>

Kommen wir nochmal auf die Eigenschaften Width und Height der Buttons zurück. Ziemlich am Anfang wurde bereits erklärt, dass diese Eigenschaften den Wert NaN enthalten, wenn keine Größenangabe gemacht wurde. Im nachfolgenden XAML-Code wird für den Schalter Btn1 eine Methode festgelegt, die beim Anklicken des Schalters aufgerufen werden soll.

<Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Canvas.Clip>
<
RectangleGeometry Rect=“-5,-5,260,85″/>
</
Canvas.Clip>
<
Button x:Name=“Btn1″ Canvas.Top=“5″ Content=“Klick mich an“ Canvas.Left=“6″ Click=“Btn1_Click“></Button>
<
Button x:Name=“Btn2″ Canvas.Top=“33″ Canvas.Left=“6″ Content=“Button 2″ Foreground=“Chocolate“></Button>
<
TextBlock x:Name=“Tb1″ Canvas.Left=“206″ Canvas.Top=“23″ Foreground=“Yellow“ Canvas.ZIndex=“2″>Text</TextBlock>
<
Ellipse x:Name=“Ellipse“ Canvas.Top=“5″ Canvas.Left=“194″ Width=“50″ Height=“50″ Canvas.ZIndex=“1″ Fill=“Black“></Ellipse>
</
Canvas>

Die Methode Btn1_Click ändert lediglich den Content des Schalters

private void Btn1_Click(object sender, RoutedEventArgs e)
{
Btn1.Content = „Du hast mich angeklickt!“;
}

Da für den Schalter keine Größenangaben getätigt wurden, ändert sich die Größe des Schalters automatisch nachdem darauf geklickt wurde.

Bei Angabe einer Größe für den Schalter bleibt diese nach Änderung des Contents erhalten. Verwendet man im XAML-Code beispielsweise eine Breite von 80 Pixeln wie in nachfolgendem XAML-Code, wird der programmatisch geänderte Text nicht vollständig angezeigt.

<Button x:Name=“Btn1″ Width=“80″ Canvas.Top=“5″ Content=“Klick mich an“ Canvas.Left=“6″ Click=“Btn1_Click“></Button>

Die automatische Anpassung der Größe hat Vor- und Nachteile. Da das Canvas keinerlei Layout-Logik umfasst, obliegt die Anordnung der Elemente dem Ersteller des XAML-Codes. Zudem können programmatische Änderungen zu unerwünschten Nebeneffekten führen. Zur Veranschaulichung habe ich die Elemente mittels des XAML-Codes ein wenig anders angeordnet.

<Canvas x:Name=“LayoutRoot“ Background=“Bisque“ Width=“250″ Height=“75″>
<
Canvas.Clip>
<
RectangleGeometry Rect=“-5,-5,260,85″/>
</
Canvas.Clip>
<
Button x:Name=“Btn1″ Canvas.Top=“5″ Content=“Klick mich an“ Canvas.Left=“6″ Click=“Btn1_Click“></Button>
<
Button x:Name=“Btn2″ Canvas.Top=“33″ Canvas.Left=“6″ Content=“Button 2″ Foreground=“Chocolate“></Button>
<
TextBlock x:Name=“Tb1″ Canvas.Left=“115″ Canvas.Top=“21″ Foreground=“Yellow“ Canvas.ZIndex=“2″>Text</TextBlock>
<
Ellipse x:Name=“Ellipse“ Canvas.Top=“5″ Canvas.Left=“102″ Width=“50″ Height=“50″ Canvas.ZIndex=“1″ Fill=“Black“></Ellipse>
</
Canvas>

Klickt der Anwender nun während der Ausführung auf den Schalter, wird dieser vergrößert und ein Teil davon wird von der Ellipse verdeckt. Die Ursache dafür liegt darin begründet, dass für die Ellipse und den Text mit Canvas.ZIndex die Reihenfolge festgelegt wurde, die anderen Elemente aber den ZIndex 0 haben und somit hinter der Ellipse und dem Text zum liegen kommen.

Damit der Schalter wieder drüber liegt, genügt es für diesen ebenfalls über Canvas.ZIndex eine Reihenfolge festzulegen.

<Button x:Name=“Btn1″ Canvas.Top=“5″ Content=“Klick mich an“ Canvas.Left=“6″ Click=“Btn1_Click“ Canvas.ZIndex=“3″></Button>

Sofern man die Reihenfolge der Elemente korrekt über den XAML-Code festlegt, ist die Verwendung von Canvas.ZIndex nicht erforderlich. Sobald aber programmatische Veränderungen ins Spiel kommen, ist die Verwendung von Canvas.ZIndex sehr hilfreich. Allerdings sollte man dann für alle Elemente den ZIndex festlegen um unerwünschte Nebeneffekte zu vermeiden.