C# 8.0 – Ein Überblick über neue Features und Funktionen

Vor nicht allzu langer Zeit, genau genommen Ende September 2019, hat Microsoft die lange angeküdigte [Mehr]

Lambda Expressions und Linq

In einem letzten Artikel (Lambda Expressions) habe ich mich mit dem Zusammenhang zwischen Delegates [Mehr]

Erweiterungsmethoden

Erweiterungsmethoden (Extension Methods) in C# sind ein sehr spannendes Thema wenn es darum geht vor [Mehr]

Lambda Expressions

Mein Kollege Michael Zöhling hat vor ein paar Tagen einen Eintrag geschrieben der sich mit dem Thema [Mehr]

WPF Datagrid Background abhängig von ausgeblendeter Spalte

Letztens wurde ich von einem ehemaligen Kursteilnehmer gefragt, wie man in WPF die Werte einzelner Z [Mehr]

Xaml Binding–Relative Sources

Wenn man mit WPF Oberflächen gestaltet kommt man nicht um Binding Anweisungen herum. Meistens sind [Mehr]

Ganzer Bildschirm Voller Screen

Es ist ein leichtes eine Silverlight Anwendung in den FullScreen Modus zu schalten. Application.Current.Host.Content.IsFullScreen = True Es gibt allerdings Einschränkungen. So kann der Benutzer keine Texteingaben mehr durchführen und man muss diese Codezeile in  ein vom Benutzer initiiertes Event legen. Button Click ist so eines. Den FullScreen kann der Benutzer mit ESC wieder verlassen. Ausnahme ist wenn man per ALT TAB (oder auch sonst wie) die aktive Anwendung wechseln möchte. Dann ist der Fullscreen  Modus ganz von alleine weg. Wenn man dies verändern möchte hilft folgendes Silverlight Beispiel Code Application.Current.Host.Content.FullScreenOptions = System.Windows.Interop.FullScreenOptions.StaysFullScreenWhenUnfocused Der Silverlight User erhält nun einen Dialog ob er dieses Verhalten erlauben möchte und kann seine Antwort auch dauerhaft abspeichern. Wenn die Silverlight Anwendung OOB ( Out Off Browser) mit elevated Priviliges läuft, kommt der Dialog nicht und der Benutzer kann sogar Tastatureingaben durchführen. Der Zoom Faktor des Browser wird übrigens im Vollbild Modus ignoriert. Wenn man das wissen und nutzen möchte, gibt es im Content.ZoomFaktor den Vergrößerungswert als Double zurück. Dies und noch viel mehr lernen Sie bei den Silverlight Schulungen der ppedv.

Anonyme Typen an ein Silverlight Datagrid binden

Was soll ich sagen, man lernt nie aus. In meiner aktuellen Silverlight Schulung saß ein Kurs Teilnehmer der es genau wissen wollte. Mein Silverlight Beispiel zeigt wie man mit minimalen Aufwand einen RSS Feed (hier n-tv) an ein Datagrid bindet. Dafür braucht man eine Klasse (hier rss) mit Eigenschaften (hier title) die Public sind. Struct geht schon mal nicht. Der sehr einfache Silverlight Prototyp: 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("xmlfile1.xml", UriKind.Relative)) End Sub Private Sub fertig(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs) Dim xml = XDocument.Parse(e.Result) Dim query = From x In xml.Descendants("item") Select New rss With {.titel = x.<title>.Value} DataGrid1.ItemsSource = query End Sub End Class Public Class rss Public Property titel As String End Class Die Ausgabe in der Silverlight Anwendung dann ungefähr so. Der liebe Silverlight Kurs Teilnehmer war faul und wollte es mit anonymen Typen versuchen also ohne die RSS Klasse zu erstellen ala Dim query = From x In xml.Descendants("item") Select New With {.titel = x.<title>.Value} DataGrid1.ItemsSource = query Das Ergbebnis dann Meine Antwort ist halt so. Anonym geht eben nicht da diese als internal deklariert sind und deshalb nicht sichtbar. Da mein Teilnehmer mit Testing ganz versiert war wusste er das man innen nach außen kehren kann mit InternalsVisibleTo. Dies muss man in der Datei AssemblyInfo.vb deklarieren. Üblicherweise befindet sich diese im Verzeichnis Properties der Silverlight Anwendung. Wenn nicht vorhanden einfach anlegen und folgendes reintippen. <Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.Windows")> Und schwupp gehts auch anonym.

MP3 download mit Silverlight

Nein dies ist kein Filesharing Anleitung um illegale MP3 Musik zu downloaden. Ich schreibe grad einen Vokabeltrainer für meinen Sohn. Dabei soll auch die Aussprache trainiert werden. Leo.org bietet neben Übersetzung auch den Service den Englischen Text vorzulesen. Für meine Lernanwendung möchte ich aber gelernte Vokabel speichern. Also das MP3 per Download im Isolatedstorage speichern. Die Download URL ist relativ simpel das gesprochene Wort mit der Endung mp3. Mittels dem Webclient wird ein asynchroner Download gestartet. Da es sich um binäre Daten handelt macht ein Stream mehr Sinn. Diesen erhält man per Openreadcompleted. Dim url As String = "http://www.leo.org/dict/audio_en/" + txtenglisch.Text + ".mp3" Dim wc As New WebClient AddHandler wc.OpenReadCompleted, AddressOf mp3fertig wc.OpenReadAsync(New Uri(url, UriKind.Absolute))) Wenn nun der Download der MP3 Datei fertig ist, muss man sich überlegen wie man die Datei speichert. Da gibt's im wesentlichen nur die Methode im IsolatedStorage. Für möglichst kurzen Code hole ich mir die Länge des Streams über einen kleinen Umweg StreamResourceInfo. Dann muss man Binär lesen und schließlich Binär schreiben. Private Sub mp3fertig(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs) Dim srInfo = New StreamResourceInfo(e.Result, Nothing) Dim sr As BinaryReader = New BinaryReader(srInfo.Stream) Dim iso As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using iosr As IsolatedStorageFileStream = iso.OpenFile(txtenglisch.Text + ".mp3", FileMode.OpenOrCreate) Using bw As BinaryWriter = New BinaryWriter(iosr) bw.Write(sr.ReadBytes(srInfo.Stream.Length)) End Using End Using sr.Close() End Sub Schließlich noch der Hinweis. Wenn der Platz  (1 MB) nicht reichen sollte, kann dieser vergrößert werden. Der Benutzer muss das aber noch bestätigen. IncreaseQuotaTo(1000000)

Silverlight Socket Client

In einem früheren Post habe ich beschrieben, wie man einen einfachen Socket Server baut. Dieses mal will ich den Service konsumieren. Eine Socket Service Kommunikation ist extrem effizient. Allerdings müssen die verwendeten Ports in allen beteiligten Firewalls auch frei geschalten sein. Silverlight auferlegt sich zudem eine künstliche Beschränkung auf den Bereich 4502-4534. Noch viel schwerwiegender ist, das man bei der TCP Socket Kommunikation sehr schnell in Cross Domain Szenarien landet. Dann erhält man Security Exceptions, außer die Anwendung läuft OOB full trusted. Für allen jene denen diese Beschreibung noch nicht verwirrend genug ist: Wenn die Anwendung im Browser läuft muss eine ClientAccessPolicy.XML Datei angelegt werden, die dann von Silverlight ungefragt angefordert wird. Format und Inhalt ungefähr so. <?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*" /> </allow-from> <grant-to> <socket-resource port="4502-4534" protocol="tcp" /> </grant-to> </policy> </cross-domain-access> </access-policy> Der vorläufige Höhepunkt ist, das Silverlight ab Version 4 versucht diese Datei auf Port 943 anzufordern. Dafür müsste man nun noch einen extra Socket Server schreiben der auf diesem Port lauscht und die Datei überträgt. Ist mühsam und deswegen gibt es wohl nun die Zweitmöglichkeit die Datei in das root des Webservers zu legen und per http auszuliefern. Allerdings muss man Silverlight das im Code noch mitteilen (SocketClientAccessPolicyProtocol.Http) wie wir gleich im folgenden Code sehen werden. Erlaubt sei noch der Hinweis das dies mit dem WebDev Server aus Visual Studio nicht geht, weil dieser auf einen zufälligen Port lauscht und nicht auf 80. Weiter gehts mit der Initialisierung der Socket Verbindung.   Die Sache mit der IP Adresse ist ein wenig mühsam und muss über den DNS Endpoint gelöst werden um rauszukriegen auf welcher Website die Anwendung läuft. Am Ende wird versucht die Verbindung herzustellen. Dim sock As Socket Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click Dim ep As DnsEndPoint = New DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4506) sock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) Dim args As SocketAsyncEventArgs = New SocketAsyncEventArgs() args.RemoteEndPoint = ep args.SocketClientAccessPolicyProtocol = SocketClientAccessPolicyProtocol.Http 'Clientaccesspolicy AddHandler args.Completed, AddressOf onSocketConnect sock.ConnectAsync(args) End Sub Da das ganze asynchron läuft, wie bei Silverlight üblich, benötigt man die Funktion onSocketConnect, die aufgerufen wird sobald die Verbindung besteht. Jetzt wirds ein wenig Tricky. Um die eingehenden Daten zu erhalten, brauchen wir noch ein zweites Event OnSocketEmpfang. Da dies auch am Completed Event der SocketArgs hängt, habe ich die Events durch deregistrieren und registrieren ausgetauscht. Ob dies der beste Weg ist, ist mir nicht bekannt. Zumindest habe ich auf an anderer Stelle ähnliche Ansätze gefunden. Nun gehts ans Empfangen mit ReceiveAsync. Private Sub onSocketConnect(ByVal sender As Object, ByVal e As SocketAsyncEventArgs) If sock.Connected Then RemoveHandler e.Completed, AddressOf onSocketConnect Dim resp(1024) As Byte e.SetBuffer(resp, 0, resp.Length) AddHandler e.Completed, AddressOf onSocketEmpfang sock.ReceiveAsync(e) End If End Sub Wenn dann ab und zu mal Sekunden in der Form 40 oder so eintrudeln, landen wir im OnSocketEmpfang. Die Daten stehen zwar freundlicherweise im e.Buffer, aber leider befinden wir uns im Background Thread. Entsprechend muss per BeginInvoke ein Delegate definiert werden die eine Methode aufruft die im UI Thread läuft.  Dabei werden die Argumente e als Parameter übergeben. Schlussendlich rufe ich wieder den ReceiveAsync Befehl auf, weil die Daten regelmäßig bis zum Sankt Nimmerleinstag kommen. Private Sub onSocketEmpfang(ByVal sender As Object, ByVal e As SocketAsyncEventArgs) Dispatcher.BeginInvoke(New Action(Of SocketAsyncEventArgs)(AddressOf updateUI), e) sock.ReceiveAsync(e) End Sub Der Vollständigkeit halber noch der Teil der letztendlich eine TextBox im Frontend aktualisiert. Private Sub updateUI(ByVal e As SocketAsyncEventArgs) TextBlock1.Text = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred) + Environment.NewLine End Sub Wer keine Erfahrung mit dem synchronsieren von Threads hat und das für Teufelszeug hält, soll mal in die app.xaml.vb oder app.xaml.cs blicken. Dort wird das globale Fehlerhandling genau so realisiert. Mit Hilfe einer Lambda Expression noch etwas eleganter codieren, obs lesbarer ist mag jeder selbst entscheiden. Dispatcher.BeginInvoke( New Action(Of SocketAsyncEventArgs)(Sub() TextBlock1.Text += Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred) + Environment.NewLine End Sub), e)