WinRT Stream Write Api

In einem früheren Blog Artikel zu TCP/IP Socket Kommunikation mit C# oder VB.NET unter Windows 10 wurde folgendes Code Schnipsel verwendet um auf einer offenen Verbindung eines Servers eine Antwort an den Client zu senden. 1: Using raus As Stream = args.Socket.OutputStream.AsStreamForWrite() 2: Dim wr As StreamWriter = New StreamWriter(raus) 3: Await wr.WriteLineAsync("FILEOK") 4: Await wr.FlushAsync() 5: 'End Using .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Args ist ein Objekt vom Typ StreamSocketListenerConnectionReceivedEventArgs und damit reinrassig aus der WinRT. Um den Typ für .net behandelbar zu machen, wird die Methode AsStreamForWrite verwendet. Dieser Bruch hat mich unzufrieden zurück gelassen und ich habe versucht den Weg der API direkt zu nehmen. In der Tat hat IOutputStream eine Methode WriteAsync, die aber ein Objekt vom Typ IBuffer erwartet. Um zu vermeiden, das wir die Prozessgrenzen zwischen managed Code und dem COM Objekt überschreiten, wird eine Windows Funktion aus der Crypto API verwendet. Dieses Objekt bietet eine Methode um aus einer Zeichenkette ein Binary erstellen zu können. Da man das Encoding als Parameter angeben kann, klappt aber auch Zeichenkette. So sollte das Marshalling überflüssig werden. 1: Await args.Socket.OutputStream.WriteAsync(CryptographicBuffer.ConvertStringToBinary("FILEOK" + Environment.NewLine, BinaryStringEncoding.Utf8)) 2: Await args.Socket.OutputStream.FlushAsync() .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Viele Code Beispiele wählen in solchen Szenarien den Weg über ein Byte Array, was zwar funktioniert und auch leicht zu debuggen ist, aber den Arbeitsspeicher der Laufzeitumgebung unnötig belastet. Überflüssig zu erwähnen das auch der Garbage Collector mehr Aufwand fürs aufräumen benötigen wird. .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

UWP Socket File Transfer

Windows 10 erbt die WinRT API aus Windows 8. Diese API wurde als Hybrid aus Technologien von COM und JavaScript zusammengesetzt. Darauf wurde ein .NET Framework Profil gepfropft. Was sich schon etwas komplex anhört, erschwert in der Tat die Programmierung. Dieser Blog Artikel erläutert am VB.NET Prototyp einen Datei Transfer per TCP/IP. Alle WinRT basierten API beginnen befinden sich im Namensraum Windows, alles was .net bietet beginnt mit System. Dabei gibt es häufig funktionelle Doppelgleisigkeiten, die meist nicht kompatibel sind. Für den Brückenschlag dienen dann meist Extension Methoden wie AsStreamForRead. 1: Imports System.Text 2: Imports System.Net.Sockets 3: Imports Windows.Networking 4: Imports Windows.Networking.Sockets 5: Imports Windows.Storage 6: Imports Windows.Storage.Streams .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Für einen TCP/IP basierenden Filetransfer brauchen wir einen TCP Server, auch listener genannt. Obwohl system.net.sockets referenziert ist, lässt sie .net 4.5 tcplistener Klasse nicht instanziieren. Die Windows Runtime nennt diese StreamSocketListener. Dabei erhält man einen Stream vom Typ IInputStream. Erste Einführung dazu in der MSDN Dokumentation. Allerdings ist es auf Windows 10 Maschinen nicht möglich Socket Connections von App zu App innerhalb des Computers auszuführen. Einzig im Debug Modus und innerhalb der gleichen App gibt es keine Probleme. Tiefer gehende Informationen zu Loop Back Exempt. Der Server wird per Button Click gestartet. Ein Streamsocket Listener lauscht auf Port 2000 und feuert bei Datenankunft ein Event OnConnection. 1: Private listener As StreamSocketListener 2:   3: Private Async Sub button_Click(sender As Object, e As RoutedEventArgs) 4: listener = New StreamSocketListener() 5: AddHandler listener.ConnectionReceived, AddressOf OnConnection 6: Await listener.BindServiceNameAsync("2000", protectionLevel:=SocketProtectionLevel.PlainSocket) 7: End Sub .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Bereits der Verbindungsaufbau eines Client löst das Event aus. Der Code wird allerdings in einem seperaten Thread ausgeführt, so das eine Rückmeldung zum User Interface per Dispatcher erfolgen muss. 1: Private Async Sub OnConnection( 2: sender As StreamSocketListener, 3: args As StreamSocketListenerConnectionReceivedEventArgs) 4:   5: Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, 6: Sub() 7: textBox.Text = args.Socket.Information.RemotePort 8: End Sub) .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Der Verbindungsablauf im  Beispiel Server startet und wartet auf Verbindung Client baut Verbindung auf Server sendet “FILEOK” Client sendet Bild Server speichert Bild Als Fortsetzung zum vorhergehenden Code Beispiel aus dem OnConnection Event, wird der Output Stream auf der aktuellen Connection genutzt. Da von der WinRt APi kommend handelt es sich um ein COM Objekt vom Typ IOutputStream. Mit der Extension Methode AsStreamForWrite wird daraus ein .net Stream Objekt. Erst mit dem Streamwriter kann darin auch geschrieben werden.  1: Using raus As Stream = args.Socket.OutputStream.AsStreamForWrite() 2: Dim wr As StreamWriter = New StreamWriter(raus) 3: Await wr.WriteLineAsync("FILEOK") 'Readline 4: Await wr.FlushAsync() 5: End Using .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Theoretisch könnte man auch direkt auf dem WinRT Objekt in der Form args.Socket.OutputStream.WriteAsync(CryptographicBuffer.ConvertStringToBinary("FILEOK", BinaryStringEncoding.Utf8)) geschrieben werden. Hat bei mir leider nicht geklappt. Da noch immer das OnConnection Event codiert wird, muss noch Schritt 5, das speichern des Bildes erledigt werden. In diesem VB.NET Prototypen wird einfach ein Bild mit dem Namen ZZZZZ angelegt. Auch StorageFile ist ein WinRT Objekt bzw. der dazu nötige Stream. Durchaus verwirrend kommt nun ein drittes Objekt zum Einsatz das einen Stream in den anderen Stream kopiert. Kein Writer, kein Flush, dafür alles über die WinRT API. 1: Dim file As StorageFile = Await KnownFolders.PicturesLibrary.CreateFileAsync("ZZZZZZZZZZZ.png", CreationCollisionOption.ReplaceExisting) 2: Using outputStream As IOutputStream = Await file.OpenAsync(FileAccessMode.ReadWrite) 3: Await RandomAccessStream.CopyAndCloseAsync(args.Socket.InputStream, outputStream) 4: End Using .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Der Client verwendet eine StreamSocket Instanz und verbindet sich zum Server auf Port 2000. 1: Private Async Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click 2: Dim client As StreamSocket 3: client = New StreamSocket() 4: Await client.ConnectAsync(New HostName("127.0.0.1"), "2000") .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Da der Server unmittelbar seine Nachricht sendet, wird diese in der nächsten Code Zeile auch gelesen. Allerdings muss hier der Stream aus der WinRT Welt mit AsStreamForRead wieder in einen .net Stream gecastet werden. Dafür reicht dann einen Zeile um die Antwort des Servers zu lesen (FILEOK). 1: Using reader As StreamReader = New StreamReader(client.InputStream.AsStreamForRead()) 2: Dim response = Await reader.ReadLineAsync() 'writeline 3: antwort.Text = response 4: End Using 5: .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Wenn der Server Bereitschaft signalisiert, kann die Datei gesendet werden. Dazu wird eine Datei geöffnet. Aus der FileIO Hilfsklasse kommt eine Methode zum Einsatz die einen referenziertes Byte Array vom Typ IBuffer bereit stellt für die Leseoperation. Soweit so praktisch, da der Stream direkt ein entsprechendes Objekt in seiner Schreiboperation benötigt. 1: If antwort.Text = "FILEOK" Then 2: Dim fs = Await KnownFolders.PicturesLibrary.GetFileAsync("youtube.png") 3: Dim b As IBuffer = Await FileIO.ReadBufferAsync(fs) 4: Dim r = Await client.OutputStream.WriteAsync(b) 5: Await client.OutputStream.FlushAsync() 6: client.Dispose() 7: End If .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Anfangs wurde mein Socket Objekt offen gehalten, da ich über eine Bidirektionale TCP Kommunikation zwischen Server und Client nachdenke. Allerdings kam dann das Bild nie am Server an. Erst das schließen der Connection per Dispose (Zeile 6) brachte den Erfolg. Hier muss ich noch weiter über die Hintergründe recherchieren. Im Ergebnis gesehen überschaubarer Aufwand. Allerdings habe ich mehrere Stunden aufgewendet um zu einem funktionierenden Ergebnis zu kommen. Ich finde einfach keine durchgängige Logik in den Klassen. Welcher Stream schreibt wohin? Welche Hilfsklassen gibt es und wie verwendet man diese optimal. Welche Extension Methoden? Wann WinRT, wann .net? Viele Fragen für jede einzelne Zeile Code.

UWP Socket File Transfer

Windows 10 erbt die WinRT API aus Windows 8. Diese API wurde als Hybrid aus Technologien von COM und JavaScript zusammengesetzt. Darauf wurde ein .NET Framework Profil gepfropft. Was sich schon etwas komplex anhört, erschwert in der Tat die Programmierung. Dieser Blog Artikel erläutert am VB.NET Prototyp einen Datei Transfer per TCP/IP. Alle WinRT basierten API beginnen befinden sich im Namensraum Windows, alles was .net bietet beginnt mit System. Dabei gibt es häufig funktionelle Doppelgleisigkeiten, die meist nicht kompatibel sind. Für den Brückenschlag dienen dann meist Extension Methoden wie AsStreamForRead. 1: Imports System.Text 2: Imports System.Net.Sockets 3: Imports Windows.Networking 4: Imports Windows.Networking.Sockets 5: Imports Windows.Storage 6: Imports Windows.Storage.Streams .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Für einen TCP/IP basierenden Filetransfer brauchen wir einen TCP Server, auch listener genannt. Obwohl system.net.sockets referenziert ist, lässt sie .net 4.5 tcplistener Klasse nicht instanziieren. Die Windows Runtime nennt diese StreamSocketListener. Dabei erhält man einen Stream vom Typ IInputStream. Erste Einführung dazu in der MSDN Dokumentation. Allerdings ist es auf Windows 10 Maschinen nicht möglich Socket Connections von App zu App innerhalb des Computers auszuführen. Einzig im Debug Modus und innerhalb der gleichen App gibt es keine Probleme. Tiefer gehende Informationen zu Loop Back Exempt. Der Server wird per Button Click gestartet. Ein Streamsocket Listener lauscht auf Port 2000 und feuert bei Datenankunft ein Event OnConnection. 1: Private listener As StreamSocketListener 2:   3: Private Async Sub button_Click(sender As Object, e As RoutedEventArgs) 4: listener = New StreamSocketListener() 5: AddHandler listener.ConnectionReceived, AddressOf OnConnection 6: Await listener.BindServiceNameAsync("2000", protectionLevel:=SocketProtectionLevel.PlainSocket) 7: End Sub .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Bereits der Verbindungsaufbau eines Client löst das Event aus. Der Code wird allerdings in einem seperaten Thread ausgeführt, so das eine Rückmeldung zum User Interface per Dispatcher erfolgen muss. 1: Private Async Sub OnConnection( 2: sender As StreamSocketListener, 3: args As StreamSocketListenerConnectionReceivedEventArgs) 4:   5: Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, 6: Sub() 7: textBox.Text = args.Socket.Information.RemotePort 8: End Sub) .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Der Verbindungsablauf im  Beispiel Server startet und wartet auf Verbindung Client baut Verbindung auf Server sendet “FILEOK” Client sendet Bild Server speichert Bild Als Fortsetzung zum vorhergehenden Code Beispiel aus dem OnConnection Event, wird der Output Stream auf der aktuellen Connection genutzt. Da von der WinRt APi kommend handelt es sich um ein COM Objekt vom Typ IOutputStream. Mit der Extension Methode AsStreamForWrite wird daraus ein .net Stream Objekt. Erst mit dem Streamwriter kann darin auch geschrieben werden.  1: Using raus As Stream = args.Socket.OutputStream.AsStreamForWrite() 2: Dim wr As StreamWriter = New StreamWriter(raus) 3: Await wr.WriteLineAsync("FILEOK") 'Readline 4: Await wr.FlushAsync() 5: End Using .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Theoretisch könnte man auch direkt auf dem WinRT Objekt in der Form args.Socket.OutputStream.WriteAsync(CryptographicBuffer.ConvertStringToBinary("FILEOK", BinaryStringEncoding.Utf8)) geschrieben werden. Hat bei mir leider nicht geklappt. Da noch immer das OnConnection Event codiert wird, muss noch Schritt 5, das speichern des Bildes erledigt werden. In diesem VB.NET Prototypen wird einfach ein Bild mit dem Namen ZZZZZ angelegt. Auch StorageFile ist ein WinRT Objekt bzw. der dazu nötige Stream. Durchaus verwirrend kommt nun ein drittes Objekt zum Einsatz das einen Stream in den anderen Stream kopiert. Kein Writer, kein Flush, dafür alles über die WinRT API. 1: Dim file As StorageFile = Await KnownFolders.PicturesLibrary.CreateFileAsync("ZZZZZZZZZZZ.png", CreationCollisionOption.ReplaceExisting) 2: Using outputStream As IOutputStream = Await file.OpenAsync(FileAccessMode.ReadWrite) 3: Await RandomAccessStream.CopyAndCloseAsync(args.Socket.InputStream, outputStream) 4: End Using .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Der Client verwendet eine StreamSocket Instanz und verbindet sich zum Server auf Port 2000. 1: Private Async Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click 2: Dim client As StreamSocket 3: client = New StreamSocket() 4: Await client.ConnectAsync(New HostName("127.0.0.1"), "2000") .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Da der Server unmittelbar seine Nachricht sendet, wird diese in der nächsten Code Zeile auch gelesen. Allerdings muss hier der Stream aus der WinRT Welt mit AsStreamForRead wieder in einen .net Stream gecastet werden. Dafür reicht dann einen Zeile um die Antwort des Servers zu lesen (FILEOK). 1: Using reader As StreamReader = New StreamReader(client.InputStream.AsStreamForRead()) 2: Dim response = Await reader.ReadLineAsync() 'writeline 3: antwort.Text = response 4: End Using 5: .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Wenn der Server Bereitschaft signalisiert, kann die Datei gesendet werden. Dazu wird eine Datei geöffnet. Aus der FileIO Hilfsklasse kommt eine Methode zum Einsatz die einen referenziertes Byte Array vom Typ IBuffer bereit stellt für die Leseoperation. Soweit so praktisch, da der Stream direkt ein entsprechendes Objekt in seiner Schreiboperation benötigt. 1: If antwort.Text = "FILEOK" Then 2: Dim fs = Await KnownFolders.PicturesLibrary.GetFileAsync("youtube.png") 3: Dim b As IBuffer = Await FileIO.ReadBufferAsync(fs) 4: Dim r = Await client.OutputStream.WriteAsync(b) 5: Await client.OutputStream.FlushAsync() 6: client.Dispose() 7: End If .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Anfangs wurde mein Socket Objekt offen gehalten, da ich über eine Bidirektionale TCP Kommunikation zwischen Server und Client nachdenke. Allerdings kam dann das Bild nie am Server an. Erst das schließen der Connection per Dispose (Zeile 6) brachte den Erfolg. Hier muss ich noch weiter über die Hintergründe recherchieren. Im Ergebnis gesehen überschaubarer Aufwand. Allerdings habe ich mehrere Stunden aufgewendet um zu einem funktionierenden Ergebnis zu kommen. Ich finde einfach keine durchgängige Logik in den Klassen. Welcher Stream schreibt wohin? Welche Hilfsklassen gibt es und wie verwendet man diese optimal. Welche Extension Methoden? Wann WinRT, wann .net? Viele Fragen für jede einzelne Zeile Code.

externe Live Titles

Was nützt einem die tollste Notification per Badge, Kachel oder Toast, wenn keiner hinschaut. Nun gut, man kann auch noch Sound abspielen um die Aufmerksamkeit des Benutzers auf sein Windows 8.1 Device zu lenken. Oder man kann sich ein USB Gadget auf den Schreibtisch stellen. In diesem Fall das Dream Cheeky USB LED Message Board. Das Produkt Man sollte sich von der Abbildung auf der Website nicht täuschen lassen. Das Display hat eine Auflösung von sagenhaften 21x7 Pixeln in nur laut Dokumentation 3 “Graustufen”. Die roten LED’s sind allerdings im Erscheinungsbild rechteckig angeordnet. Das USB Led Board kommt in einem schwarzen Gehäuse, mit schmalem integrierten Standfuss. Die Abmessung des Leuchtbereiches sind rund 8x1,5 cm. Installation braucht es keine, einfach mit dem integrierten 1 m USB Kabel einstecken und Betriebsbereit. Das USB Device erscheint dann als HID (Human Interface Device) im Gerätemanager. Die Preisspanne liegt von 20-29 €. Programmierung Wie man ein HID Device mit der WInRT Api anprogrammiert folgt etwas später.  Faktisch schickt man Bit Sequenzen an das Device. Das LED Board wertet diese per Microcontroller aus und steuert die Matrix an. Die Anzeige erlischt nach 400ms. Das bedeutet, das man regelmäßig ein refresh durchführen muss. Nun gibt es nichts was man braucht, wie Zeichensatz oder Display Treiber. Muss alles selbst entwickelt werden. Die Dokumentation dazu ist einfach und kurz. Was länger dauert, ist einen Zeichensatz zu definieren und eine Umrechenlogik zu coden. Ich habe mit Excel eine Matrix verfasst, x gemalt und eine einfache Hexformel hinterlegt. Um ein komplettes Display zu beschreiben müssen vier mal 9 Bytes gesendet werden. Byte 1 ist immer 0, Byte 2 die Helligkeit von 0-2. Byte 3 die Displayzeile (0,2,4,6) Byte 3 bis 5 stellen die erste Zeile dar. 6 bis 9 die Zweite Zeile. Dunkel ist 1 Hell ist 0. Wer bist Du In den seltensten Fällen gibt es bei einem USB Gimmick eine Developer Dokumentation für Windows RT. Die wichtigsten vier Parameter sind Vendor ID, Product ID, Usage Page und Usage ID. Alles im Integer Bereich, aber üblicherweise als Hex verwendet. Das gratis Programm HIDTest liefert die nötigen Daten. Wir wissen nun für das USB LED Messageboard VendorID: 1D34 DeviceID: 0016 UsagePage: FF00 UsageID: 0001 Windows 8 (METRO) App und HID Als nächstes wird mit Visual Studio 2013 eine Windows Store App angelegt. Um ein HID Gerät verwenden zu können muss man im Manifest die Rechte definieren. Da der default Visual Studio Editor für das package.appxmanifest  keine UI bietet, bleibt nur der Weg über den XML Editor um die Capability (Funktion) für ein HID Device zu erstellen. 1: <Capabilities> 2: <Capability Name="internetClient" /> 3: <m2:DeviceCapability Name="humaninterfacedevice"> 4: <m2:Device Id="vidpid:1D34 0013"> 5: <m2:Function Type="usage:FF00 0001"/> 6: </m2:Device> 7: </m2:DeviceCapability> 8: </Capabilities> 9: </Package> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Visual Studio zeigt diese dann auch nicht an. Beim ersten Start erhält der Benutzer einen Dialog, mit dem er die Nutzung des Gerätes durch die APP erlaubt. Der Benutzer kann diese in den Settings über die Berechtigungen jederzeit verändern. Folglich muss der Programmiercode (VB.NET oder C#) bei jedem Aufruf dies prüfen. Jetzt sind wir bereit, das Device zu initialisieren. Da es eine beliebige Anzahl von identen LED Lichtleisten in meinem Windows Computer geben kann, als Liste. Mit dem ersten Device wird einen Lese und Schreib Kommunikation aufgebaut. Da es nur Read oder ReadWrite gibt, die passende Option auch wenn nichts gelesen wird. 1: Private Async Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded 2: Dim deviceSelector = HidDevice.GetDeviceSelector(&HFF00, &H1, &H1D34, &H13) 3: Dim devices = Await DeviceInformation.FindAllAsync(deviceSelector) 4: Dim device = devices.Item(0) 5: devicehandler = Await HidDevice.FromIdAsync(device.Id, FileAccessMode.ReadWrite) 6: report = devicehandler.CreateOutputReport(0) 'nothing Manifest 7: End Sub .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Es existieren zwei Arten von Kommunikation mit dem HID Kanal. Der FeatureReport setzt das Gerät in einen bestimmten Zustand und der Outputreport löst eine konkrete Funktion aus. Der Parameter 0 wurde geraten und definiert die ReportID. Diesem USB Device ist das egal, andere fordern spezifische ID’s für eine spezifische Funktion. Nun müssen nur noch Daten über den USB Draht. Wie vorhin ausgeführt sind das vier Kommandos mit selbst berechneten HEX Werten. Außerdem werden diese nur kurz angezeigt. 1: Public Async Function feedDisplay() As Task 2: Dim daten() As Byte = {&H0, &H2, &H0, &HEB, &H24, &H83, &HEA, &HB2, &H4F} 3: sendDaten(daten) 4: daten = {&H0, &H2, &H2, &HEA, &HB2, &H4F, &HEA, &HA4, &H8A} 5: sendDaten(daten) 6: daten = {&H0, &H2, &H4, &HEA, &HB6, &HDE, &HF6, &HB6, &HDE} 7: sendDaten(daten) 8: daten = {&H0, &H2, &H6, &HF7, &H26, &HD8, &HEF, &HFF, &HFF} 9: sendDaten(daten) 10: Await Task.Delay(400) 11: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Das eigentliche senden kopiert das Array in den Report Buffer.  Die auskommentierte Zuweisung erzeugt keinen Laufzeitfehler, aber das Display leuchtet dann auch nicht. 1: Public Async Function sendDaten(daten() As Byte) As Task 2: 'report.Data = daten.AsBuffer klappt nicht 3: daten.CopyTo(0, report.Data, 0, daten.Length) 4: Await devicehandler.SendOutputReportAsync(report) 5: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Am Ende noch die deklarierten VB.NET Variablen des WinRT Code und der Timer der alle 400 Millisekunden die Funktion aufruft die mit dem Display kommuniziert. Dim devicehandler As HidDevice Dim report As HidOutputReport Dim _timer As DispatcherTimer Private Async Function Button_Click(sender As Object, e As RoutedEventArgs) As Task _timer = New DispatcherTimer() _timer.Interval = New TimeSpan(0, 0, 0, 0, 400) AddHandler _timer.Tick, AddressOf feedDisplay _timer.Start()End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Schwierigkeiten treten immer auf, wenn man die vier Parameter nicht kennt, vertauscht oder auch falsch anwendet. Es passiert dann schlicht nichts oder eine nichtssagende Exception. Wie üblich der Hinweis, das dies ist ein Prototyp Beispiel.

Windows 8.1 Dateien auf Readonly setzen

In einer aktuellen Windows 8 Schulung zeigt mir ein Kursteilnehmer seine Lösung um einen Schreibschutz von einer Datei per WinRT zu entfernen. Habe ich noch nie gemacht. Der Code sieht jedenfalls kompliziert aus und so fange ich mal an mit VB.NET zu testen. Zuerst eine Bild Datei auf readonly setzen. Dazu muss man Wissen das die Attribute per Bitmaske gesetzt werden. READONLY 0×1 HIDDEN 0×2SYSTEM 0×4DIRECTORY 0×10ARCHIVE 0×20DEVICE 0×40NORMAL 0×80TEMPORARY 0×100SPARSE_FILE 0×200REPARSE_POINT 0×400COMPRESSED 0×800OFFLINE 0×1000NOT_CONTENT_INDEXED 0×2000ENCRYPTED 0×4000 Die Attribute werden per Retrieve und Save gelesen und gesetzt. Per Text wird festgelegt welche Art von Metainformation gelesen werden soll. Davon gibts eine ganze Reihe. 1: Dim FILE_ATTRIBUTE_READONLY As UInt32 = 1 2: Dim SYSTEM_FILEATTRIBUTES = "System.FileAttributes" 3: Dim file = Await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync("vespa.png") 4: Dim fileAtr = Await file.Properties.RetrievePropertiesAsync({SYSTEM_FILEATTRIBUTES}) 5:   6: If fileAtr IsNot Nothing Then 7: Dim t = fileAtr.First.Value Or FILE_ATTRIBUTE_READONLY 8: fileAtr(SYSTEM_FILEATTRIBUTES) = t 9: End If 10:   11: Await file.Properties.SavePropertiesAsync(fileAtr) .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Dem Schreibschutz entfernt man, indem man einfach die Attribute der Datei auf normal setzt. 1: fileAtr(SYSTEM_FILEATTRIBUTES) = 128 .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

HID in Windows 8.1 mit Wiimote

Wer sich mit den Eigenschaften des Wiimote Controllers beschäftigt, kann nur begeistert sein. Dabei ist das Ding sieben Jahre alt. Beschleunigungssensoren, Infrarot Kamera, LED, Buttons, Vibration, Bluetooth und ganz abgefahren einen Lautsprecher auf den man streamen kann. Und das alles für ca 30 Euro. Windows 8.1 erkennt unzählige USB oder auch Bluetooth Geräte als HID (human Input devices). WinRT bietet eine einfach API um diese Devices verwenden zu können. Dieser Blog Artikel zeigt wie man in einer XAML Windows Store App (modern UI/METRO) Anwendung einen Spiele Controller verwendet. In Windows 8.1 kann dieser Controller per Bluetooth relativ einfach verbunden (gepaired) werden. Auch wenn ein PIN Code gefordert wird, kann dieser übersprungen werden. In den Blogs stehen unterschiedliche Anweisungen (die bei mir alle nicht funktionieren) für das Pairing mit Pin. Ich habe nicht herausgefunden, wie man die PIN des Wii Controllers herausfindet. Geht auch so. Ohne Sicherheits Code entweder die Tasten 1 und 2 gleichzeitig drücken oder den roten Knopf beim Batteriefach. Die vier blauen LEDs blinken dann. Die Bluetooth Pairing Code einfach leer lassen und weiter drücken. Die  Wii erscheint dann als HID Device im Geräte Manager Wer sich mit den HID Device Code Beispielen beschäftigt weis, das es zwei oder besser vier Parameter braucht um das Gerät überhaupt ansprechen zu können. Geübte HID Profis finden das in den Device Settings. Andere (wie ich) brauchen Doku oder Werkzeuge.  Sehr nützlich für den Start ist ein Testtoolgefunden auf Codeplex SimpleHIDTest. So findet sich der Wii Controller als Nintendo RVL-CNT-01. Dadurch erhält man die Parameter für die HID Device Klasse (Zeile 2).  UsagePage, UsageID, vendorid, ProductID. Diese sind für jedes Gerät anders. Es existieren sogar zwei Wii Varianten. 1: Private Async Function Button_Click(sender As Object, e As RoutedEventArgs) As Task 2: Dim deviceSelector = HidDevice.GetDeviceSelector(&H1, &H5, &H57E, &H306) 3: Dim devices = Await DeviceInformation.FindAllAsync(deviceSelector) 4: Dim device = devices.Item(0) 5: devicehandler = Await HidDevice.FromIdAsync(device.Id, FileAccessMode.ReadWrite) 6: AddHandler deviceHandler.InputReportReceived, AddressOf inputrec 7: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Im VB.NET Code wird dann ein Event registriert um die eingehenden Daten zu erhalten. Die Daten enhalten immer exakt 22 Bytes (siehe Bild oben Hex 16). 1: Private Async Function inputrec(sender As HidDevice, args As HidInputReportReceivedEventArgs) As Task 2: Dim buf As IBuffer = args.Report.Data 3: Dim bufarray = buf.ToArray 4: If bufarray(0) = InputReport.Buttons Then 5: Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, 6: Sub() 7: txtButton.Text = ParseButtons(bufarray) 8: End Sub) 9: End If 10: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Aber noch weis niemand was die Daten aus dem Byte Array bedeuten. Dies ist für jedes HID Device extrem unterschiedlich bis auf Bit Ebene. Aufschluss gibt ein Dokument in dem die Wii Commands beschrieben sind. Es gibt Channels in denen die 22 Byte Arrays gesendet oder empfangen werden. Jede Funktion hat einen eigenen Channel oder auch mehrere. Diese Information stammt aus einem PDF. Das erste Byte definiert den Channel und die zwei nächsten die nötigen Daten. Ein Tastendruck kommt auf Kanal Hex 30 (0x30). Ein weiteres Tool zeigt die eingehenden Daten und erlaubt sogar Daten an die Wii zu senden. Da der Autor verstorben ist, gibt es keine Source Code zu SimpleHIDWrite3. Man erkennt beim drücken des B Buttons (unten am Controller) die ankommenden Hex Werte. Eine Enumeration macht den Code später lesbarer und wird im vorigen Listing Zeile 4 bereits angewandt. 1: Public Enum InputReport As Byte 2: Status = &H20 3: ReadData = &H21 4: Buttons = &H30 5: ButtonsAccel = &H31 6: IRAccel = &H33 7: ButtonsExtension = &H34 8: ExtensionAccel = &H35 9: IRExtensionAccel = &H37 10: End Enum .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Das Parsing der Daten erscheint etwas ungewohnt und kann recht umfangreich ausfallen. Immerhin können mehre Buttons gleichzeitig gedrückt werden.  Folgendes VB.NET Code Beispiel dient nur als Idee und liefert für die Buttons A oder B einen Wert. 1: Private Function ParseButtons(ByVal buff() As Byte) As String 2: If buff(2) = &H8 Then 3: Return "A" 4: End If 5: If buff(2) = &H4 Then 6: Return "B" 7: End If 8: Return "-" 9: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Da beim loslassen des Buttons wiederum 22 Bytes kommen (siehe Bild SimpleHIDWrite) wird auch das per Event registriert und behandelt (hier Zeile 8) Im vorigen Screenshot wurde in der Zeile wr die linke LED des Controllers eingeschalten. Man spricht im HID Umfeld von Reports die gelesen oder geschrieben werden. Die ersten 4 Bits stehen für die 4 LEDS, der rechte Teil löst die Vibration aus. Der Kanal ist 11. Damit sollte folgender Code in der Windows 8 Store App die blaue LED links einschalten. 1: Private Async Function CheckBox_Checked(sender As Object, e As RoutedEventArgs) As Task 2: Dim report = devicehandler.CreateOutputReport() 3: Dim daten() As Byte = {&H11, &H10, &H0, &H0, &H0, 4: &H0, &H0, &H0, &H0, &H0, 5: &H0, &H0, &H0, &H0, &H0, 6: &H0, &H0, &H0, &H0, &H0, 7: &H0, &H0} 8:   9: report.Data = daten.AsBuffer 10: Await devicehandler.SendOutputReportAsync(report) 11: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } WICHTIG: Dies klappt bei mir nicht! Vielleicht hat ja jemand einen Tipp. Danke an Brian Peek für Support.

Windows 8.1 Store App Submission

Vor längerer Zeit habe ich schon einmal den Prozess: App in den Store. Eine App die per Share Contract Bilder in SharePoint hoch lädt. Damals habe ich nach Wochen entnervt aufgegeben. Großes Problem war (und ist) das für die Funktion ein SharePoint Server nötig ist und die Tester offensichtlich keine Ahnung davon haben. Soweit ich gehört hatte, sind die Zyklen von Wochen auf Tage bzw Stunden geschrumpft, so das es relativ schnell möglich sein sollte, seine Windows 8 App in den Store zu bekommen. Meine APP ist fertig, ich bin voll stolz. Was nun? Folgende Abhandlung ist keine Kritik an Microsoft und seinen Testern, sondern beschreibt meine Erfahrungen von der fertigen App bis zur finalen Download Fähigkeit. Und darüber hinaus. Anwendungsfall ist, eine Website zu laden und diese mit Kommentaren zu versehen. Dies habe ich als Beispiel APP zum neuen WebBrowser Control aus Windows 8.1 (WebView) verwendet. Die Kommentare werden dann per Share Contract weiter gereicht. Damit der Empfänger diese lesen kann, wird ein HTML Offline Viewer in ein Zip gepackt. Das z.b. per Mail versandte Paket kann dann entpackt werden und mit jedem beliebigen Browser die Kommentare gesichtet werden. Dazu werden Screenshots aus Visual Studio zur APP angefertigt. Start am 31.10 um ca 21:00   Um eine Beschreibung und Screenshots zu erstellen benötigt man ganz andere Skills, als ein Developer üblicherweise hat. Die App ist gratis und erfordert keine TAX Informationen. Die 8er Version habe ich mir auch gespart. Viele der Infos die da rein müssen sind extrem nervig. So zb der berühmte Link auf die Datenschutzrichtlinien. Schlussendlich noch die Notes für den Tester, die ich irgendwie instinktiv auf Englisch verfasse. Fail #1 1.11 Ihre App entspricht nicht der Anforderung 6.2. Weitere Informationen Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Der App-Inhalt (einschließlich Werbung) ist für die angegebene Altersfreigabe nicht geeignet. Sie deklarieren eine Einstufung von einer Einstufungsstelle in einer Spieldefinitionsdatei, aber Sie haben keinen Nachweis für diese Einstufung angegeben. Sie haben die App als geeignet für Kinder ab 3 oder 7 aufgeführt. Sie bietet aber dem Benutzer unkontrollierten Zugriff auf soziale Onlinenetzwerke, bzw. es können persönliche Daten mit Dritten, einschließlich anderen Spielern oder Onlinebekanntschaften, unkontrolliert geteilt werden. Für solche Aktivitäten, die "kontrolliert" werden sollen, muss Ihre App Features für den Jugendschutz enthalten. Diese Features müssen die Erlaubnis der Eltern für die Verwendung aller Features zum Teilen erforderlich machen. Sie müssen diese identifizieren und ihre Funktionalität in den Hinweisen für die Tester erläutern. Für einen der von Ihnen ausgewählten Vertriebsmärkte muss eine Einstufung einer Einstufungsstelle vorliegen, diese fehlt jedoch. Ihre App entspricht nicht der Anforderung 6.5. Weitere Informationen Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Die App unterstützt keine der zertifizierten Sprachen. Die App funktioniert nur in einigen Sprachen, für die sie vorgeschlagen wurde, aber nicht in allen. Die Info auf der Beschreibungsseite der App (z. B. ihre Beschreibung, Features, Screenshots usw.) reflektiert nicht den Umfang der Lokalisierung der App. Ihre App entspricht nicht der Anforderung 6.8. Weitere Informationen Ein häufiger Grund, warum Apps diese Anforderung nicht erfüllen, besteht darin, dass mindestens ein Screenshot grafisch aufbereitet zu sein scheint. Hinweise von Testern: The description of this app did not appear localized correctly to our reviewer. Screenshots provided are not appropriately localized for each language the app is supported in. During the review of this app, it did not appear to be fully localized for the languages it supports. The app appears to provide users with uncontrolled access to online social networks or sharing of personal information with third parties. Because of this, you must assign a Windows Store age rating of at least 12+. Nun bin ich kein Freund von Richtlinien durchlesen. Aber da steht wirklich drin, wenn die App vertrauliche Informationen weitergeben kann (der Screenshot) dann muss das Altersrating auf 12. Hab ich geändert. Im Report hängt auch ein entsprechender Screenshot Fail #2 1.11 Ihre App entspricht nicht der Anforderung 1.2. Weitere Informationen Einige Fragen zur Anregung: Ist für Ihre App ein Benutzerkonto erforderlich? Wenn ja, dann stellen Sie ein Testkonto im Feld "Hinweise für die Tester" zur Verfügung. Können Benutzer über Ihre App einkaufen? Wenn ja, dann stellen Sie eine Möglichkeit bereit, die Einkäufe zu testen. Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Die App enthält nicht funktionale Abschnitte oder Platzhalter (etwa mit Etiketten wie "Bald erhältlich", "Bald mehr", "Noch nicht erhältlich" usw.) für Standardbenutzerszenarien. Die App arbeitet nicht wie angegeben in allen Architekturen. Wenn Sie z. B. angeben, dass Ihre App mit jeder CPU arbeitet, muss sie in allen Architekturen funktionieren, einschließlich ARM. Die App-Beschreibung ist irreführend oder vage. In der App-Beschreibung werden Screenshots oder Anweisungen verwendet, die sich auf Features beziehen, die nicht implementiert wurden. Die App reagiert nicht auf Tastaturereignisse vom Typ "Wiedergabe" oder "Pause", damit der Benutzer die Audiowiedergabe steuern kann. In der App-Beschreibung sind weder Hardware- noch Netzwerkanforderungen explizit benannt. Ihre App entspricht nicht der Anforderung 6.5. Weitere Informationen Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Die App unterstützt keine der zertifizierten Sprachen. Die App funktioniert nur in einigen Sprachen, für die sie vorgeschlagen wurde, aber nicht in allen. Die Info auf der Beschreibungsseite der App (z. B. ihre Beschreibung, Features, Screenshots usw.) reflektiert nicht den Umfang der Lokalisierung der App. Ihre App entspricht nicht der Anforderung 6.8. Weitere Informationen Ein häufiger Grund, warum Apps diese Anforderung nicht erfüllen, besteht darin, dass mindestens ein Screenshot grafisch aufbereitet zu sein scheint. Hinweise von Testern: The description of this app did not appear localized correctly to our reviewer. The captions for the screenshots are not properly localized for each language the app is supported in. From your screenshots, it appears that you are using an unlicensed copy of Windows. Please update your screenshots appropriately. During the review of this app, it did not appear to be fully localized for the languages it supports.   In der Tat habe ich eine noch nicht registrierte Windows Version verwendet. Schön wär der Hinweis gleich im ersten Report gewesen. Also Windows aktiviert und neue Screenshots gemacht. Das mit der lokalisierung hatte ich noch nicht verstanden. Aber Nachfragen ist nicht. Erst nach längerem suchen fand ich in den Visual Studio Project Settings EN-US und habs auf DE geändert. Paket neu erzeugt und hochgeladen. Fail #3 4.11 Ihre App entspricht nicht der Anforderung 1.1. Weitere Informationen Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Der Wert oder der Nutzen der App ist unklar. Die App hat nur einen Wert oder ist von Nutzen für Sprachgruppen, die von ihr unterstützt werden. Hinweise von Testern: This app didn't appear to provide value or didn't seem useful to the reviewer.   Also dem Tester in den Notes nochmal genau erklärt, was der Zweck ist und unverändert neu angestoßen. Fail #4 4.11 Ihre App entspricht nicht der Anforderung 3.2. Weitere Informationen Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Die App stürzt beim Starten ab. Die App stürzt willkürlich oder wiederholt ab. Die App friert ein, und der Benutzer muss die App schließen und erneut starten. Für die App ist eine Windows-Komponentenbibliothek erforderlich, die der Windows Store nicht unterstützt. Um dies zu vermeiden, sollten Sie stets die aktuellsten Entwicklungstools verwenden. Hinweise von Testern: The app crashed during our review. If we were able to capture a crash report, we have provided it to you. Please see: http://go.microsoft.com/fwlink/?LinkId=279806 for information on how to work with the crash reports. Nun rächt sich der Tester, vermutlich hat ihm mein Ton nicht gefallen. Der Screenshot aus dem Zertifzierungsbericht bringt mich auf die Spur. Ich ändere ein wenig die Fehlerbehandlung im Code und erstell ein neues Paket.   Fail #5 4.11 In diesem Test wird die inhaltliche Kompatibilität der Software mit den Zertifizierungsanforderungen für den Windows Store überprüft. Weitere Informationen Ihre App entspricht nicht der Anforderung 6.8. Weitere Informationen Ein häufiger Grund, warum Apps diese Anforderung nicht erfüllen, besteht darin, dass mindestens ein Screenshot grafisch aufbereitet zu sein scheint. Hinweise von Testern: The screenshots provided do not reflect the App.   Hä? ich hab keine Idee was das soll. Einfach nochmal submitted Fail #6 5.11 Die häufigsten Gründe, warum Apps diese Anforderung nicht erfüllen: Die App enthält nicht funktionale Abschnitte oder Platzhalter (etwa mit Etiketten wie "Bald erhältlich", "Bald mehr", "Noch nicht erhältlich" usw.) für Standardbenutzerszenarien. Die App arbeitet nicht wie angegeben in allen Architekturen. Wenn Sie z. B. angeben, dass Ihre App mit jeder CPU arbeitet, muss sie in allen Architekturen funktionieren, einschließlich ARM. Die App-Beschreibung ist irreführend oder vage. In der App-Beschreibung werden Screenshots oder Anweisungen verwendet, die sich auf Features beziehen, die nicht implementiert wurden. Die App reagiert nicht auf Tastaturereignisse vom Typ "Wiedergabe" oder "Pause", damit der Benutzer die Audiowiedergabe steuern kann. In der App-Beschreibung sind weder Hardware- noch Netzwerkanforderungen explizit benannt. Hinweise von Testern: This app appears to be incomplete in its functionality or is still under development and is not fully functional. Wie geil ist das denn? Der Zertifizierungsbericht zeigt, was der Tester so macht. Er ruft eine Website auf. Nette Idee. Klappt bei mir aber. Also nochmal unverändert submitted. Fail #7 7.11 Hinweise von Testern: The content in the app does not appear to be age appropriate for the Windows Store age rating selected for this app. Da die Paragraphen schon bekannt sind, nur mehr die Notes des Testers. Übersetzt, weil man mit der App Porno Seiten öffnen kann, Altersrating 16. OK- Hätte man auch früher sagen können. Von ab 3Jahren auf 12 und nun ab 16. Mein Fehler. Fail #8 8.11 Hinweise von Testern: One or more links, including privacy policy, websites, support contact or other websites linked to from this App do not resolve to a functional webpage and/or do not appear to be complete. The screenshots provided do not reflect the App. Offensichtlich fehlende Netzwerk Verbindung. Ob beim Tester oder unserem Webserver ist unklar. Eine 404er sieht jedenfalls anders aus. Also einfach dem Tester nette Worte geschrieben, das er die URL einfach mal direkt in seinem Browser aufrufen soll. Die Notes für den Tester werden länger und länger. Fail #9 9.11 Hinweise von Testern: The screenshots provided do not reflect the App. In der Tat beziehen sich drei von sechs Screenshots auf das Handling des ZIP Bundles im Browser. Das erklärt einfach die Funktion viel besser. Andere APPS im Store zeigen auch Word oder ähnliches. Auf Rat aus der Facebook Community habe ich die Screenshots dann doch gelöscht. Der Tipp war, sie später beim nächsten Release, wieder einzufügen. Success #10 10.11 Die App ist im Store, aber erst nach vier Stunden auch auffindbar. Sieht fürchterlich aus. Aus diesem Grund bieten wir auf der GUI&DESIGN auch einen Zeichenworkshop an um Bilder zu erstellen, die auch verkaufen. Das Ergebnis nach einem halben Tag im Store, spricht auch Bände. Null downloads. 0€ x 0 Downloads <>Millionär Fazit: Der Submission Prozess für Windows 8 Apps ist deutlich schneller geworden. Für Texte und Grafik muss man reichlich Zeit einplanen bzw externe Ressourcen nutzen. Es verkürzt den Prozess die Guidelines zu lesen. Für den Tester umfangreiche Bedienhinweise erstellen. Trotzdem kann man nicht 100tig sicher sein, das es auch funktioniert. Der Store ersetzt kein Marketing.

Dateien Zippen mit Windows 8.1

Zuerst ist die Idee, dann kommen die Probleme. Wenn man es schafft die Probleme in kleine lösbare Tasks aufzubrechen, ist  man quasi schon fertig. Denkt man. Einer dieser kleinen Tasks ist, mehrere Dateien in ein ZIP File zu legen. In den Windows 8.1 SDK Beispielen findet sich auch ein Beispiel (Compression Sample) um Strings und Streams zu komprimieren. Leider überhaupt nicht passend und auch kaum hilfreich. Der Namensraum System.IO.Compression und eine Methode Compressor. Verschiedene Blog Einträge behaupten, das man damit keine Dateien in ein ZIP komprimieren kann und verweisen auf irgendwelche Nuget Pakete. Das ist ein neues Problem: wem soll man glauben? (Wie immer ein Problem gelöst, 1+n geschaffen) Nur der Visual Studio  Object Viewer gibt einen verlässlichen Überblick, der zu 100% stimmt. Gelernt: der Namensraum hat eine Version 4.5.1 und offensichtlich eine Entry Verwaltung (ZipArchiveEntry), was deutlich in Richtung Multi Dateien Zip deutet. Letztendlich kann ein Entry per Open geöffnet und dann eine Datei per Write zum Archiv hinzugefügt werden. Folgendes Code Beispiel komprimiert zwei HTML Dateien, die im Windows Store APP Projekt Paket inkludiert sind, in eine ZIP Datei und speichert diese im temporären Verzeichnis auf der lokalen Festplatte. Das ist nur Prototyping Code! (refactoring ruft) 1:Dim dataUri As New Uri("ms-appx:///html/default.html") 2:Dim file1 As StorageFile = Await StorageFile.GetFileFromApplicationUriAsync(dataUri) 3:dataUri = New Uri("ms-appx:///html/detail.html") 4:Dim file2 As StorageFile = Await StorageFile.GetFileFromApplicationUriAsync(dataUri) 5:Dim storage = Await ApplicationData.Current.TemporaryFolder.CreateFileAsync("webcommentbundle.zip") 6:Using s = Await storage.OpenStreamForWriteAsync() 7: Using zip = New ZipArchive(s, ZipArchiveMode.Create) 8: Dim entry = zip.CreateEntry(file1.Name) 9: Using ZipFile = entry.Open() 10: Dim buffer As IBuffer = Await FileIO.ReadBufferAsync(file1) 11: ZipFile.Write(buffer.ToArray, 0, buffer.Length) 12: End Using 13: Dim entry2 = zip.CreateEntry(file2.Name) 14: Using ZipFile = entry2.Open() 15: Dim buffer As IBuffer = Await FileIO.ReadBufferAsync(file2) 16: ZipFile.Write(buffer.ToArray, 0, buffer.Length) 17: End Using 18: End Using 19:End Using 20: .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Die Write Methode benötigt  ein Byte Array als Parameter. Wir haben Storagefile als Ausgangspunkt und erhalten einen WinRT API typischen iBuffer. Auch hier bringen die Suchmaschinen “Storagefile to Byte Array” die unterschiedlichsten Ergebnisse. Offensichtlich wenig bekannt ist die Methode ToArray (als Extension nachgeliefert)

Searchbox Windows 8.1

Die Suche in Windows 8.1 ist eine glatte Kehrtwendung von Microsoft. Statt im Charm Bar, wird wieder direkt in der Anwendung gesucht. Nur wenige neue Anwendungen beinhalten aktuell (Oktober 2013) das dafür vorgesehene SearchBox Control. Die auf Windows 8.1 vorinstallierten Bing Weather oder Bing Stocks App lassen erahnen wohin die Reise geht. Zusätzlich bietet das XAML Steuerelement eine Vorschlagsliste, die bei Bedarf auch Bilder anzeigen kann. Hier ein Screenshot aus der Kontakte App. Das Standardverhalten ist bisherige Suchbegriffe als Vorschlagsliste anzuzeigen. Alternativ ( oder auch zusätzlich, lassen sich die Vorschläge auch aus einem Service oder lokalen Daten generieren. Dazu wurde im XAML die Search Historie deaktiviert und das Event Suggestion Requested anprogrammiert. Über das Property FocusOnKeyboard, wird die Suche verwendet, sobald der Benutzer am Keyboard eine Taste drückt. Auch wenn gerade ein anderes UI Element den Focus hat. Ganz wie der Windows 8.1 Startscreen. 1: <SearchBox FocusOnKeyboardInput="True" 2: QuerySubmitted="SearchBox_QuerySubmitted" 3: SuggestionsRequested="SearchBox_SuggestionsRequested" 4: SearchHistoryEnabled="False"> 5: </SearchBox> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } In diesem konkreten VB.NET Beispiel werden Ozon Messdaten in einer Listview angezeigt. Die Daten sind als JSON direkt im Projekt eingebettet. Wenn der Benutzer sucht, wird per LINQ Query die Filterung durchgeführt. 1: Dim l As List(Of Messstation) 2: Private Async Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded 3: Dim sf = Await StorageFile.GetFileFromApplicationUriAsync(New Uri("ms-appx:///ozon.json")) 4: Dim js = Await sf.OpenStreamForReadAsync 5: Dim ser As New DataContractJsonSerializer(GetType(List(Of Messstation))) 6: l = CType(ser.ReadObject(js), List(Of Messstation)) 7: End Sub 8:   9: Private Async Function SearchBox_QuerySubmitted(sender As SearchBox, args As SearchBoxQuerySubmittedEventArgs) As Task 10: Dim query = From item In l 11: Where item.name.Contains(args.QueryText) 12: Select item 13: liste0.ItemsSource = query 14: End Function .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Etwas spannender ist die Vorschlagsliste. Über die Eventargs erhält man Zugriff auf die Systemvorschlagsliste und kann dort Einträge einzeln per AppendQuerySuggestion anhängen. Oder man nimmt gleich eine ganze Liste von Strings, wie im folgenden VB.NET Code Sample. 2: Private Sub SearchBox_SuggestionsRequested(sender As SearchBox, args As SearchBoxSuggestionsRequestedEventArgs) 4: Dim queryText = args.QueryText 5: If Not String.IsNullOrEmpty(queryText) Then 6: Dim query = From item In l 7: Where item.name.Contains(args.QueryText) 8: Select item.name 9: Take 10 10: Dim sugColl As SearchSuggestionCollection = 11: args.Request.SearchSuggestionCollection 12: sugColl.AppendQuerySuggestions(query) 13: End If 14: End Sub 16: .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } In meinen Versuchen habe ich den Filezugriff in die Methode SuggestionsRequested gelegt, in der Art : 1: Private Async Sub SearchBox_SuggestionsRequested(sender As SearchBox, args As SearchBoxSuggestionsRequestedEventArgs) 2: Dim sf1 = Await StorageFile.GetFileFromApplicationUriAsync(New Uri("ms-appx:///ozon.json")) 3: .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Geerntet habe ich eine Exception in der Zeile 12 (AppendQuery). A first chance exception of type 'System.InvalidOperationException' occurred in App2.exe. WinRT information: Eine Methode wurde zu einem unerwarteten Zeitpunkt aufgerufen. Oder auf Englisch: System.InvalidOperationException: A method was called at an unexpected time Fürs erste war ich ratlos. Tim Heuer von Microsoft hat mich auf die passende Lösung gestoßen. Genau wie im OnSuspending Event, muss man sich aus den Eventargs die Referenz auf das Deferal holen, um nicht in Timing Probleme zu laufen. 1: Dim deferral = args.Request.GetDeferral() 2: Dim foo = Await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(New Uri("ms-appx:///assets/logo.png")) 3: args.Request.SearchSuggestionCollection.AppendQuerySuggestion("test") 4: deferral.Complete() .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Windows 8.1 Scrollviewer fixe Header und Spalte

Letztens in einem Vortrag von mir, fiel es mir wie Schuppen von den Augen. Während die Worte über die Lippen flossen, sprach mein Gehirn zu mir. Das ist es! Es geht nicht um Maus, Keyboard oder Touch. Es geht nicht um Tablet, PC oder Phone. Es geht um die Nutzung, die Situation, den Ort. Wir brauchen Bedienkonzepte die genau das Berücksichtigen. Es gilt nicht mehr “Spielekonsole= Wohnzimmer”, “Computer=Schreibtisch”, “Telefon = Küchenwand” mehr. UX Design wird damit nicht einfacher. Controls helfen dabei konkrete Nutzungsszenarien (sozusagen als Entwurfsmuster) abzudecken. Sehr bekanntes Entwurfsmuster ist die Tabelle. Eine oft gehörte Frage in den ppedv Trainings ist: “wie kann man eine Excel ähnliche Tabelle im Formular darstellen?” Neben den vielen Feldern in den Spalten und Reihen, ist die Eigenheit von Excel, das die Bezeichner (Beschriftung) im Kopf und Links fixiert sind. Dies klappt nun auch mit dem XAML Scrollviewer aus Windows 8.1 (METRO, Modern UI). Neu hinzugekommen sind drei Templates TopLeftHeader TopHeader LeftHeade Im folgenden Beispiel werden Ozonwerte von Österreichischen Messstationen dargestellt. Es sind mehr Zeilen als auf das Display passen und auch mehr Spalten. Der Benutzer kann dann in beide Richtungen scrollen. Hier wird die Spalte Ort in einem eigenen Listview dargestellt um diese Werte in die Spalte LeftHeader zu bekommen. Die Überschrift in der Zeile TopHeader ist ident mit den gebundenen Werten und Darstellung in der zweiten Listview. Diese zweite Listview mit den eigentlichen Daten wird letztendlich vom Scrollviewer umschlossen und stellt den großen nicht darstellbaren Bereich dar. Über die Attribute Horizontal – und VerticalScrollmode wird unterbunden, das man zwei bzw. vier Scrollbalken zu Gesicht bekommt. 1: <ScrollViewer Width="500" Margin="133,63,0,0" 2: HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Visible"> 3: <ScrollViewer.TopLeftHeader> 4: <Rectangle Fill="White" Width="276" Height="38"> 5: </Rectangle> 6: </ScrollViewer.TopLeftHeader> 7: <ScrollViewer.TopHeader> 8: <StackPanel Orientation="Horizontal" Background="White" > 9: <TextBlock Text="StationID" Width="100" ></TextBlock> 10: <TextBlock Text="Zeit" Width="140" ></TextBlock> 11: <TextBlock Text="Breitengrad" Width="100"></TextBlock> 12: <TextBlock Text="Längengrad" Width="100"></TextBlock> 13: <TextBlock Text="ozon 1h" Width="100" ></TextBlock> 14: <TextBlock Text="Max ozon 1h" Width="100"></TextBlock> 15: <TextBlock Text="ozon 8h" Width="100" ></TextBlock> 16: </StackPanel> 17: </ScrollViewer.TopHeader> 18: <ScrollViewer.LeftHeader> 19: <ListView Background="DarkGray" 20: ScrollViewer.HorizontalScrollMode="Disabled" 21: ScrollViewer.VerticalScrollMode="Disabled" 22: x:Name="liste1" HorizontalAlignment="Left" VerticalAlignment="Top" 23: SelectionMode="None" > 24: <ListView.ItemTemplate> 25: <DataTemplate> 26: <TextBlock Text="{Binding name}" Width="250"></TextBlock> 27: </DataTemplate> 28: </ListView.ItemTemplate> 29: </ListView> 30: </ScrollViewer.LeftHeader> 31: <ListView SelectionMode="None" 32: ScrollViewer.HorizontalScrollMode="Disabled" 33: ScrollViewer.VerticalScrollMode="Disabled" 34: x:Name="liste0" HorizontalAlignment="Left" VerticalAlignment="Top" > 35: <ListView.ItemTemplate> 36: <DataTemplate> 37: <StackPanel Orientation="Horizontal"> 38: <TextBlock Text="{Binding id}" Width="100"></TextBlock> 39: <TextBlock Text="{Binding timestamp_utc}" Width="140"></TextBlock> 40: <TextBlock Text="{Binding lat}" Width="100"></TextBlock> 41: <TextBlock Text="{Binding lon}" Width="100"></TextBlock> 42: <TextBlock Text="{Binding ozon1h}" Width="100"></TextBlock> 43: <TextBlock Text="{Binding ozon1hMax}" Width="100"></TextBlock> 44: <TextBlock Text="{Binding ozon8h}" Width="100"></TextBlock> 45: </StackPanel> 46: </DataTemplate> 47: </ListView.ItemTemplate> 48: </ListView> 49: </ScrollViewer> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Anmerkung: Da ein Listview auch intern u.a. aus einem Scrollviewer besteht, erscheint es mir etwas seltsam dem inneren Scrollviewer das Scrollen abzugewöhnen und dafür einen außen rum zu definieren. Passender wäre es per Setter in das innere Template einzugreifen.