Letztes mal habe ich kurz beschrieben was man mit einer Listbox und Templating in Silverlight so machen kann. Wenn man allerdings keine Auswahl Möglichkeit braucht, kann man auch das schlankere Itemscontrol (übrigens erbt Listbox davon) verwendet werden. Die Daten werden als Liste in einer Klasse erzeugt. Public Class personen
Inherits List(Of person)
Public Sub New()
Add(New person With {.alter = 27, .Firma = "ppedv ag", .Name = "Hannes"})
Add(New person With {.alter = 32, .Firma = "ppedv ag", .Name = "Andreas"})
Add(New person With {.alter = 23, .Firma = "ppedv ag", .Name = "Bernhard"})
Add(New person With {.alter = 45, .Firma = "ppedv ag", .Name = "Stefan"})
Add(New person With {.alter = 12, .Firma = "ppedv ag", .Name = "Arnold"})
End Sub
End Class
Diese Daten werden deklarativ instanziert und gebunden. Nicht weil es nötig wäre, sondern schlicht weil es möglich ist. Das folgende passiert dann auch in der XAML Datei
... xmlns:local="clr-namespace:KoelnSL">
<UserControl.Resources>
<local:personen x:Key="personen"></local:personen>
</UserControl.Resources>
Ein positver Nebeneffekt ist, das die Bindung auch in Blend und Visual Studio 2010 zur Entwurfszeit voll sichtbar ist.
Im XAML werden dann die drei Templates definiert
<ItemsControl Width="100" ItemsSource="{Binding Source={StaticResource personen}}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="BlueViolet" BorderThickness="1" CornerRadius="10">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Background="SkyBlue" Margin="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Height="20" Width="50" Margin="4"></TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Auf jedem guten TV Sender gibt es eine Wetter Fee. Diese meist sehr attraktive Person choreographiert vor einer blauen oder heute meist grünen Wand. Die Clouds oder Sonnen werden Computer animiert eingespielt. Dazu brauchen wir eine Wetterfee, eine Kamera, ein Aufnahmestudio, Expression Blend 4 und .. die Möglichkeit in einem Video eine Farbe auf transparent zu schalten. Die Wetterfee ist meine entzückende Kollegin Lilly. Die Kamera eine Smartflip ( jetzt Cisco). Das Aufnahmestudio eines Fotografen der im gleichen Haus sitzt und eine grüne Rollwand besitzt. Diese rollt man ein Stück über den Boden so das auch keine Kanten sichtbar sind. Dann das ganze nach Expression Blend importiert und per Drag& Drop auf eine Seite ziehen. Warum Lilly hier so seltsame Handbewegungen macht? Ich wollte später eine per Silverlight Animation eine Sonne durchs Bild schieben. Was man hier gut sehen kann ist, das der Hintergrund nicht homogen ist. Das ist mir bei unserem 30 Sekunden Shot nicht aufgefallen. Es liegt daran das Fotografen in der Regel einen Spot auf den Hintergrund legen um das Foto aufzulockern. Hier müsste der Spot abgeschalten werden. Dazu aber gleich mehr. Seit Silverlight 3 gibt es Pixelshader. Diese helfen Bilder direkt in der Grafikkarte performant zu verändern( Wenn GPU Acceleration aktiviert ist). Per Standard ist Schatten und Unschärfe vorhanden. In Blend 4 noch ein wenig mehr Shader. Wir brauchen aber einen Pixelshader der aus Grün Transparent macht. Dies nennt man fachspezfisch den ChromaKey fürs Alpha Blending. Entsprechend auch der Name ChromaKeyAlphaEffect. Der Download für die installierbare MSI findet sich hier http://code.msdn.microsoft.com/SL3ChromaKeyEffect/Release/ProjectReleases.aspx?ReleaseId=3900 Leider ist der aktuelle Build 1.3 vom 9.Februar.2010 noch SL 3. Entsprechend ist Blend 4 und Visual Studio 2010 ein wenig mit der Aufgabe überfordert und man benötigt etwas Handarbeit. Zunächst setzt man eine Referenz im Silverlight Projekt auf Synergist.Effects.dll. Dann kann man in Expression Blend im Reiter Assets den Chroma Key Alpha Effect auf das Video ziehen. Im Property Dialog sollte dann entweder direkt in den Eigenschaften des ChromaAlphaEffect (siehe Bild) oder in den Eigenschaften des Videos im Effect Reiter die Farbe ausgewählt werden können. Am besten geht das mit dem Color Picker des üblichen Farbdialoges. Leider tritt hier der erste Bug zu Tage. Das zusätzliche Property Tolerance wird nicht angeboten. Damit kann man den Grünbereich etwas aufweiten. Mögliche Werte sind 0-1. Ganz gut trifft oft rund um 0,2. Aktuell sieht dann Lilly so aus. Wenn man im XAML Source dann Tolerance setzt, <MediaElement x:Name="lilly1_wmv" Source="lilly1.wmv" Stretch="Fill" AutoPlay="True" MediaEnded="lilly1_wmv_MediaEnded" >
<MediaElement.Effect>
<Synergist_Effects:ChromaKeyAlphaEffect
Tolerance="0.2"
ColorKey="#FF9DE094"/>
</MediaElement.Effect>
</MediaElement
siehts in Blend so aus.
In Visual Studio 2010 ist es im Ergebnis das gleiche. Allerdings lauffähig ist das ganze schon. Also ab in den Browser und staunen.
Noch nicht perfekt und ich habe deswegen auf die Animation verzichtet aber ich glaube wir machen noch einen zweiten Dreh mit besseren Hintergrund.
In den nächsten Blog Einträgen werde ich meine Gedanken zum Thema Services mit Silverlight zu ordnen beginnen. Es gibt einfach zu viele Wege um Daten mit der Client Server Technologie Silverlight hin und her zu schicken. Leider sind die recht einfachen ASMX Web Services obsolet und in WCF aufgegangen. Der größte Unterschied ist das man WCF (Windows Communication Foundation) Dienste nicht mal so spaßeshalber im Browser aufrufen kann. Ergänzend der Hinweis auf das Blog meines Kollegen Bernhard, der ein wahres WCF Genie ist. Zunächst einmal die Vorarbeit mit einer Datenklasse. Imports System.Runtime.Serialization
<DataContract()>
Public Class person
Private _FamName As String
<DataMember()>
Public Property FamName() As String
Get
Return _FamName
End Get
Set(ByVal value As String)
_FamName = value
End Set
End Property
Private _GebDat As Date
<DataMember()>
Public Property GebDat() As Date
Get
Return _GebDat
End Get
Set(ByVal value As Date)
_GebDat = value
End Set
End Property
Private _bild As String
<DataMember()>
Public Property bild() As String
Get
Return _bild
End Get
Set(ByVal value As String)
_bild = value
End Set
End Property
End Class
Wenn DataMember nicht dekoriert wird, gibt es später beim aufrufen des Services einen deserialisierungsfehler in der Form InnerException: System.Runtime.Serialization.SerializationException. Das sind eben WCF Basics. Dann erstelle ich eine Business Objekt das mir eine Liste von Personen zurück gibt.
Public Class BO1
Public Function getPersonen() As List(Of person)
Dim lofP As New List(Of person)
lofP.Add(New person With {.bild = "bild1.jpg", .FamName = "Maier", .GebDat = New Date(("01.13.1978"))})
lofP.Add(New person With {.bild = "bild2.jpg", .FamName = "Huber", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild3.jpg", .FamName = "Müller", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild4.jpg", .FamName = "Gates", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild5.jpg", .FamName = "Heuer", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild6.jpg", .FamName = "Holesch", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild7.jpg", .FamName = "dela Rosa", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild8.jpg", .FamName = "Jobs", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild9.jpg", .FamName = "Hayat", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild10.jpg", .FamName = "Hatahet", .GebDat = New Date("01.13.1978")})
lofP.Add(New person With {.bild = "bild11.jpg", .FamName = "Thyret", .GebDat = New Date("01.13.1978")})
Return lofP
End Function
End Class
Ein WCF Service wird im Silverlight Projekt per Template Silverlight-Enabled-WCF-Service erstellt. Dieser Service ruft dann die Funktion auf, die die Personenliste erstellt. Per Dafult findet sich da immer die DoWork Prozedur.
<OperationContract()>
Public Function GetAllPersons() As List(Of person)
Dim bo As New BO1
Return bo.getPersonen()
End Function
Der Service ist im Browser aufrufbar. Mit dem Parameter WSDL erscheint auch die Beschreibung der Funktion und Objekte. Diese wird von Werkzeugen verwendet um Proxy Klassen erzeugen zu können. Das sollten Sie für einen ersten Test auf alle Fälle tun.
Dann wechseln wir in das Silverlight Projekt und erstellen eine Service Referenz im Projekt Baum per Context Menü Add Service.
In der Silverlight Anwendung wird dann eine Instanz der Proxy Klasse erzeugt, eine Rücksprung Methode definiert und der Service aufgerufen. Vergessen Sie nicht, das Silverlight nur Asynchron arbeitet und deswegen nicht auf eine Rückgabe wartet. Die eigentliche Liste von Personen wird dann per e.Result in der Rücksprung Methode ausgelesen und einem Datagrid zugewiesen. Hier passiert eine wenig VB casting magic. Sie sollten wissen das der Visual Studio Proxy Wizard immer aus Listen einen Typ ObservableCollection erzeugt. ich habe bereits hierzu ein paar Worte verloren, das das auch anders geht.
Private Sub WCF1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim svc As New ServiceReference1.Service1Client
AddHandler svc.GetAllPersonsCompleted, AddressOf fertig
svc.GetAllPersonsAsync()
End Sub
Private Sub fertig(ByVal sender As Object, ByVal e As ServiceReference1.GetAllPersonsCompletedEventArgs)
SVC1.ServiceReference1.person)
DataGrid1.ItemsSource = e.Result
End Sub
Mit Hilfe des Silverlight RIA Services Toolkit lassen sich zusätzliche Optionen der RIA Services nutzen. LinqToSql DomainService SOAP Endpoint JSON Endpoint ASP.NET DomainDataSource Control In diesem Blog Eintrag betrachte ich einmal die Möglichkeiten der serialisierung. Insgesamt kann man stand heute folgenden Formate wählen. Binary OData SOAP JSON Allerdings muss in der Web.Config manuell die Konfiguration erweitert werden. <domainServices>
<endpoints>
<add name="OData"
type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="JSON"
type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="Soap"
type="Microsoft.ServiceModel.DomainServices.Hosting.SoapXmlEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</endpoints>
</domainServices>
Der Aufruf für eine JSon Rückgabe erfolgt in der Form
http://localhost:[port]/ClientBin/[Projektname]-Web-[DomainServiceklasse].svc/Json/[Methode]
Da die SVC Dateien nicht wirklich existieren sondern per IIS Modul Mapping behandelt werden kann der Pfad statt Clientbin auch z.B. Service lauten.
Wenn man den Traffic mit Fiddler mitschneidet kann man sehr gut erkennen wie das Json Format aufgebaut ist.
Die Aufrufe der einzelnen Methoden und die Rückgabe Datenmenge in KB im Vergleich
..KoelnSL-Web-DomainServiceMD.svc/binary/GetOrdersDetails
607K
..KoelnSL-Web-DomainServiceMD.svc/Json/GetOrdersDetails
563K
..KoelnSL-Web-DomainServiceMD.svc/OData/GetOrdersDetails
1109K
..KoelnSL-Web-DomainServiceMD.svc/Soap/GetOrdersDetails
erzeugt 400er Error
Soap Abrufe sind leider im Browser nicht direkt möglich. Deshalb auch die Fehlermeldung. Um den Service bzw die Methode testweise aufzurufen könnten man das Hilfstool WCFtestClient.exe verwenden. Leider unterstützt dieses keine Domainservices. Also bleibt nur der Weg über das Silverlight Projekt.
Leider kompiliert meine Lösung anschliessend nicht mehr. Selbst auf Nachfrage bei Microsoft konnte man adhoc das Problem nicht lösen. Die einzige Antwort das es aktuellere Bits der RIA Services und Silverlight (GDR) gibt hilft nicht, da ich dafür alles neu installieren muss und dann mit RC Bits arbeite die nirgendwo laufen. Entsprechend werde ich die Lösung für SOAP in einem späteren Blog nachliefern.
Die Headline stimmt nur bedingt, aber schliesslich muss ich meinen Artikel ja verkaufen . Am Ende ist es aber auch nicht ganz falsch. Wenn ein Silverlight Projekt mit RIA Services besteht, ist Master Detail tatsächlich nur 1 Codezeile und 2 clicks entfernt. Zunächst einmal nehme ich die gute alte Nordwind Datenbank und die Tabellen Orders und OrderDetails als Ausgangspunkt und erstelle im Web Projekt mit Entity Framework ein Modell. Weiter geht es mit einer neuen Domain Service Klasse. Wie immer vorher kompilieren nicht vergessen und die Metadaten erzeugen lassen. In der DomainService Klasse erzeuge ich per copy paste eine weitere Queryklasse. Per Standard findet sich nur die GetOrders. Da ich ja noch die Details mit liefern möchte nenne ich die Funktion GetOrdersDetails. Wichtig ist per LINQ Query und dem Kommando Include die Kind Tabelle Order_Details einzubinden. Mit dem Attribut <Query> stelle ich sicher das später im Data Designer die Query auch auftaucht. <Query()> _
Public Function GetOrdersDetails() As IQueryable(Of Orders)
Return Me.ObjectContext.Orders.Include("Order_Details")
End Function
Dann muss in den Metadaten (xxx.metadata.vb) noch der Schlüssel Property markiert werden. Dazu wird <Include()> vor den bereits vorhandenen Propertynamen vorgestellt.
<Include()>
Public Property Order_Details As EntityCollection(Of Order_Details)
Seit VB 2010 braucht man dafür übrigens keinen Unterstrich mehr *like`*. Dann wieder kompilieren.
In Visual Studio ins Silverligth Projekt wechseln und eine XAML Datei auswählen. Dann ist der Menüpunkt Data vorhanden. Dort einfach die Darstellungsform Datagrid einstellen und die Orders und die Order Details auf die Page ziehen.
Das fertige Ergebnis nach 9,67 Sekunden im Browser
Ich habe mir einen Wunschtraum erfüllt. Eine österreische Ampel als mini App. Da Windows Phone noch kein Silverlight unterstützt, vorerst nur als Desktop Anwendung Was macht nun eine österreichische Ampel so einmalig. Sie blinkt 3 mal Grün bevor sie auf gelb schaltet! Diese Ampel kann uns nun den Arbeitsalltag versüssen, gibt uns klare Richtlinien vor. Sinnlosses Starren auf den Fortschrittsbalken ist nun endgültig vorbei. Jetzt kommt sinnloses Starren auf die Ampel. Ich bitte um Überweisung von 3,99 € pro Download. Reklamation und Rückgabe ausgeschlossen. Zunächst einmal das technsiche Konzept. Ich implementiere die Ampel als Usercontrol und nutze den Visual Statemanager um die Statusinformation Rot, Gelb und Grün mit Animationen hinterlegen zu können. Startpunkt ist das UI Design mit Blend. Es beginnt relativ harmlos mit einer Border, Farbverlauf und wie immer runden Ecken. Dann widme ich mich der ersten Lampe. Ein Kreis mit etwas dickerem Rand (Strockethickness 10) und zwei gegenläufige Farbverläufe im Graubereich erzeugen einen 3D Eindruck. Da rein kommt dann nochmal ein kleinerer Kreis, der ein wenig Abstand zum äusseren Rahmen hat auf den ersten Kreis. So das man den Hintergrund am Rand noch ein wenig sehen kann. Dieser Kreis wird rot gefärbt und bekommt einen im Zentrum leicht versetzten runden Farbverlauf. Das GradientBrushtool ( dicker Pfeil) aus der linken Toolleiste hilft dabei ungemein. Um die die Wölbung perfekt zu machen, wird Lichteinfall von oben simuliert. Dazu wird ein weiße, sehr transparente Elypse, darüber gelegt. Das gleiche machen Comic Zeichner z.B. mit den großen Heidi Augen um diese wässrig aussehen zu lassen. Ich verwende hier 30% alpha blending und versüse das mit einem Effekt um ein unscharfe Kontur zu haben. Dann werden die Ui Element in ein Canvas gepackt, kopiert und umgefärbt. Weiter gehts mit dem Visual State Manager. Dort erzeuge ich eine Gruppe mit dem Namen Phasen und darin die drei States: rot, gelb und gruen. Dann kann jeder in jedem State und Übergang das UI animiert werden. Im wesentlichen werden die beiden anderen Kreise einfach schwarz gefärbt mit einem Delay von 0,2 Sekunden. Schliesslich leuchtet so eine antike Glühbirne ja nach. LED Ampeln gibts in Österreich noch nicht. Für die Phase Grün auf Gelb habe ich einen speziellen Übergang definiert. Diese Animation läuft drei Sekunden und wechselt dabei von abwechselnd drei mal von Grün nach Schwarz den SolidcolorBrush im Fill Attribut. Als nächstes wird das Usercontrol in eine weitere XAML Seite eingepackt. Dort gehts dann nur mehr mit puren Code zur Sache. Ein Timer wirft die Statusänderungen an. Da die Ampelphasen ja unterschiedlich lang sind, brauch ich ein wenig Logik um die Timer Zeiten zu ändern. Da die Grün Blink Phase 3 Sekunden dauert muss die Gelb Phase 5 Sekunden sein um in Summe 2 Sekunden Gelb zu sehen. Ich denke der Code spricht auch ohne refactoring für sich selbst Dim dp As New DispatcherTimer
Dim status As Integer = 0
private Sub page35_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
dp.Interval = New TimeSpan(0, 0, 10)
AddHandler dp.Tick, AddressOf ticking
VisualStateManager.GoToState(Ampel1, "rot", True)
status = 1
dp.Start()
End Sub
Private Sub ticking(ByVal sender As Object, ByVal e As EventArgs)
Select Case status
Case 0
VisualStateManager.GoToState(Ampel1, "rot", True)
dp.Interval = New TimeSpan(0, 0, 10)
status = 1
Case 1
VisualStateManager.GoToState(Ampel1, "gelb", True)
dp.Interval = New TimeSpan(0, 0, 2)
status = 2
Case 2
VisualStateManager.GoToState(Ampel1, "gruen", True)
dp.Interval = New TimeSpan(0, 0, 10)
status = 3
Case 3
VisualStateManager.GoToState(Ampel1, "gelb", True)
dp.Interval = New TimeSpan(0, 0, 5)
status = 0
End Select
End Sub
Jetzt habe ich nur mehr das Problem, wie komme ich an Ihr Geld. Bzw wie können Sie ganz einfach die Anwendung starten?
Dazu rufen Sie einfach folgende Website auf. Per rechtsclick können Sie diese auch lokal installieren.
In meiner aktuellen Silverlight Schulung wurde heute das Thema Mouse Events besprochen. Seit Silverlight 4 gibt es ja bekannterweise auch Events zur rechten Maustaste. Also sollte theoretisch auch eine Textbox per Rechstclick ein Context Menü anzeigen können. Die Idee des Schulungs Teilnehmers sah ungefähr so aus. <TextBox Height="111" MouseRightButtonUp="textBox1_MouseRightButtonUp" Name="textBox1" Width="180" />
Allerdings funktioniert das nicht, weil die Textbox das Event als behandelt markiert und damit nicht mehr weiter reicht. Um ein Context Menü zu erstellen gibt es aber seit Silverligth 4 im seperat erhältlichen Toolkit eine passende Lösung. Mit dem ContectMenuService wird ala Tooltip die Textbox um zusätzliche Funktion erweitert.
Das Context Menü funktioniert genauso wie in WPF. Es besteht aus Menuitems. Pro Menüpunkt ein Item. Dieses wiederum aus einem Header, der üblicherweise den Menütext darstellt. Optisch aufgepeppt wird über ein Unterelement MenuItem.Icon per Image Element. Das Attribut Icon im MenuItem Element kann dafür nicht genutzt werden. Für den Trennstrich gibt es das Seperator Element.
<TextBox Height="30" HorizontalAlignment="Left" Margin="161,36,0,0" Name="textBox1" VerticalAlignment="Top" Width="180" >
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem
Header="Hyperlink"
Click="MenuItem1_Click">
<toolkit:MenuItem.Icon>
<Image Source="Images/link.png"></Image>
</toolkit:MenuItem.Icon>
</toolkit:MenuItem>
<toolkit:Separator />
<toolkit:MenuItem Header="drucken"
Click="MenuItem2_Click">
<toolkit:MenuItem.Icon>
<Image Source="Images/print.png"></Image>
</toolkit:MenuItem.Icon>
</toolkit:MenuItem>
<toolkit:MenuItem Header="speichern"
Click="MenuItem3_Click" IsEnabled="True">
<toolkit:MenuItem.Icon>
<Image Source="Images/save.png"></Image>
</toolkit:MenuItem.Icon>
</toolkit:MenuItem>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBox>
In diesem Beispiel werden einzelne Events pro Menüpunkt deklariert. Denkbar ist auch per Commands zu arbeiten. Dann wird das Attribut Command bzw CommandParameter verwendet.
Eben sind die neuen Programme aus der Live Serie erschienen. Ich schreibe dies auch auf dem neuen Windows Live Writer Beta. Den gibts aktuell leider nur in Englisch und so muss ich auf die deutsche Rechtschreibprüfung verzichten. Man verzeihe mir schon jetzt. Was seit Beginn der Zune Zeiten auffällt, ist die Reduktion auf weniger. Also vor allem kurzer Text statt bunter Icons. Apple geht hier aktuell einen anderen Weg. Microsoft hat das ganze sogar in seinem Metro UI Style Guide beschrieben. Dort steht auch, das man Oberflächen bewusst leer halten soll. Eine alte Designer Regel. Ausserdem soll man alle Elemente dem Benutzer zugänglich machen. Als Beispiel dafür möchte ich die klassische Windows XResult Fehlermeldung nennen, deren Text man nicht in die Zwischenablage kopieren kann, da es als Label implementiert ist. Um das klar zu sagen, ich stehe zu beiden Aussagen. Das macht Sinn und ist die Zukunft. Ich habe mir nun einen einfachen Silverlight Login Dialog vorgenommen um dieses Design Pattern umzusetzen. Textblock ist eigentlich obsolet und gehört verboten. Entsprechend kommt die Beschriftung in die Textbox und verschwindet, wenn der Benutzer etwas eingetippt hat. Davon ausgehend, das der Benutzer auch nachträglich erkennen kann was der Inhalt bedeutet. Wenn die Textbox wieder geleert wird, erscheint die Watermark in wieder in Grau. Realisiert habe ich das ganz einfach. Zunächst wird der Watermark Text der Textbox im Tag und Text Element abgelegt und der Foreground Grau gesetzt. Das Ziel ist es die Watermark wieder herstellen zu können. Dann brauche ich nur mehr die Events GotFocus und LostFocus um minimale Funktion zu implementieren. <TextBox Height="26" Name="txtUser" Width="63" Margin="10"
MaxLength="3" Foreground="gray"
GotFocus="txt_GotFocus" LostFocus="txt_LostFocus"
Text="User" Tag="User">
</TextBox>
<TextBox Height="26" Name="txtPassword" Width="84" Foreground="gray"
GotFocus="txt_GotFocus" LostFocus="txt_LostFocus"
Text="Passwort" Tag="Password"></TextBox>
Die Methoden sind universell ausgelegt und erkennen das Sender Control.
Private Sub txt_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim tb As TextBox = CType(sender, TextBox)
If (tb.Text = String.Empty) Then
tb.Text = tb.Tag
tb.Foreground = New SolidColorBrush(Colors.Gray)
End If
End Sub
Private Sub txt_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim tb As TextBox = CType(sender, TextBox)
If tb.Text = tb.Tag Then
tb.Text = ""
tb.Foreground = New SolidColorBrush(Colors.Black)
End If
End Sub
Sieht doch schick aus, oder?
Sind Sie ein Designer? Nein, aber möchten trotzdem schöne Oberflächen? Dann könnten die Silverlight Themes etwas für Sie sein. Es gibt aktuell drei Neue mit dem Namen Accent Color, Windows 7 und Cosmopolitan. Ursächlich sind diese für die sogenannte Silverlight Navigation oder Silverlight Business Application gedacht. Allerdings können die Styles auch anderwärtig verwendet werden. Dazu später mehr. Der Download für die Version 1.1 umfasst Style für die Silverlight Core, Silverlight SDK und Silverlight ToolkitControls. Es gibt drei einzelne Dateien •README_FIRST.txt – •SL4Themes-templates.zip –darin enthalten ein Ordner für Expression Blend und Visual Studio Templates. Ebenso ist ein Unterordner für RIA Services Templates. •SL4Themes-rawassets.zip – in dieser Datei stecken sozusagen die nackten Styles ohne Visual Studio Template. In der beigefügten Readme Datei ist beschrieben wie man ein SIlverlight Projekt mit manuell mit diesen Style versieht. Dies ist eigentlich recht einfach und für VB Entwickler der einzige Weg die Styles zu verwenden. SL4Themes-Templates Um die Themes für Visual Studio 2010 zu installieren einfach die *.vsix files im VS2010 Ordner starten. Um die Themes für Expression Blend 4 muss man manuell die Dateien kopieren. Der Zielordner für Blend4 findet sich in %ProgramFiles%\Microsoft Expression\Blend 4\ProjectTemplates\en\CSharp\Silverlight. Beim starten von Blend stehen dann die Templates zur Verfügung. In Visual Studio sieht das dann so aus Für beide Fälle gilt das dies nur in C# Projekten zur Verfügung steht. Mir klingt noch in den Ohren “VB.net is a first class citicen”. Für VB.NETler geht dann eben nur der Weg über SL4Themes-rawassets. Eine Silverlight Navigation Application mit dem Cosmopolitian Theme sieht ein wenig aus wie im Metro UI Style Guide definiert. Zune Anwendungen und zukünftige Windows phone Anwendungen folgen diesem recht nüchternen Text orientieren Design. Im Silverlight Projekt ist dies über den Assets Ordner zugeordnet. Dort finden sich in XAML Dateien die Styles. Die kann man natürlich nach eigenen Geschmack auch anpassen. Die Zuweisung der Styles findet in der app.xaml statt. <Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/Styles.xaml"/>
<ResourceDictionary Source="Assets/CoreStyles.xaml"/>
<ResourceDictionary Source="Assets/SDKStyles.xaml"/>
<!--<ResourceDictionary Source="Assets/ToolkitStyles.xaml"/>
To extend this theme to include the toolkit controls:
1. Install the Silverlight Toolkit for Silverlight 4
2. Add a Toolkit control to your project from the toolbox. This will add references to toolkit assemblies.
3. Change the "Build Action" for ToolkitStyles.xaml to "Page"
4. Uncomment the resource dictionary include above.
If you do not intend to use toolkit controls, delete this comment and the ToolkitStyles.xaml file.-->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Tim Heuer schreibt auch noch ein wenig in seinem Blog darüber. Komplette Beispielanwendungen als Design Muster
http://www.silverlight.net/content/samples/sl4/themes/cosmopolitan.html
http://www.silverlight.net/content/samples/sl4/themes/windows7.html
http://www.silverlight.net/content/samples/sl4/themes/accent.html
Wenn man beim erstellen eines Domainservices die Metadaten dazu erstellt, kann man sehr einfach per die Eigenschaften des Geschäftsobjekte deklarativ im Code mit Informationen versehen, die später zur Laufzeit ausgewertet werden. Also per <Attribut> Syntax. Dadurch erhält man dann im Web Projekt Klassen in der Konvention DomainService1.metadata.vb. Im Code dieser Klasse kann dann per Annotation die Logik jeder Eigenschaft gesteuert werden. Folgendes lässt in einem Dataform das Feld Telefon erscheinen und es wird Fett dargestellt, da es ein muss Feld ist. Darüber erscheint es ziemlich weit oben. <Required()>
<Display(Name:="Telefon", Order:=2)>
Public Property Phone As String
Was passiert nun wen Benutzer keine Telefonnummer angibt und das Feld leer lässt?
Natürlich kann man auch den Fehlertext noch ändern. Dazu einfach im Required ErrorMessage:= setzen. Diese Prüfung der Benutzereingabe findet Adhoc, also nach verlassen des Eingabefeldes statt. Ohne Interaktion zum Server.
Für komplexere Validierungen stellt Silverlight ein Benutzerdefinierte Prüfung sowohl am Client als auch am Server bereit. Dazu wird das entsprechende Property in der IRA DomainKlasse per Attribut Customvalidatiion dekoriert. Der erste Parameter stellt den Klassennamen dar und der zweite die Methode.
<CustomValidation(GetType(regeln), "hannesValid")>
Der Prüfcode muss eine gewissen Regelwerk folgen, sonst wird er schlicht nicht ausgeführt. Zunächst muss die Prüfroutine shared (c# static) sein. Die Rückgabe muss vom Typ Validationresult sein.
Imports System.ComponentModel.DataAnnotations
Public Class regeln
Public Shared Function hannesValid(ByVal region As String) As ValidationResult
If region.Length > 2 Then
Return New ValidationResult("nur 2 Zeichen")
End If
Return ValidationResult.Success
End Function
End Class
Das wird nun eine reine Prüfung am Server bewirken. Also erst wenn der Datensatz nach einer Änderung zurück geschrieben wird. Mit Hilfe von Shared Code kann diese Prüfung aber auch am Client sofort durchgeführt werden. Zeitpunkt ist wenn die Datenbindung aktualisiert wird, also bei LostFocus.
Die Prüfungsklasse muss nun in der Web Anwendung in einer eigenen Datei erstellt werden die der Namens Konvention shared.vb oder shared.cs folgt.
Der Code wird dann automatisch von Visual Studio 2010 auch in die Silverlight Anwendung “repliziert”.
In meinem Beispiel validiert laut debugger zwar die Silverlight Anwendung aber der Benutzer sieht keine Fehlermeldung. Das muss man erst über einen optionalen Parameter im ValidationResult ansteuern. Dabei wird der Feldname als Parameter angegeben, hier eben Region.
Public Shared Function hannesValid(ByVal region As String) As ValidationResult
If region.Length > 2 Then
Return New ValidationResult("nur 2 Zeichen", New String() {"Region"})
Denkbar ist auch, da als Array vorhanden, mehrere Felder anzugeben in der Form New String() {"Region", "Phone"})
Sinnvoller ist meines Erachtens einen universellen Silverlight Fehlerchecker zu haben. Dazu wird der Context des auslösenden Controls mitgebeben.
Public Class regeln
Public Shared Function hannesValid(ByVal region As String, ByVal ctx As ValidationContext) As ValidationResult
If region.Length > 2 Then
Return New ValidationResult("nur 2 Zeichen", New String() {ctx.MemberName})
Das alles und viel mehr lernt man in meine Silverlight Schulungen bei ppedv.