Socket Server mit VB.NET

Um ein größeres Ziel zu verfolgen musste ich einen Socket Server schreiben. Tendenziell wird man bei Server an einen Lösungsansatz mit Service denken. Ich nehme aber eine einfach Winforms Anwendung, weil mein Server nur Sinn macht wenn ein Benutzer angemeldet ist. Im weitesten Sinn überwacht mein Socket Server ein Stück Hardware und meldet Daten in einer Art Event an eine Silverlight Anwendung. Die Implementierung erfolgt auf Alpha Prototyp Level. Also sicher noch erhebliches Refactoring Potential. Ich war wirklich faul und habe mich auch auf die vorhandenen Steuerelemente gestürzt und eben ein Timer Control (Timer1) auf das Formular gezogen. Der Socket Server lauscht auf Port 5406, weil dies im Bereich der von Silverlight nutzbaren Ports liegt. tcpL = New TcpListener(IPAddress.Parse("127.0.0.1"), 4506) tcpL.Start() tcpL.BeginAcceptSocket(AddressOf onConnect, Nothing) Der Listener und auch der Client (es reicht eine Verbindung) wird global in der Forms Klasse definiert. Dim tcpL As TcpListener Dim tcpClient As TcpClient Weil es später zur Laufzeit recht wenig Transparenz gibt, was das Programm gerade tut, schreibe ich in das Output Fenster von Visual Studio. Mit AcceptClient wird der Kanal geöffnet und gewartet bis sich ein Client meldet. Die eigentliche Kommunikation findet dann asynchron statt. Da ich die Daten die vom Silverlight Client gesendet werden nicht brauche, ist die Funktion der “ClientSpricht” leer. Dim iasy As IAsyncResult Private Sub onConnect(ByVal ia As IAsyncResult) Try tcpClient = tcpL.EndAcceptTcpClient(ia) iasy = tcpL.BeginAcceptTcpClient(AddressOf clientSpricht, Nothing) Trace.WriteLine("connected...") Catch ex As Exception Trace.WriteLine(ex.ToString()) End Try End Sub Viel wichtiger ist, was der Server zum Server sagt. Dazu wird regelmäßig ein Timer Event gefeuert, das folgenden Code ausführt und die zur Demonstration Sekunden sendet. If IsNothing(tcpClient) = False AndAlso tcpClient.Connected Then Dim networkStream As NetworkStream = tcpClient.GetStream Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytesDate.Now.Second.ToString) networkStream.Write(sendBytes, 0, sendBytes.Length) Trace.WriteLine("sending..." + Date.Now.Second.ToString) End If Wie kann man nun feststellen das dieser Server auch funktioniert? Ganz einfach mit Telnet4358. Zuerst wird das Kommando telnet localhost 4506 eingegeben und dann erscheinen die Zahlen wie im Bild ersichtlich regelmäßig.

ppedv Konferenzen

Ich versuche mein Blog Werbefrei zu halten. Insofern auch mehr zur Info, weil ich weis das viele Leser meines Blogs auch gerne mal auf Konferenzen sprechen. Also was ist am Plan SQLdays  Rosenheim 19.-20.Oktober http://www.sqldays.net/ Highlights: schaut euch mal die Sprecher an. Ist ein sehr kommunikativer Rahmen. Als ne echte Chance für die BI und SQL Spezialisten sich zu treffen. ADC Advanced Developer Conference Bonn 25.-26.Oktober http://www.adc10.de/ Die älteste Konferenz (seit gut 15 Jahren)  für professionelle Entwickler. Keine Einstieg oder Einführung Sessions, Level 300 und up. Cooles Sprecherboard! GUI&Design Nürnberg 8.-9. Dezember http://gui-design.ppedv.de Weg vom grauen Rechteck im oberen Bildschirmeck. Hin zu Anwendungen die die Benutzer lieben. Auf allen Geräten von kleinsten Windows Phone 7 bis zu Surface Formfaktor. Eben die Mischung von Usablity und Design die der Entwickler in Zukunft braucht. Call for papers ist offen. VSone und Sharepoint konferenz 16.-17.Februar 2011 München Neue bessere Location! Das Key Event der Branche mit 10 parallelen Tracks und allen Themen. Hier trifft sich die gesamte Community im Microsoft Eco System. Call for papers ist nicht eröffnet. www.vsone.de Geheim Projekt- coming soon 18.-19 Februar München…

Cross Domain und die drei tapferen Network Stacks

Folgende Zeichnung zeige ich immer in meinen Silverlight Kurs. (Tablet PC sei Dank) Damit soll der Silverlight Schulung Teilnehmer lernen, das es per Definition verboten ist, auf einen zweiten Webserver zuzugreifen (Stichwort crossdomain.xml und clientaccesspolicy.xml). Diese Grundlagen werde ich vielleicht wann anders beschreiben. Wer es nachschlagen möchte “Crossdomain”. Das folgende Szenario ist nicht ganz trivial. Läuft die Anwendung OOB ( Out of Browser) mit elevated Priviliges werden crossdomain limitierungen ignoriert. Wenn die Anwendung im Browser läuft wird erstens per default ein anderer Network Stack verwendet und es gibt eine Fehlermeldung. Außer der Web Server hat im Root Directory eine der beiden vorher erwähnten XML Dateien liegen. Das bedeutet das eine Silverlight Anwendung ,abhängig wo sie läuft, unterschiedlichen Code ausführt. Also folgenden Zeilen Code benutzen effektiv zwei unterschiedliche Network Stacks. Dim wc As New WebClient AddHandler wc.DownloadStringCompleted, AddressOf fertig wc.DownloadStringAsync(... Der eine Stack wird Network Stack genannt, weil er echte Netzwerk Funktion implementiert. Der andere Stack heißt Browser Stack weil er das XMLHTTP Objekt des Browser kapselt. Nun gibt es die Anforderung das ein solcher Cross Domain Zugriff (zb auf die Twitter API) durchgeführt werden soll aus dem Browser unter Ignoranz der crossdomain policys Das geht mit dem “dritten Netzwerk” Stack. Dem XMLHttp Objekt direkt z.B. aus JScript. Silverlight bietet dafür eine HTMLPage Helper Klasse mit der man ziemlich einfach direkt im Browser arbeiten kann. So lässt sich per eval beliebig dynamisch generierter JScript Code ausführen oder mit Invoke eine Jscript Funktion aufrufen. Noch besser ist aber das Silverlight ScriptObject. Damit lassen sich unter Semi Intellisense Unterstützung JScript Objekte instanzieren. Mit Invoke werden dann die Methoden aufgerufen. Ausgehend von folgendem Jscript Beispiel var w = new XMLHttpRequest(); w.onreadystatechange = function () { if (w.readyState == 4) { alert(w.responseText); }} w.open("GET", "http://...", true); w.send(null); Lässt sich so mit managed Code in VB.NET die Silverlight Anwendung ausprogrammieren. Dim url As String = "http://..." Dim req As ScriptObject req = HtmlPage.Window.CreateInstance("XMLHttpRequest") req.Invoke("open", "GET", url, False) req.Invoke("send") TextBlock1.Text = req.GetProperty("responseText") Ein erster Test mit Chrome und Firefox funktioniert ohne Probleme. Mit Internet Explorer 9 kommt eine Sicherheitsabfrage die der Benutzer bestätigen muss. Abgesehen von dieser kleinen Hürde kann so die Crossdomain Limitierung von Silverlight mit wenigen Code Zeilen umgangen werden. Darüber hinaus bietet der XMLHTTP JScript Network Stack auch mehr Funktionen, da der Silverlight Wrapper nur das nötigste implementiert hat.

aneinandergereiht: POCO nach XML oder JSON in Silverlight

Aktuell bin ich wieder mal in einer Silverlight Schulung. Obwohl ich das Thema wirklich von A-Z kenne, entdecke ich immer wieder Neues. Dieses mal im ausgezeichneten Silverlight 4 Buch von Thomas Claudius Huber (erschienen bei Galileo). Thomas macht auf Seite 833 etwas, was ich so nicht tun würde. Er nimmt einen DataContractJsonSerializer um einen JSon Rückgabe eines REST Services nach POCO zu wandeln. Interessante Idee, aber nicht im Sinne des Erfinders. Dafür waren wohl eher die Klassen JSoneArray oder JSonObject gedacht. Nicht desto trotz – einen Blick wert. Aber ich möchte erst mal die Basics aufgreifen. Ein Objekt, hier z.B. Person muss serialsiert werden um z.B. im Isolated Storage dauerhaft gespeichert zu werden. Zunächst einmal ein Prototyp der einfach das Objekt serialisiert und im UI ausgibt. Es müssen drei Namensräume eingebunden werden System.IO System.Runtime.Serialization System.Text Dann wird der klassische DataContractSerializer angeworfen der XML erzeugen wird. Am Ende wird das erzeugte Byte Array in UTF Encodiert um die Anzeige zu realisieren. Dim p As New person Dim ms As New MemoryStream() Dim ser As New DataContractSerializer(GetType(person)) ser.WriteObject(ms, p) Dim array() As Byte = ms.ToArray() ms.Close() TextBlock1.Text = Encoding.UTF8.GetString(array, 0, array.Length) Alternativ gibt es auch noch ein Klasse XMLSerializer mit den Methoden Serialize und Deserialize auf die ich hier aktuell nicht eingehen möchte. Will man Json erzeugen muss man einen anderen Serialisierer nehmen. Der wiederum findet sich im Namensraum System.Runtime.Serialization.Json. Dann muss nur eine Zeile getauscht werden. Dim ser As New DataContractJsonSerializer(GetType(person)) Deutlich zu erkennen ist, das die Datenmenge bei Json im Vergleich zu XML deutlich geringer ist. Aus diesem Grund sehe ich auch SOAP als noch viel schwer gewichtigeres XML Format sehr kritisch und sehe die Zukunft eher in REST basierten Ansätzen wie das moderne ODATA. Wie kommen nun die Daten wieder zurück? Der sehr gut verkürzte Code. Dim p As New person Dim ser As New DataContractJsonSerializer(GetType(person)) Dim ms As New MemoryStream(Encoding.UTF8.GetBytes(TextBlock1.Text)) p = ser.ReadObject(ms) ms.Close() Im Grund nichts Neues. Das gibts in .NET schon eine ganze Weile (seit 3.5).

Asynchrone Validierung von Benutzereingaben mit INotifydataErrorInfo

Manchmal ist die Wahl wirklich die Qual. In Silverlight 4 gibt es mindestens vier mir bekannte Möglichkeiten in Dialogen Eingaben des Benutzers zu prüfen. per Exception (von Anbegin) per Eigenschaftsattribut (mit RIA Services eingeführt) IDataErrorInfo INotifyDataErrorInfo (neu in SL4) Mit dem letzteren werden wir uns hier nun beschäftigen. Wenn eine Datenklasse das Interface INotifyDataerrorInfo implementiert kann von andere Stelle im Code ein Fehler Event aufgerufen werden. Im Unterschied zu IDataErrorInfo kann dann ein Property auch mehrere Fehler besitzen (ob es sich darüber freuen wird?) was den Code nicht einfacher macht. Darüber hinaus kann man direkt das ErrorsChanged Event aufrufen und so auch asynchron validieren. Mein folgendes Silverlight Beispiel ist Teil einer kleinen APP die die UST ID auf Gültigkeit beim zuständigen Finanzamt prüft. Das heißt es werden Daten an einen Webservice geschickt und dieser antwortet für jedes Feld ob dies gültig oder ungültig ist. Der Benutzer muss dann die Eingabe korrigieren oder seinem Geschäftspartner mitteilen das eine UST freie Lieferung nicht möglich ist. Dazu erstellte ich mir einen Datenklasse die später dann auch an das UI per Binding gebunden wird. Die Attribute dienen ebenfalls der Eingabe validierung bzw dem Layout des Dialoges. Dort kommen Label Steuerelemente zum Einsatz die dann z.B. Display Name verwenden. Public Class ustidFirma Implements INotifyDataErrorInfo <Required()> <Display(Name:="Ustid")> Public Property ustid As String <Required()> <Display(Name:="Firma")> Public Property firma As String Wesentlicher ist aber die Logik. Eine exemplarische Implementierung nachdem das Interface InotifyDataErrorInfo implementiert wird. Es werden zwei Methoden benötigt. Die Eigenschaft HasErrors definiert ob grundlegend Fehler vorhanden sind. Die Funktion GetErrors gibt eine Liste der Fehler zurück die für einen bestimmte Eigenschaft, z.B. ustid, vorliegen. Das ErrorsChanged Event dient dazu die UI über Änderungen zu informieren. Imports System.ComponentModel Public Class test1 Implements INotifyDataErrorInfo Public Event ErrorsChanged(ByVal sender As Object, ByVal e As System.ComponentModel.DataErrorsChangedEventArgs) Implements System.ComponentModel.INotifyDataErrorInfo.ErrorsChanged Public Function GetErrors(ByVal propertyName As String) As System.Collections.IEnumerable Implements System.ComponentModel.INotifyDataErrorInfo.GetErrors End Function Public ReadOnly Property HasErrors As Boolean Implements System.ComponentModel.INotifyDataErrorInfo.HasErrors Get End Get End Property End Class Zurück zu meinem Beispiel UST Prüfung. Um die Fehler zu verwalten erstelle ich mir eine Liste für die Attribute und deren Fehler. Private errors As New Dictionary(Of String, List(Of String)) HasErrors liefert dann zurück ob in der Liste was drin steht. Public ReadOnly Property HasErrors As Boolean Implements System.ComponentModel.INotifyDataErrorInfo.HasErrors Get Return errors.Count > 0 End Get End Property Die Funktion GetErrors, liefert die Fehlerliste für das ausgewählte Property. Public Function GetErrors(ByVal propertyName As String) As System.Collections.IEnumerable Implements System.ComponentModel.INotifyDataErrorInfo.GetErrors If errors.ContainsKey(propertyName) Then Return errors(propertyName) Else Return Nothing End If End Function   Eine manuell von mir erstellte Hilfsfunktion RaisErrorsChanged füllt die Fehlerliste und wirft dann das Event um im  Userinterface die Bindung zu aktualisieren. Public Sub RaiseErrorsChanged(ByVal propertyName As String, ByVal Fehler As String) If Not errors.ContainsKey(propertyName) Then errors(propertyName) = New List(Of String)() errors(propertyName).Add(Fehler) RaiseEvent ErrorsChanged(Me, New DataErrorsChangedEventArgs(propertyName)) End Sub Diese Sub wird von mir sozusagen per Hand aufgerufen. Die dazu gehörige Logik blende ich hier aus. Stellen Sie sich einfach einen Button vor der folgenden Code enthält. Dim ui As ustidFirma = LayoutRoot.DataContext ui.RaiseErrorsChanged("ustid", "USTID falsch") Da es sich um eine gebundenes Objekt handelt kann ich jederzeit dieses Objekt zurückholen und darauf dann die Methode RaiseErrorsChanged aufrufen. Im XAML Code sind dafür mehrere Dinge nötig. Der Namensraum (hier willkürlich local genannt) um auf die Projektklassen zugreifen zu können. Die deklarative Instanz mit dem frei gewählten Namen uf1.  Das Grid mit dem Namen Layoutroot ( Silverligth Default) bekommt dann dieses Objekt uf1 per Binding in seinem DataContext zugewiesen. Beachten Sie das dies notwendig ist, um im vorigen Codeblock damit agieren zu können und das Label und die Textbox zu Binden. Dabei ist noch wesentlich das in der Bindung der Text Eigenschaft der Textbox das Binding Attribut ValidatesOnNotifyDataErrors gesetzt wird. Der folgende XAML Code ist stark gekürzt. xmlns:local="clr-namespace:KoelnSL" > <UserControl.Resources> <local:ustidFirma x:Key="uf1"/>..... <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource uf1}}"> ..... <sdk:Label Target="{Binding ElementName=txtmyID}"/> <sdk:Label Target="{Binding ElementName=txtID}"/> .. <TextBox x:Name="txtmyID" /> <TextBox x:Name="txtID" Text="{Binding ustid,Mode=TwoWay,ValidatesOnExceptions=true, NotifyOnValidationError=true,ValidatesOnNotifyDataErrors=True}" /> .. </Grid> Der Vollständigkeit halber noch der fertige Dialog.

WebCam Bild schiessen und in Isolated Storage speichern

Man muss das Rad nicht immer neu erfinden. Im heutigen Beispiel kommt deshalb die Library Imagetools zum Einsatz. Diese verwendet wiederum eine Zip Bibliothek und die wiederum … Und am Ende wird meine Silverlight Anwendung davon profitieren. Der “Use Case” ist eine Webcam im Browser. Der Benutzer kann ein Foto Schießen und dieses dauerhaft speichern. Den Part mit der Webcam halte ich hier sehr kurz. Zur Darstellung wird ein einfaches Rechteck (Rectangle1) verwendet. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices(0) Dim wb As New VideoBrush wb.SetSource(cs) Rectangle1.Fill = wb If CaptureDeviceConfiguration.AllowedDeviceAccess Or CaptureDeviceConfiguration.RequestDeviceAccess Then cs.Start() End If End Sub Wenn der Benutzer auf den Button drückt läuft das aktuelle Bild als Video. Ein zweiter Button dient dazu den Snapshot zu erstellen und das Bild zu speichern. Dazu müssen die DLL’s ImageTools, ImageTools.IO.Png und ImageTools.Utils als Referenz eingebunden werden. Frage Nummer eins ist, wie kommt man ans Bild? Und in welchem Format? Wer in den Dokus nachschlägt wird immer das Event CaptureImageAsync finden. Allerdings kann man auch direkt die Writeablebitmap Klasse verwenden um einen Screenshot eines beliebigen UIElements zu erstellen. Auch bei CaptureImageAsync erhält man ein Obekt vom Typ WriteableBitmap. Braucht nur ein bisschen mehr Code. Ein weiterer Unterschied ist, das Writeablebitmap erst nach vollständigen Rendering den “Screenshot” erstellt. Mit CaptureImageasync  bekommt man den Screenshot schneller aber ohne Effekte ala Pixelshader. Für das Capturen von laufenden Videos braucht es aber noch ein wenig mehr. Dazu dient dann Videosink. Mehr dazu im Blog von Rene Schulte. Im Silverlight Isolated Storage kann dann das Bild serialisiert gespeichert werden. Dazu wird aus dem WriteableBitmap zunächst ein Bild vom Typ PNG erzeugt. Ein netter Trick ist die Extension Methode ToImage, die der WriteableBitmap Klasse zugewiesen wird. (enthalten in Utils). Der PngEncoder erzeugt das korrekte PNG Format. Es liegen auch noch Encoder für BMP, GIF und JPG bei. Private Sub speichern_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles speichern.Click Dim wb As New WriteableBitmap(Rectangle1, Nothing) Image1.Source = wb Dim iso As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using sr As IsolatedStorageFileStream = iso.OpenFile("bild" + Date.Now.ToString("YYYMMDDHHmmss") + ".png", FileMode.OpenOrCreate) Using bw As BinaryWriter = New BinaryWriter(sr) Dim enc As PngEncoder = New PngEncoder() Dim bytesImage As Byte() Using ms As MemoryStream = New MemoryStream() Dim itImage = wb.ToImage() enc.Encode(itImage, ms) bytesImage = ms.ToArray() bw.Write(bytesImage) End Using End Using End Using End Sub Ziemlich cool ist, das die Bilder dann auch wirklich physikalisch auf der Festplatte rum liegen.

Bild aus Explorer in die Silverlight Anwendung ziehen

“Nur kein Code ist guter Code”. Ein Zitat aus meinem Munde [hannesp]. Entsprechend versuche ich immer mit minimalen Code mein Ziel zu erreichen. Vielleicht auch ein Grund warum ich mit Layern und MVxx recht sparsam umgehe. Deshalb bin ich auch ganz stolz auf mein Silverlight Bilder Drag & Drop Beispiel. Fünf Zeilen Code. Weniger geht kaum. Drag& Drop ist eine Funktion in Silverlight 4 die sowohl für OOB als auch in Browser Anwendungen funktioniert. Der Benutzer kann eine oder mehrere Dateien auf ein UIElement ziehen und dort fallen lassen. Dafür muss im UIElement das Attribut Allowdrop auf true gesetzt werden. Dann werden vier mögliche Events gefeuert. DragEnter DragOver DragLeaver Drop Wirklich wichtig ist nur das letzte Ereignis Drop. Die anderen Events könnten verwendet werden um den Benutzer durch z.B. Farbänderungen sichtbar zu machen wohin er gerade dropped. In Meinem Beispiel möchte ich auf ein Image Element ein Bild ziehen können. Da das Image ohne Bild nicht sichtbar ist, reagiert es auch auf keine Events und ist nicht abschätzbar wo die Grenzen sind. Ähnliches passiert meinen Silverlight Kurs Teilnehmer recht häufig mit einem Rechteck. Das ist auch nur clickbar wenn es mit einer Brush gefüllt ist. Um also den Rahmen zu zeichnen und das Drop Event zu bekommen, packe ich einfach das Image in ein Border der gefüllt ist. <Border HorizontalAlignment="Left" Height="211" Margin="75,0,0,0" Drop="Image1_Drop" AllowDrop="True" VerticalAlignment="Top" Width="202"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="Black" Offset="0"/> <GradientStop Color="White" Offset="1"/> </LinearGradientBrush> </Border.Background> <Image MouseLeftButtonDown="Image1_MouseLeftButtonDown" x:Name="Image1" Stretch="Fill" /> </Border> Im Drop  Event bekommt man per e.Data die Liste der gedroppten Dateien. In meinem Fall nehme ich einfach die erste Datei im Index und gehe davon aus das es ein JPG oder PNG ist. Dann wird die Datei per Filestream geöffnet und einem BitmapImage zugewiesen. Dieses wiederum ist die perfekte Quelle für das Image Element. Private Sub Image1_Drop(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs) Dim fi As FileInfo = e.Data.GetData(DataFormats.FileDrop)(0) Using fs As FileStream = fi.OpenRead Dim bi As New BitmapImage bi.SetSource(fs) Image1.Source = bi End Using End Sub   wie gesagt alles Beispiele aus meiner Silverlight Schulung. Der nächste Termin ist in Leipzig.

Silverlight Zwischenablage

Ein weiteres (sehr kleines) Beispiel aus meiner Silverlight Trickkiste. Irgendwann muss ich das mal strukturieren und ein Silverlight Tutorial draus machen. Da irgendwann in weiter Zukunft liegt zunächst mal was kleines. Die Zwischenablage des Betriebssystems kann in Silverlight 4 gelesen und geschrieben werden. Leider kann der Benutzer aber nur reine Texte kopieren. Es gibt drei Methoden auf dem statischen Clipboard Objekt. ContainsText GetText SetText Name ist Programm. Da der User den Zugriff auf die Zwischenablage (ähnlich der Webcam) explizit erlauben muss, gibt es eigentlich drei Fälle. Folgender Silverlight Code mit VB.NET zeigt die Szenarien. Try     If Clipboard.ContainsText() Then        TextBox1.Text = Clipboard.GetText()     Else        MessageBox.Show("Zwischenablage leer")     End If Catch ex As SecurityException     MessageBox.Show("Bitte Zugriff auf Zwischenablage erlauben") End Try Das funktioniert OOB ( out of browser) und auch für Browser Anwendungen.

Dynamische Silverlight UI mit C(l)ick

Manchmal kommen mir so Ideen und ich probiere rum bis es geht ohne eine Idee zu haben wofür eigentlich. Meine heutige Abendbeschäftigung sind entschärfte Commands um dynamisch ein Formular zu erzeugen. Meist sieht man den Einsatz von Commands im Zusammenhang mit MVVM. Ich will es aber einfacher. Zunächst die Ausgangsituation. Silverlight 4 besitzt ein Command Attribut mit denen man Commands deklarativ zuweisen kann. Weiters kann man mit dem XAMLReader ein UI Element aus einem String erzeugen lassen. Wenn man Events per Attribut zuweisen möchte meckert der XAML Parser. Also wie sieht normalerweise ein Button Event aus <Button Content="fester Button" Height="37" Click="Button1_Click" HorizontalAlignment="Left" Margin="42,35,0,0" Name="Button1" VerticalAlignment="Top" Width="102" /> Im VB.NET Code dann im Ansatz so Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Alternativ kann man auch im Command Attribut ein Command angeben. Dazu benötigt man aber eine Klasse die das Interface ICommand implementiert. In der minimal Ausstattung wie folgt mit einer simplen MessageBox. Public Class HannesCommand1 Implements ICommand Public Sub HannesCommand1() End Sub Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute Return True End Function Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute MessageBox.Show("hannescommand1") End Sub End Class Das Command kann aber nicht direkt verwendet werden. Der Umweg muss über die Ressourcen. In jedem Fall benötig man den Namensraum auf das aktuelle Silverlight Projekt das den Namen KoelnSL heißt. Der Namensraum wird willkürlich local genannt. xmlns:local="clr-namespace:KoelnSL" Das Command wird dann als Unterelement deklariert über den Namesraum local und den Klassennamen. <Button Content="Button" Height="38" HorizontalAlignment="Left" Margin="121,227,0,0" Name="Button1" VerticalAlignment="Top" Width="105" > <Button.Command> <local:HannesCommand1></local:HannesCommand1> </Button.Command> </Button> Alternativ besteht auch die Möglichkeit über eine Statische Ressource zu gehen. Die Instanz wird per x:Key willkürlich hCommand benannt. <UserControl.Resources> <local:HannesCommand1 x:Key="hCommand"></local:HannesCommand1> </UserControl.Resources> Im XAML des Button Elements kann man dann mit kurzer Syntax direkt per Command Attribut die Bindung deklarieren auf hCommand. <Button Content="Button" Height="38" Command="{StaticResource hCommand}" HorizontalAlignment="Left" Margin="121,227,0,0" Name="Button1" VerticalAlignment="Top" Width="105" > Soweit die Theorie zum Teil Commands. Als nächstes gehts um das dynamsiche laden von XAML. Die passende Klasse ist XAMLReader zu finden im Namenraum System.Windows.Markup. Damit kann aus einem String ein UIElement erstellt werden, das man an den XAML Tree anhängen kann. Der Parser benötigt allerdings den XAML presentation Namensraum im String. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Dim s As String = "<Button xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" " + " Content=""dynamischer Button"" Command=""{StaticResource hCommand}"" Height=""37"" HorizontalAlignment=""Left"" " + " Margin=""42,107,0,0"" Name=""Button2"" VerticalAlignment=""Top"" Width=""189"" >" + "</Button>" Dim xaml As UIElement = XamlReader.Load(s) LayoutRoot.Children.Add(xaml) End Sub Das Problem war bei mir das der XAML Parser von Silverlight zur Laufzeit einen Fehler wirft, weil er hCommand nicht findet. Es scheint ein Problem in der Hierarchie geben, dem ich später auf den Grund gehen werde. Aber ich weis das wenn eine Resource nicht gefunden wird, der Parser in den Ressourcen von APP.XAML nachschlägt. Deshalb also einfach dort deklariert. <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d=http://schemas.microsoft.com/expression/blend/2008 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="KoelnSL.App" xmlns:local="clr-namespace:KoelnSL"> <Application.Resources> <local:HannesCommand1 x:Key="hCommand1"></local:HannesCommand1> </Application.Resources> </Application> Demnächst auch ein echtes MVVM Beispiel mit Binding.

Einen Silverlight Clipping Path Animieren

Aktuell habe ich in meiner Silverlight Schulung einen Teilnehmer, der wirklich interessante Fragen aufwirft, deren Antwort ich dann im Nachgang suche und hier in meinem Blog poste. Schließlich sind nur glückliche Silverlight Kurs Teilnehmer auch gute Teilnehmer. In diesem Silverlight 4 Beispiel soll ein Bild beschnitten werden. Das geht auch ganz einfach mit dem Clipping Path Attribut. Mit Expression Blend nimmt man ein Bild, legt darüber eine Kurve (z.B. Kreis) und markiert beide. Dann wird im Context Menü Path- Make Clipping Path ausgewählt. Im XAML sieht das wie folgt  aus und man sieht nur mehr den Hintern der hübschen Person vom Foto . <Image Margin="30,41,-140,-124" Source="/maus[1].jpg" Stretch="Fill" Clip="M470.5,351.5 C470.5,387.3985 425.05692,416.5 369,416.5 C312.94308,416.5 267.5,387.3985 267.5,351.5 C267.5,315.6015 312.94308,286.5 369,286.5 C425.05692,286.5 470.5,315.6015 470.5,351.5 z"/> Leider lässt sich das so nicht mehr animieren. Viele Attribute eines UIElements lassen sich durch ein Unterelement aufsplitten. So auch das Clip Attribut zu Image.Clip. Der Pfad wird dann mit Geometry Elementen beschrieben. In unserem Beispiel eben eine Ellipse. <Image x:Name="image" Source="/maus[1].jpg" Stretch="Fill" RenderTransformOrigin="0.5,0.5" > <Image.Clip> <EllipseGeometry RadiusX="50" RadiusY="50" Center="100,100"> </EllipseGeometry> </Image.Clip> </Image> Insgesamt gibt es fünf solcher Geomtrien. EllipseGeometry GeometryGroup LineGeometry PathGeometry RectangleGeometry Jetzt bleibt nur mehr das Problem der Animation. Leider gibt es keine Unterstützung durch Expression Blend oder schon gar nicht Visual Studio 2010. Also ran per Hand. Da ich eine Punkt verschieben möchte ist die PointAnimation das passende. Recht Tricky ist die Adressierung des Center Propertys über die XAML Hierarchie. <Storyboard x:Name="Storyboard1"> <PointAnimation BeginTime="0" Duration="0:0:01" From="0,0" To="200,200" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Clip).(EllipseGeometry.Center)"></PointAnimation></Storyboard> Gestartet wird die Animation per Expression Blend behavior. Am Ende eine Null Code Lösung. <i:Interaction.Triggers> <i:EventTrigger EventName="MouseLeftButtonDown"> <ei:ControlStoryboardAction Storyboard="{StaticResource Storyboard1}"/> </i:EventTrigger> </i:Interaction.Triggers> Funktioniert. Ich bin wieder mal begeistert.