WCF Services in Silverlight einbinden

Wieder einmal eine Frage aus meinem Silverlight Kurs. Der Teilnehmer will zwei WFC Services in ein Projekt einbinden. Dazu verwendet er das Werkzeug slsvcutil um die Proxy Klassen zu erzeugen. Ich würde das einfach per Visual Studio und durch hinzufügen einer Service Reference lösen. Im Silverlight Projekt Service Referenz hinzufügen. Man erhält dann eine Menge Dateien. Der erzeugte Code enthält dann auch die Propertys: <System.Runtime.Serialization.DataMemberAttribute()> _ Public Property Region() As String Get Return Me.RegionField End Get Set If (Object.ReferenceEquals(Me.RegionField, value) <> true) Then Me.RegionField = value Me.RaisePropertyChanged("Region") End If End Set End Property Zunächst also einmal der erste Versuch einen Proxy zu generieren. Das Tool befindet sich bei mir in C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Tools. Der Aufruf für mich als VBler slsvcutil http://localhost:1892/Service2.svc?wsdl /language:VB Un dann schon die ernüchternde Fehlermeldung. Process is terminated due to StackOverflowException Liegt daran das ich kein US System habe. Workaround durch anlegen der config Datei beschrieben hier. http://blogs.msdn.com/b/silverlightws/archive/2010/04/30/workaround-for-stackoverflowexception-when-using-slsvcutil-exe.aspx Die Proxy Klasse weist aber leichte Unterschiede auf. So werden Arrays statt ObservableCollection erzeugt. Keine InotifyPropertychanged Interfaces Implementiert und es gibt keine automatischen Namensraum Anpassungen. Das kann durchaus erwünscht sein, da so die WCF Kommunikation etwas leicht gewichtiger wird. Der Code für ein Property sieht dann so aus im Vergleich zu vorher. <System.Runtime.Serialization.DataMemberAttribute()> _ Public Property Region() As String Get Return Me.RegionField End Get Set Me.RegionField = value End Set End Property   Die erzeugte Klassendatei wird einfach dem Silverlight Projekt hinzugefügt. Wenn man das ursprüngliche Verhalten möchte dann führt folgende Syntax zum Erfolg. slsvcutil http://localhost:1892/Service2.svc?wsdl /edb /namespace:"*,SilverlightApplication1.ServiceReference1" /ct:System.Collections.ObjectModel.ObservableCollection`1 /r:"%PROGRAMFILES%\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.Windows.dll" /language:VB Grundsätzlich sehe ich keine Grund, die Kommandozeilen Variante zu wählen.

Compact Docu zu Silverlight RIA Services DomainDatasource Control

Mit den Silverlight Ria Services kommt auch wieder eine Drag&Drop Experience auf uns Entwickler zu. Egal was man davon halten will, einen Blick ist es allemal Wert. Ich setze voraus das der geneigte Leser Ahnung von den WCF RIA Services hat. Einstiegspunkt ist die fertige Domainservice Klasse mit dem Namen DomainService1. In Visual Studio 2010 ist dann per Menüpunkt Data, ein neuer Arbeitsbereich zu öffnen. In Datasources müssten sich dann die vorher z.B. per Entity Framework modellierten Datenobjekte befinden. Per Drag und Drop kann man nun die ganze Tabelle auf das Silverlight Usercontrol gezogen werden. Dabei stehen die Optionen Datagrid oder Details zur Verfügung. Bei Detail werden alle Felder mit einem Label und Textbox Control dargestellt. Im Punkt Customize lässt sich auch jedes beliebige andere Silverlight Control festlegen. Auch einzeln können die Felder aufs Formular gezogen werden. Im weiteren Beispiel habe ich ein Datagrid per Drag und Drop aus der Datasource erstellt. Der unaufregende Teil ist das Datagrid selbst. <sdk:DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" ItemsSource="{Binding ElementName=CustomersDomainDataSource, Path=Data}" Margin="12,12,0,0" Name="CustomersDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="400"> <sdk:DataGrid.Columns> <sdk:DataGridTextColumn x:Name="AddressColumn" Binding="{Binding Path=Address}" Header="Address" Width="SizeToHeader" /> ..... </sdk:DataGrid> Obwohl auch da schon ein wenig drinsteckt. So wird die Itemssource per Binding zugewiesen. Dazu gleich mehr. Die einzelnen Spalten werden ebenfalls per Binding Syntax gebunden. Aber an was eigentlich? An ein anderes Control, wie man im per ElementName definierten Element to Element Binding erkennen kann. Diese Control befindet sich im Ria Toolkit und wird in diesem Fall automatisch per Namensraum bekannt gemacht. xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" Im Control DomainDataSource wird die Datenquelle definiert. Diese ist in diesem Fall als über einen Namensraum my auf die Klasse (hier eben Domainservice1 genannt) instanziert.  <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:Customers, CreateList=true}" Height="0" Name="CustomersDomainDataSource" QueryName="GetCustomersQuery" Width="0"> <riaControls:DomainDataSource.DomainContext> <my:DomainService1 /> </riaControls:DomainDataSource.DomainContext> </riaControls:DomainDataSource> Zusätzlich wird per Queryname die Methode bestimmt die aufgerufen werden soll. Diese finden sich im eigentlichen Service und wird im Attribut mit dem Zusatz Query versehen. Für den aufmerksamen Leser noch kurz ein Hinweis: DesignData ist optional und hilft zur Entwurfszeit in Expression Blend und Visual Studio einen Preview auf die Daten zu bekommen. Wenn die Abfrage Methode Parameter erwartet, werden die mit dem Element Queryparamters bestimmt. <riaControls:DomainDataSource.QueryParameters> <riaControls:Parameter ParameterName="CustomerID" Value="ALFKI" /></riaControls:DomainDataSource.QueryParameters> Das Attribut Value kann auch Werte von anderen Controls per Element Binding erhalten. Also z.B. eine TextBox als Eingabemöglichkeit für einen Filterwert. Generell kann man mit einem Filterdescriptor flexibel filtern. Der Filterwert wird in meinem Beispiel aus einer Textbox per Element Binding eingesteuert. Der Operator entspricht einer LINQ ähnlichen Syntax. <riaControls:DomainDataSource.FilterDescriptors> <riaControls:FilterDescriptor Operator="StartsWith" PropertyPath="CompanyName" Value="{Binding ElementName=TextBox1, Path=Text}" </riaControls:FilterDescriptor> </riaControls:DomainDataSource.FilterDescriptors> Wenn mehrere Filterdescriptor angegeben sind, werden die logisch per AND verknüpft. Wie bei Element Binding üblich wirkt die Abfrage sofort bei Änderung des Wertes in der Textbox. Sehr spannend ist wenn man per httpfiddler auf den Traffic schaut. GET /ClientBin/KoelnSL-Web-DomainService1.svc/binary/GetCustomers?$where= (it.CompanyName.ToLower().StartsWith(%2522%2522)%253d%253dTrue) Man sieht eine typische Rest Query. Die Daten selbst Binär codiert um die Datenmenge zu reduzieren. Die RIA Services übertragen also auch wirklich nur die Daten die von Client benötigt werden. Im Browser sieht das dann so aus. Die Query lässt sich aus noch ausbauen um z.B. eine Sortierung einzubauen. <riaControls:DomainDataSource.SortDescriptors> <riaControls:SortDescriptor PropertyPath="CompanyName" Direction="Ascending" /> </riaControls:DomainDataSource.SortDescriptors> Im Datagrid wird die sortierung auch angezeigt. Total abgefahren finde ich die Möglichkeit auch zu gruppieren. Das ist eine generelle Eigenschaft des Datagrid’s um auch z.B. Master Detail Szenarien zu lösen. In Zusammenarbeit mit der DomainDatasource geht das ganz einfach. Dazu gibt es das Element Groupdescriptor mit dem die Gruppierung festgelegt wird. <riaControls:DomainDataSource.GroupDescriptors> <riaControls:GroupDescriptor PropertyPath="Region" /> </riaControls:DomainDataSource.GroupDescriptors> Das sieht dann so aus. Eine Region (hier leer mit 60 items) habe ich zugeklappt Bei großen Datenmengen ist es natürlich besser nur wenige Datensätze zu holen. Dies ist der Benutzer gewohnt und kennt es als Paging. Das passende Control heist natürlich Datapager und befindet sich im Silverlight SDK. Ein Wermutstropfen ist, das zuerst im Dataservice eine sortierung in den Code eingebaut werden muss. Ansonsten kommt es zu seltsamen Fehlermeldungen zur Laufzeit.(The method skip is only supported for sorted input in Linq to entitys). In VB.NET sieht die Kurzform per Lambda Ausdruck. Public Function GetCustomers() As IQueryable(Of Customers)          Return Me.ObjectContext.Customers.OrderBy(Function(t) t.CompanyName) End Function C# ungefähr so public IQueryable<User> GetCustomers() { return this.ObjectContext.Customers.OrderBy(b => b.CompanyName); } Also wie gesagt, das in dem Web Projekt passieren. So nun aber auf zum Pager im XAML Code dort wird angegeben wie groß der Bereich sein soll. <sdk:DataPager PageSize="5" Source="{Binding ElementName=CustomersDomainDataSource, Path=Data}"/> Allerdings kann bzw soll man auch im DomainDatasource Control einstellungen vorgenommen werden. Interesannt ist das man mit Loadsize quasi schon ein paar Seiten vorladen, also buffern kann. <riaControls:DomainDataSource PageSize="5" LoadSize="10"... Die Browser Ansicht  Wichtig zu wissen: Das Paging wird per REST Querys durchgeführt. Es fließen also nur die benötigten Daten über die Leitung. Der Benutzer kann Daten auch ganz einfach editieren. Allerdings ist es dann mit dem Paging vorbei (hier plötzlich grau im Bild) und die Daten wandern auch nicht von ganz alleine über den Domainservice in die Datenbank. Dafür braucht man doch einen Softwareentwickler, der eine Zeile Code schreibt und submitChanges aufruft. Wo man das tut, ist eine Entscheidung des Anwendungsfalles. In meinem Beispiel wird die Methode RowEditEnded des Datagrids verwendet. Private Sub CustomersDataGrid_RowEditEnded(ByVal sender As Object, ByVal e As System.Windows.Controls.DataGridRowEditEndedEventArgs) CustomersDomainDataSource.SubmitChanges() End Sub Was soll man sagen? Es funktioniert und ist ziemlich einfach. Allerdings ist es sozusagen die Version 1. Man wird sehen.

Twitter Search API mit Silverlight befragen

Im Zuge der Österreichischen Sharepoint Konferenz, die wir von ppedv im Auftrag von Microsoft veranstaltet haben, habe ich eine Twitterwall geschrieben. Twitter besitzt drei API’s wovon die Search API die einfachste ist. Im folgenden Beispiel suchen wir alle Tweets mit dem Hashtag Silverlight. http://search.twitter.com/search?q=%23silverlight Um die Daten im Atom Format zu bekommen muss die Syntax leicht verändert werden mit dem ATOM Zusatz http://search.twitter.com/search.atom?q=%23silverlight Atom ist ein XML Format und entsprechend schwergewichtig. Wesentlich schlanker ist Json. Mit folgender Query erhält man diese Daten als JSon. http://search.twitter.com/search.json?q=%23silverlight Dabei reduziert sich der Traffic auf ein Drittel. Auch das Parsen müsste deutlich schneller sein. Aktuell habe  ich aber keine Performance Messungen vorgenommen. Nun laden wir diese Daten in einer Silverlight Anwendung mit einem WebClient. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click Dim wc As New WebClient AddHandler wc.DownloadStringCompleted, AddressOf fertig wc.DownloadStringAsync(New Uri("http://search.twitter.com/search.json?q=%23silverlight&lang=all&rpp=5", UriKind.Absolute)) End Sub In der asynchronen Methode Fertig werden dann die Daten bearbeitet. Da gibt es in Silverlight (und nur dort) eine nettes Assembly System.Json. Per JSonObject wird die Zeichenkette geladen und dann alle “Results” Elemente/Objekte in ein JsconArray gepackt. Per Linq könnte man dann noch filtern, was in diesem Beispiel nach der From Anweisung per Where codiert würde. Dann werden die Items dem Datagrid zugewiesen. Private Sub fertig(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs) Dim js As JsonArray Dim jo As JsonObject jo = JsonObject.Parse(e.Result) js = jo("results") Dim items = From x In js Select x DataGrid1.ItemsSource = items End Sub Um die deklarative Bindung durchzuführen muss man noch ein wenig in die Objekte reinsehen. Items ist ein Dictionary von JsonObject. Ein JsonObject wiederum ist eine unsortierte Collection von Key Value Paaren. Durch Twitter Json stehen da elf Werte drin über die der Debugger Auskunft gibt. Neben ID ist das wichtigste der eigentliche Text. Entsprechend die Bindung in XAML per IndexBinding an den Key “text”. <sdk:DataGrid AutoGenerateColumns="False" Height="217" HorizontalAlignment="Left" Margin="12,37,0,0" Name="DataGrid1" VerticalAlignment="Top" Width="379" > <sdk:DataGrid.Columns> <sdk:DataGridTextColumn Binding="{Binding Path=[text]}"></sdk:DataGridTextColumn> </sdk:DataGrid.Columns> </sdk:DataGrid>      

Websites fr alle Silverlight Flle vorbereiten

Aktuell habe ich das ppedv Logo auf der ppedv Startseite durch eine Silverlight Anwendung ersetzt um zu Marketing für unsere kommen Twittertage zu machen. Von Donnerstag bis Sonntag gibts per #ppedv Twittertag Hammeraktionen. Natürlich soll die Website für alle Kunden nach wie vor das Logo präsentieren. Silverlight 4 ist installiert. Da die Anwendung per Silverlight 4 entwickelt wurde ist das der einfachste Fall. Einfach den Object Tag in den HTHL Code einfügen. Da die Silverlight Anwendung über den HTML Inhalt liegen soll muss der Hintergrund in XAML transparent sein. Im Object Element braucht man zwei Attribute windowless und background. <object style="float: left;" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="400" height="100"> <param name="source" value="/ClientBin/ppedvlogo.xap"/> <param name="windowless" value="true"/> <param name="background" value="transparent" /> <param name="minRuntimeVersion" value="4.0.50401.0" /> </object> Silverlight ist nicht installiert Um zu testen was passiert wenn Silverlight nicht installiert ist, kann man einfach aus dem Wert x-Silverlight-2 einen anderen z.B. x-silverlight-3 machen. Da Silverlight von Version 2-4 immer mit den gleichen Eintrag referenziert wird, wird so auf ein unbekanntes Plugin verwiesen. Was ident mit keinem installierten Silverlight ist. Dann kann man ein alternatives HTML Template innerhalb des Object Elements definieren. In meinem Fall das ppedv Logo als png. Um zu verhindern das ein Dialog hochkommt der den Benutzer zum installieren von Silverlight auffordert, habe ich autoupgrade auf false gesetzt. Ist ja schliesslich nicht unser Job Silverlight auszurollen. <object style="float: left;" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="400" height="100"> <param name="source" value="/ClientBin/ppedvlogo.xap"/> <param name="windowless" value="true"/> <param name="background" value="transparent" /> <param name="minRuntimeVersion" value="4.0.50401.0" /> <param name="autoUpgrade" value="false" /> <img src="http://www.ppedv.de/Images/ppedv_sternelogo.png" border="0" /> </object> Silverlight ist in der falschen Version installiert Der kompliziertere Fall ist wenn Silverlight vorhanden ist aber z.B. das Plugin Version 2 hat und die XAP Anwendung mit Silverlight 3 oder 4 geschrieben ist. Das kann man ganz nett simulieren indem man in das Attribute minRuntimeVersion einen Wert 5.0 reinschreibt. Silverlight 5 gibt es schlicht noch nicht. Dann kommt in einer Standard Anwendung ein JScript Exception. Auch dies unpassend für die ppedv Webseite. In diesem Fall habe ich den Object Tag in ein DIV gepackt und tausche dann schlicht per Jscript den Inhalt durch ein HTML Image Element aus. Silverlight kann per Attribut onError festlegen welche Jscript Methode im Fehlerfall aufgerufen werden soll. <div id="silverlightControlHost"> <object style="float: left;" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="400" height="100"> <param name="source" value="/ClientBin/ppedvlogo.xap"/> <param name="windowless" value="true"/> <param name="background" value="transparent" /> <param name="minRuntimeVersion" value="4.0.50401.0" /> <param name="autoUpgrade" value="false" /> <param name="onError" value="onSilverlightError" /> <img src="http://www.ppedv.de/Images/ppedv_sternelogo.png" border="0"/> </object> </div> Dann noch ein kurzes Stück JavaScript das den Fehler Code 8001 behandelt. <script type="text/javascript"> function onSilverlightError(sender, args) { var iErrorCode = args.ErrorCode; if (iErrorCode==8001) { document.getElementById("silverlightControlHost").innerHTML = '<img style="float: left;" src="http://www.ppedv.de/Images/ppedv_sternelogo.png" border="0" />'; } } </script>

Expression Blend 4 und Storyboards

Mein Kollege Bernhard meint zwar das er Storyboards lieber in XAML Code tippt aber ich verlasse mich da besser auf das passende Werkzeug: Expression Blend. Dies ist ja nun in Version 4 verfügbar. Dieses mal möchte ich kurz zeigen wie man Storyboards steuern kann ganz ohne Code. Ich gehe mal davon aus das die grundlegende Kenntnisse vorhanden sind. Falls nicht einfach Mail an hannesp AT ppedv.de senden und ich werde ein Einführungs Post nachschieben. Hier geht es um das Starten einer Animation und was nachher passieren soll. ControlStoryBoardAction Aus den Bereich Assets finden sich einige Blend typische Behavoirs. Mit diesen kann ein Designer ohne Code das Verhalten einer Benutzer Schnittstelle steuern. Um ein Storyboard auf eine definierte Benutzer Aktion in Silverlight zu starten verwendet man ControlStoryBoardAction. Diese Action zieht man per Drag Drop einfach auf das Control das als Steuerung dienen soll. In diesem Beispiel die ganze Silverlight Anwendung per Layoutroot. Dann wird in dem Eigenschaft Dialog festgelegt welche Aktion ( hier MouseleftbuttonDown) welches Storyboard (hier Storyboard1) steuern soll und letztendlich welche Aktion ausgeführt werden soll. Das war's schon. Keine Zeile Code nötig. In den Triggern gibt es auch noch ein paar ganz abgefahrene. So auch einen der das Event behandelt wenn das Storybord fertig ist. RemoveElementAction Im nächsten Teil betrachten wir wenn ein Storyboard zu Ende ist, also ein finished Event feuert. In meiner Anforderung steht das ein neu erzeugtes UIElement eine Animation ablaufen lassen soll und dann anschliessen spurlos aus dem Controltree entfernt werden soll. Das kann man aufwendig per Code erledigen oder per Maus und Expression Blend. Dafür kommt das RemoveElementAction Behavior zum Einsatz. Einfach per Drag und Drop reinziehen. Dann wird noch ganz ähnlich wie vorher beschrieben gesteuert was wann wie entfernt werden soll. In meinem Fall soll nach Ende der Animation das animierte Objekt (hier Ellipse) verschwinden.   Für mich einfach nur genial.

Timespan in XAML binden

Ein Teilnehmer meiner Silverlight Schulung bei ppedv schickt mir soeben eine Frage per Mail mit einem Stück XAML Code. Warum klappt die Datenbindung nicht? Das Ergebnis ist nichts. Also keine Fehlermeldung oder Exception sondern einfach nichts. Ich liebe solche Bugs *Ironie*. Das beste ist dann noch : “ bei mir gehts aber”. Was es in der Tat auch tut. Im wesentlichen versucht der Silverlight Entwickler mittels eines eingebauten TypeConverters ein Datum zu binden. Glaubte ich jedenfalls. Nach einigen hin und her, stellte sich heraus das nicht DateTime sondern vom Typ Timespan gebunden werden sollte. Das habe ich in der Tat noch nie probiert. In Silverlight sind eine Reihe solcher Converter in System.Component Model vorhanden. Unter anderem auch ein TimespanConverter. Nur leider scheint der von einer anderen Person Programmiert worden zu sein, als der DatetimeConverter. Siehe auch meinen Blog Eintrag zu Databinding mit Datumswerten. In der Doku finde ich dazu nichts. Als Silverlight Insider habe ich aber ganz gute Drähte zu den Microsoft Entwicklern direkt. Da habe ich auch nachgefragt. Die Lösung ist, das man den Doppelpunkt, oder andere Sonderzeichen, mit zwei Backslashes Escapen muss. Das sieht dann so aus. <TextBlock Text="{Binding genauesAlter,StringFormat=hh\\:mm}" Width="300" Height="30" Margin="0,66,67,204"/> <TextBlock Text="{Binding genauesAlter,StringFormat=\{0:hh\\:mm\}}" Width="300" Height="30" Margin="0,66,67,204"/> Beide Varianten sind möglich. Ansonsten ist die Syntax aus den Datetime Format Expressions entnommen. Wie man so einen Converter selbst baut steht hier übrigens beschrieben http://msdn.microsoft.com/en-us/library/cc645047(VS.95).aspx

Datum formatieren

In diesem Blog gehe ich kurz auf die Neuen Formatierungsmöglichkeiten in der Datenbindung bei Silverlight 4 ein. Früher (also Silverlight 3 ) müsste man in der Regel einen Converter implementieren und bei der Bindung angeben um Datumswerte passend zu formatieren. Nun gibt es das Attribut Stringformat das auf die in .NET üblichen Formatexpressions zurück greift. Formatausdrücke (Formatexpressions) http://msdn.microsoft.com/de-de/library/dwhawy9k(v=VS.80).aspx Datum und Zeit Formatausdrücke http://msdn.microsoft.com/de-de/library/az4se3k1(v=VS.80).aspx Für mein Beispiel binde ich eine Person Klasse an das Layoutroot UIElement. Private Sub page31_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded Dim p As New person With {.datum = Date.Now} LayoutRoot.DataContext = p End Sub Dann wird in XAML zb die verkürzte Zeit Syntax verwendet. <TextBlock Text="{Binding datum,StringFormat=t}" Width="300" Height="30" Margin="0,0,67,258"/> Wenn nun das Ergebnis ein Englisches Format ist lässt sich durchaus überraschend über das Attribut ConverterCulture ein anderes Culture Schema erzwingen. <TextBlock Text="{Binding datum,StringFormat=t,ConverterCulture=de-DE}" Width="300" Height="30" Margin="0,0,67,258"/> Alternativ kann man die formatierung natürlich auch direkt maskieren. <TextBlock Text="{Binding datum,StringFormat=mm:ss}" Width="300" Height="30" Margin="0,0,67,258"/> Für Sonderzeichen die z.B. in XAML enthalten sind müssen die entsprechenden Entitys eingesetzt werden. <TextBlock Text="{Binding datum,StringFormat=mm&gt;ss}" Width="300" Height="30" Margin="0,0,67,258"/> Es scheint auch eine beinahe grundlegend andere Syntax zu funktionieren, die man meist in WPF Dokus findet. <TextBlock Text="{Binding datum,StringFormat=\{0:t\}}" Width="300" Height="30" Margin="0,0,67,258"/> Jedenfalls gehts bei mir in Silverligth 4. Alle Samples sind in VB.NET und C# getestet. Dies und anderes in meinen Schulungen und Kursen zu Silverlight, bzw auf der APPs-konferenz.

mehrere Notification Fenster gleichzeitig

Die Anwort ist, geht nicht. Silverlight 4 bringt zwar für OOB Anwendungen die Möglichkeit ein Popup im Bereich Tray Icons zu zeigen, aber eben nur eines. Wenn man ein zweites Notification Window per Show anzeigen möchte erntet man eine Exception. Die Lösung ist eine Queue zu bauen, etwas was ich seit ca 10 Jahren nicht mehr gemacht habe. In .NET ist das aber super einfach. Hier unkommentiert das komplette Code Beispiel. Um zuverlässig mehr Toasts erzeugen zu können als angezeigt werden habe ich einen Timer verwendet der jede Sekunde eine Notification Window anzeigt das 2 Sekunden steht. Imports System.Windows.Threading Partial Public Class page30 Inherits UserControl Public Sub New() InitializeComponent() End Sub Dim dp As New DispatcherTimer Private nwQueue As Queue(Of NotificationWindow) Private nwIsopen As Boolean = False Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click nwQueue = New Queue(Of NotificationWindow)() dp.Interval = New TimeSpan(0, 0, 1) AddHandler dp.Tick, AddressOf ticking dp.Start() End Sub Private Sub ticking(ByVal sender As Object, ByVal e As EventArgs) Dim nw As New NotificationWindow nw.Width = 150 nw.Height = 30 nw.Content = New TextBlock With {.Text = "hannes" + Date.Now.Second.ToString} AddHandler nw.Closed, AddressOf nwClosed If Not nwIsopen Then nw.Show(2000) nwIsopen = True Else nwQueue.Enqueue(nw) End If End Sub Private Sub nwClosed(ByVal sender As Object, ByVal e As EventArgs) If nwQueue.LongCount = 0 Then nwIsopen = False Else Dim nw As NotificationWindow = nwQueue.Dequeue() nw.Show(2000) nwIsopen = True End If End Sub End Class

Silverlight ChildWindow chirurgisch entfernen

Aufgestachelt durch die neuesten Erkenntnisse, das der Title im Silverlight ChildWindow den Namen Chrome hat, habe ich etwas versucht. Nicht das ich jemals so UI’s bauen würde. Es ging mir einfach um das Machbare. Mittels des VisualTreehelpers lässt sich in Silverlight jedes UI Element finden. So ähnlich wie bei HTML DocumentGetElementbyID. Ist zwar nicht die schnellste Methode, aber es geht. In meinem vorigen Post haben ich herausgefunden das die Title Bar im Child Window ein Border Element mit dem Namen Chrome ist. Deshalb habe ich folgendes probiert und es geht. Private Sub ChildWindow3_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded Dim chrome As FrameworkElement = VisualTreeHelper.GetChild(Me, 0) CType(chrome.FindName("Chrome"), UIElement).Visibility = Windows.Visibility.Collapsed End Sub Vor Nachahmung wird gewarnt.

Silverlight ChildWindow in sein Template zerlegen

Das Childwindow ist ein quasi modaler Dialog. In einem Silverligth Projekt ist dieses ein eigener Dateityp. In meinen aktuellen Silverlight Kurs kam die Frage auf wie man die Titelzeile entfernen kann. Ich wusste das man per Attribut Titel den Text und per HaseCloseButton=false das Clsoing Icon entfernen kann, aber wie man direkt die TitleBar (auch Chrome) genannt entfernt hatte ich vorher noch nie gemacht. Und so hole ich dies für meinen Schulungsteilnehmer auf diese Art nach. Am einfachsten gehts es wohl mit Expression Blend. Dort kann man per Context Menü das Template des ChildWindow erzeugen. Bitte beachten Sie das auch Childwindow markiert sein muss im Objects und Timeline Explorer von Expression Blend. Dann naviegiert man z.b. über das Bread Crump Menü oben, in das Template. EInfach das UIElement Chrome entfernen. Ein auf diese Art erzeugtes Style kann auch in APP.XAML oder eine Resource Dictonary ausgelagert werden und auf weitere Silverlight Childwindows verwendet werden. Diese und viele weitere Tricks werde ich auch auf der APPs konferenz im Juni präsentieren. Dort wird Silverlight und Blend ein ganz starkes Thema.