Netzwerkverbindungen in Windows 8

Paradigmenwechsel flutscht ja schon leicht mal über die Lippen. Windows 8 ist aber definitiv einer. Im historischen Abriss war der Wechsel von DOS auf Windows ein harter, der Wechsel von XP auf Vista ein weicher. Windows 7 und Windows 8 sind, auch wenn optisch und funktionell ähnlich ,in vielen ganz anders. Immer dann wenn die Unterschiede erkennbar werden und man dann über die Gründe und Motivationen nachdenkt und das dann sogar noch in Code gießen soll, wird klar, das wir an der Grenze zu etwas ganz neuem stehen. Am Ende steht die Wende, die Developer Wende. Eine einfache Netzwerkverbindung ist so ein Fall. In .NET egal ob WPF oder Silverlight macht man sich dazu keine Gedanken. Netzwerk ist da oder wenn's schlimm kommt nicht da. In Windows 8 sieht das  etwas differenzierter aus. Es gibt verschiedene Netzwerkverbindungen, Profile genannt. Jedes Profil hat unterschiedliche wechselnde Bandbreiten. Es kann eine Verbindung ins Internet herstellen oder nur ins Intranet. Es kann ein Proxy zum Einsatz kommen und einen Benutzeranmeldung. Die Verbindung kann einen Mobile G3/G4 sein, mit Flatrate, Datenlimit oder pay per Use. Wir können uns in einer Roaming Situation befinden. Das alles kann dauernd wechseln, wahrend ein WCF Call oder Download läuft. Der Witz ist, Entwickler können das in Ihrem Code berücksichtigen oder auch nicht. Der Benutzer wird es nicht direkt merken. Faktisch bräuchte man eine Angabe der Datensignatur ähnlich des CO2 Ausstoßes eines Autos. Der folgende VB.NET Code kann nur einen Bruchteil der Windows 8 WinRT Funktionen zeigen die dafür vorgesehen sind. Ausgangspunkt im Windows.Networking Namensraum ist NetworkInformation. So kann man das aktive Profil auslesen oder eine Liste von allen Profilen. Fuer den Download einer Datei kann man sich dann per Code Logik die dickste Leitung aussuchen. Dim icp As ConnectionProfile = NetworkInformation.GetInternetConnectionProfile() Dim allICP = NetworkInformation.GetConnectionProfiles() For Each i In allICP … i.NetworkAdapter.OutboundMaxBitsPerSecond Next .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 man wissen will, wie viel aktuell die Verbindung kostet muss man die Datenmenge und den Preis dafür feststellen. text2.Text = icp.GetConnectionCost.NetworkCostType Dim data = icp.GetLocalUsage(t.Subtract(New TimeSpan(1, 0, 0)), t) text1.Text = data.BytesSent .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 ist mir nicht gelungen die Datenmenge herauszufinden, die eine einzelne APP  oder die eine andere Anwendung verursacht.

Zwischenablage in Windows 8 und .NET

  Egal ob mit C# oder VB.NET, die Verwendung des Clipboards ist mit .net sehr einfach. So ab Silverlight 4 mit der statischen Clipboard Klasse und Settext. Wie der Name schon sagt Text. WinRT kann aber auch HTML, Bilder, URLs und einiges mehr. Eigentlich ist die zwischen Ablage obsolet im Windows 8 METRO styled APPs Konzept. Die Aufgabe ist viel besser mit den Sharing Contracts (Target/Source) zu meistern. Aber es gibt ja noch klassische Anwendungen die mit Winforms oder WPF entwickelt sind. Da ist neben dem Dateisystem, die Zwischenablage, die einzig sinnvolle und mögliche Form der Interprozess Kommunikation. Es gibt auch in WinRT eine statische Clipboard Klasse die per Clipboard.SetContent einen Typ  Datapackage als Parameter akzeptiert.  Datapackage hat verschiedene Methoden für den jeweiligen Typ wie HTML oder Text. Dim dp As DataPackage = New DataPackage Dim html = HtmlFormatHelper.CreateHtmlFormat("test") DataPackage.SetHtmlFormat(html) dp.SetText("Hallo Welt") .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 folgenden Beispiel wird ein Bild von der lokalen Festplatte gelesen, angezeigt und in die Zwischenablage kopiert. So steht das Bild dann auch z.B. einem Desktop Programm wie Word zur Verfügung. Dim fs As IStorageFile = Await KnownFolders.PicturesLibrary.GetFileAsync("world.jpg") Dim b As IRandomAccessStream = Await fs.OpenAsync(FileAccessMode.Read) Dim bi As BitmapImage = New BitmapImage() bi.SetSource(b) img1.Source = bi Dim dp As DataPackage = New DataPackage Dim ra = RandomAccessStreamReference.CreateFromFile(fs) dp.SetBitmap(ra) Clipboard.SetContent(dp) .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; }   Um ein beliebiges Bild aus der Zwischenablage zu lesen und in einem Image Steuerelement (hier img1) anzuzeigen hilft GetContent. Dim dp As DataPackageView = Clipboard.GetContent() Dim b As BitmapImage = New BitmapImage() Dim img As IRandomAccessStreamReference = Await dp.GetBitmapAsync() Dim s As IRandomAccessStream = Await img.OpenReadAsync() b.SetSource(s) img1.Source = b .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; }   Viel Spass damit und die Fragen wie immer hier im Blog Post.

Bild upload–Bild Download mit WinRT und .net

Wenn Windows 8 Tablet Geräte schon eine Kamera haben, dann sollte man Bilder auch auf eine Website hochladen können. Folgendes VB.NET Beispiel zeigt das auch, Es zeigt aber auch, zum wiederholten male, die Grenzen von WinrRt und .NET. Zentrale Frage war unter anderem auch wie konvertiert man einen Stream nach IRandomAccessStream und umgekehrt. Ersteres liegt im .NET System.IO Namensraum und zweiteres im Windows.Storage.Streams. Die Wahrheit ist, es ist mir bis zum Schluss nicht gelungen ohne aufwendig Byte Arrays dazwischen schieben zu müssen. File Upload Für das Hochladen einer Bilddatei habe ich einen ASHX Handler geschrieben, den ich schon in einem Blog Post verwendetet habe. Dort war es relativ einfach den Stream zu verarbeiten. Dieses METRO Code Sample holt sich das JPG Bild aber von der lokalen Festplatte. IBuffer als Winrt Typ kann aber nicht direkt in einem HTTP Send übergeben werden. Dazu benötigt man die Helper Klasse AsStream. Leider nicht in WinRT enthalten, sondern eben per Extension Methode über den Namensraum System.Runtime.InteropServices.WindowsRuntime einzubinden. Das Problem dabei ist, das Visual Studio 11 Intellisense das nicht vorschlägt, sondern das man das Wissen muss. Ein ungewohnter Zustand. Dim url As New Uri("http://win8-pre9/lab1/upload1.ashx?datei=" & Date.Now.Ticks) wait1.IsActive = True Dim fs = Await KnownFolders.PicturesLibrary.GetFileAsync("world.jpg") Dim b As IBuffer = Await FileIO.ReadBufferAsync(fs) Dim sc As StreamContent = New StreamContent(b.AsStream) 'asstream extension methode aus System.Runtime.InteropServices.WindowsRuntime Dim http As New HttpClient() Dim req As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Post, url) req.Content = sc Dim resp As HttpResponseMessage = Await http.SendAsync(req) wait1.IsActive = False Dim msg As MessageDialog = New MessageDialog(Await resp.Content.ReadAsStringAsync) Await msg.ShowAsync() .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 Rest gestaltet sich ganz einfach. Mit dem HTTPClient ein Post absenden und dabei den Stream hier als StreamContent übergeben. ait1 ist ein XAML Progressring Steuerelement, das ich so ein und ausblende. Datei herunterladen/Download Bei diesem VB/NET WinRT Beispiel habe ich schon an mir zu Zweifeln beginnen, Wie lade ich ein Bild und zeige es an? Das man ein Image Control braucht und ein BitmapImage Objekt, kannte ich aus Silverlight. Der Download mit HttpClient statt Webclient war mir auch bekannt. Es waren die beiden Zeilen dazwischen, die es in sich hatten. Erstens zeigt mir Bitampimage an, das es gerne ein IRandomAccessStream hätte (Winrt) und Resp ist wieder ein System.IO Stream. über den kleine Zwischenweg des InMemoryRandomaccessStream und der Extension Methode asStreamForWrite, klappt es dann doch ganz einfach. Aber ohne die Helper Klasse aus WindowsRuntimeStreamExtensions wären das ungleich mehr Code Zeilen geworden wait1.IsActive = True Dim url As New Uri("http://win8-pre9/lab1/images/welt.jpg") Dim http As New HttpClient() Dim resp As Stream = Await http.GetStreamAsync(url) Dim ras = New InMemoryRandomAccessStream() Await resp.CopyToAsync(ras.AsStreamForWrite()) 'Imports System.IO.WindowsRuntimeStreamExtensions Dim bi As BitmapImage = New BitmapImage() bi.SetSource(ras) image1.Source = bi wait1.IsActive = False .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; }   Kleines Fazit. Die Jungs bei Microsoft aus der WinDiv die für das WinRT verantwortlich sind, haben wenig Ahnung  oder Rücksicht genommen auf die Belange der DevDiv (die .NET gemacht haben). Mit Hilfe der Helper Klassen wird nun versucht dies zu korrigieren.

HTTP Compression mit HTTP Client

Eigentlich ging ich davon aus, das der Network Stack von WinRT so ähnlich funktioniert wie ich das von Silverlight (webclient) kenne. Doch Windows 8 ist anders, es kennt keinen WebClient oder WebRequest. Es gibt dort nur Socket Klassen. Erst im .NET Layer ist eine HTTPClient Klasse vorhanden, die es erlaubt Web Requests abzusetzen. Beispiele dazu habe ich (vb.net) schon in meinem Blog gezeigt. Private Async Function Button_Click_1(sender As Object, e As RoutedEventArgs) As Task Dim http = New HttpClient http.MaxResponseContentBufferSize = 100000 Dim resp = Await Http.GetStringAsync(New Uri("http://www.heise.de")) text1.Text = Windows.Data.Html.HtmlUtilities.ConvertToText(resp) 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 ersten Windows METRO VB.NET Beispiel hole ich den HTML Code aus einer Website und zeige ihn in einem Textblock an. die HTML Tags werden mit ConvertTo entfernt. Eine häufige Fehlerquelle ist, das der Inhalt des Requests plötzlich grösser ist als der Default Wert. Solche Exceptions kommen auch nicht beim User an, sondern erzeugen einfach kein Ergebnis. Der HTML Code der hier übertragen wird, ist sozusagen pur (Zeile 1 im Fiddler Screenshot) . Wenn man diese Website mit dem Browser öffnet wird, diese komprimiert übertragen( Zeile 2). Der Unterschied ist in dem Fall gewaltig, fast die vierfache Datenmenge. Der Grund liegt im HTTP Header. Der Client muss Accept-Encoding gzip und/oder deflate mitsenden, damit der Web Server weis, das er die Daten komprimieren darf. Der Header des METRO Clients Man kann den HTTP Header auch manuell anhängen. Dim http = New HttpClient http.DefaultRequestHeaders.AcceptEncoding.Add(Headers.StringWithQualityHeaderValue.Parse("gzip")) http.DefaultRequestHeaders.AcceptEncoding.Add(Headers.StringWithQualityHeaderValue.Parse("deflate")) http.MaxResponseContentBufferSize = 100000 Dim resp = Await Http.GetStringAsync(New Uri("http://www.heise.de")) text1.Text = Windows.Data.Html.HtmlUtilities.ConvertToText(resp) .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 reduziert die übertragene Daten (Zeile 3) natürlich. Angezeigt werden können diese aber nicht, weil das decodieren der komprimierten HTML Daten fehlt. Man sieht das im Fiddler sehr gut. Im Request Header die zusätzliche Zeile im Client. Im Response sieht man im Textview nur eine Raute. Eben komprimiert. Das Fiddlertool ist so nett und bietet manuell die Möglichkeit zu dekodieren. Aber das reicht für eine laufende Anwendung natürlich nicht. Im HttpClient kann man ähnlich der IIS Pipeline in den Request Prozess per Handler eingreifen und so auch den Header beeinflussen. In diesem Beispiel wird die automatische Kompression aktiviert. Sowohl für den Request als auch den Response. Dim http = New HttpClient(New HttpClientHandler() With {.AutomaticDecompression = DecompressionMethods.Deflate Or DecompressionMethods.GZip}) .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 Ergebnis sieht man im Fiddler Zeile 4.

Wo ist Was? WinRT API

Eine der dauernden Herausforderungen bei Programmierung von Windows 8 METRO APPS ist herauszufinden wo sich aktuell was befindet. In Silverlight verwende ich z.B.  HttpUtility.HtmlDecode ( oder HMTLEncode, UrlEncode, UrlDecode), wie geht das nun in VB.NET oder C# mit der WinRT API? Die Antwort ist, es kommt darauf an. Es gibt im sozusagen nativen Namensraum eine HtmlUtilities Klasse. Windows.Data.Html.HtmlUtilities.ConvertToText("<b>test</>") .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; } Entsprechend der Murphey gibts naturlich keine Entsprechung zu HTMLEncode. Dazu gleich mehr. Im Namensraum gibt es dafür noch eine JSON und DOM Helper Klasse. Windows.Data.Json.JsonArray() Windows.Data.Xml.Dom(XSL) .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 es gibt ja noch .net. Eine komplette Bibliothek auf WinRt aufgesetzt. MIt Garbage Collector und co. So kann man heute auch noch beruhigt einen StringBuilder einsetzen. Der Namensraumist nun WebUtility statt HttpUtility. Fürs leichtere erkennen alles mit System beginnend. System.Net.WebUtility.HtmlDecode("<b>test</>") .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 .NET Assemblies liegen nach wie vor als DLL auf der Platte und werden im Assembly Cache verwaltet. Die Windows Namensräume müssen in Visual Studio 11 nicht extra referenziert werden. Man findet die Beschreibung im :\Program Files (x86)\Windows Kits\8.0\Windows Metadata\ Am Ende ist der Objekt Browser von Visual Studio seit jeher der nützlichste Helfer um Methoden und Klassen zu suchen.

Windows 8 Background Downloader Fast Track

Eine typische METRO APP läuft in der Regel nur exklusiv. Mit der Wisch (wipe) Geste vom linken Rand wird eine andere APP gestartet. Nach 5 Sekunden ist die vorige Anwendung dann für das System nur mehr eine Belastung und wird suspended. Im Task Manager ist das auch gut zu erkennen. Was geschieht, wenn die Anwendung gerade einen Service Call macht? in meinen Tests scheinen die 5 Sekunden in solchen Fällen auch mal bis auf 10 Sekunden ausgedehnt. In der Doku finde ich dazu allerdings bisher nichts. Bei einem lang laufenden Download, wird dieses definitiv unterbrochen. Statt dessen kommt der Background Downloader zum Einsatz. Dim bd As BackgroundDownloader = New BackgroundDownloader() Dim sf As StorageFile = Await KnownFolders.VideosLibrary.CreateFileAsync("581.wmv", CreationCollisionOption.ReplaceExisting) Dim op As DownloadOperation = bd.CreateDownload(New Uri("http://video.ch9.ms/build/2011/wmv/581.wmv"), sf) Await op.StartAsync() .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 die Anwendung suspended wird, läuft der Download nun trotzdem weiter. Erkennen kann man das ganz gut im Taskmanager von Windows 8. Mann kann im onSuspending event auch den Download per  pauseAll anhalten und ihn dann später wieder aufnehmen. Wird die METRO APP beendet (durch ALT F4 oder Wishgeste nach unten) dann bricht der Download nach wenigen (ca 5) Sekunden ab. In einem weiteren Biog Artikel werde ich mich mit ein paar Details wie z.B. BackgroundDownloadProgress beschäftigen.

Semantic Zoom in Windows 8 1x1

Noch immer experimentiere ich mit großen Datenmengen wie sie in typischen Geschäftsanwendungen vorkommen (LOB). Dabei werden viele Tausend Vornamen als Liste dargestellt. Meine Winrt Prototyp soll nun per Sematic Zoom eine Auswahl per Buchstaben ermöglichen, Ähnlich einem Reiter im Telefonbuch. Als Ausgangspunkt haben ich ein Beispiel aus dem METRO SDK genommen, das XAML grouped data controls sample. Das funktioniert zwar weder in der C# noch VB.NET Variante richtig, aber ist ein Code Beispiel. Alles beginnt ganz harmlos mit einem sehr langem String Array Dim sf = Await KnownFolders.DocumentsLibrary.GetFileAsync("vorname.txt") Dim s As String = Await FileIO.ReadTextAsync(sf) Dim liste As String() = s.Split(vbLf) .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; }   Jetzt wird's ein wenig kniffelig. Um im Semantic Zoom die gruppierte Sicht darstellen zu können, braucht man ein gruppierungsmerkmal. In meinem Fall der Anfangsbuchstabe. Allerdings muss auch ein bestimmter Typ (Igrouping) vorliegen. Folgender VB.NET Code fällt in die Kategorie geklaut. Public Class GroupInfo(Of TKey, TItem) 'interface required to show grouped data Implements IGrouping(Of TKey, TItem) Private _source As IEnumerable(Of TItem) Private _key As TKey 'takes a LINQ IGrouping and converts it to a GroupInfo Public Sub New(key As TKey, source As IEnumerable(Of TItem)) _key = key _source = source End Sub Private Function IEnumerable_GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator Return _source.GetEnumerator End Function Public Function GetEnumerator() As IEnumerator(Of TItem) Implements IEnumerable(Of TItem).GetEnumerator Return _source.Cast(Of TItem).GetEnumerator() End Function Public ReadOnly Property key As TKey Implements IGrouping(Of TKey, TItem).Key Get Return _key End Get End Property End Class .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; }   Jetzt kann man mit einem LINQ query aus der Liste die passenden Daten, samt Grouping Info zusammenstellen. Anfangsbuchstabe + voriges Objekt (einfach ein string) Dim q = From p In liste Group p By key1 = p.Substring(0, 1) Into g = Group Select New GroupInfo(Of String, String)(key1, g) .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; }   Auch der folgende Code ist quasi geklaut. Im Xaml wird eine CollectionViewSource definiert als Datenquelle für Grid und Listview. Diese muss das Attribut Groued aktiviert haben. <UserControl.Resources> <CollectionViewSource x:Name="cvs1" IsSourceGrouped="true" /> </UserControl.Resources> .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; }   Wieder im  VB.NET Source Code, fehlen noch zwei Zeilen. Die Linq Query wird als DatenQuelle zugewiesen. cvs1.Source = q ZoomOut1.ItemsSource = cvs1.View.CollectionGroups .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 Zweite Zeile Programmcode verweist auf den Zoomout Part des Semantic Zoom Controls. Kurz gesagt, es gibt ja zwei Sichten, die normale und die komprimierte/gruppierte. Alle Beispiele zeigen eine Zuweisung per Code und nicht per Deklaration, was auch denkbar wäre. Der leere Steuerelement zeigt das Konzept recht gut. ZoomIn ist die normale Ansicht. <SemanticZoom HorizontalAlignment="Left" Margin="278,45,0,0" VerticalAlignment="Top"> <SemanticZoom.ZoomedInView> <GridView/> </SemanticZoom.ZoomedInView> <SemanticZoom.ZoomedOutView> <ListView/> </SemanticZoom.ZoomedOutView> </SemanticZoom> .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; } Wesentlich ist,das nun die CollectionViewsource an das ItemSource Attribut gebunden wird, und ein TextBlock für das Bindung erstellt wird. Das es sich hier um ein einfachen String und kein komplexes Objekt handelt ohne Zusatz in den geschweiften Klammern. <SemanticZoom.ZoomedInView> <GridView x:Name="grid1" HorizontalAlignment="Left" VerticalAlignment="Top" d:LayoutOverrides="Margin" ItemsSource="{Binding Source={StaticResource cvs1}}"> <GridView.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding }"/> </DataTemplate> </GridView.ItemTemplate> </GridView> </SemanticZoom.ZoomedInView> .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 den ersten Test ist das ausreichend, wird aber später einer Erweiterung bedürfen. Eigentlich ganz einfach ist der OutView. da es hier ein Objekt GroupInfo gibt,muss das binding an Group.key erfolgen. <SemanticZoom.ZoomedOutView> <GridView Foreground="White" x:Name="ZoomOut1" > <GridView.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Group.key}" FontFamily="Segoe UI Light" FontSize="24" Foreground="silver" /> </DataTemplate> </GridView.ItemTemplate> </GridView> </SemanticZoom.ZoomedOutView> .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; }   Was hier so locker lässig aussieht, hat mich Stunden gekostet. Schon der kleinste Fehler und man erhält kein Ergebnis und meist auch keine Fehlermeldung, Das debuggen von XAML klapp bei mir nicht (Consumer Preview von Windows 8). Und die Semantische vergrößerte Ansicht. Wenn man nun auf Q klickt, kommt man zwar in die ursprüngliche Sicht zurück, landet aber bei A und nicht bei Q. Es muss noch die Verbindungzwischen den zwei Views hergestellt werden. Dafür braucht das Gridview eine Gruppierung auf den Wert (Key) des Zoomoutviews. <GridView.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text='{Binding Key}' Foreground="Gray" Margin="5" FontSize="18" FontFamily="Segoe UI Light" /> </DataTemplate> </GroupStyle.HeaderTemplate> <GroupStyle.ContainerStyle> <Style TargetType="GroupItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="GroupItem"> <StackPanel Orientation="Vertical"> <ContentPresenter Content="{TemplateBinding Content}" /> <ItemsControl x:Name="ItemsControl" ItemsSource="{Binding GroupItems}" /> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </GroupStyle.ContainerStyle> <GroupStyle.Panel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" MaximumRowsOrColumns="10" /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </GridView.GroupStyle> .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 Windows 8 METRO APP Prototyp funktioniert. Das Ergebnis enttäuscht aber in Bezug auf Performance. Der Wechsel zwischen den Views dauert mehrere Sekunden. Die Dokumentation ist unzureichend und das XAML METRO VB.NET und C# Beispiel funktioniert nicht komplett. Die Fehlersuche wird durch Visual Studio 11 nicht ausreichend unterstützt. Im Vergleich zu Silverlight und WPF ist erheblich mehr Aufwand nötig. Ich werde versuchen, den Use Case zu optimieren.

Schnipp und Weg. Foto mit Windows 8

Als Vorbereitung für das nächste VB.net WinRT Beispiel, wollte ich ein Foto machen. Was unter Silverlight, quasi ein drei Zeiler ist, erweist sich mit WinRT als ziemlich kompliziert. Die System.IO Klassen sind stark verändert. Streams funktionieren ganz anders und es gibt kein einfache Möglichkeit ala WriteableBitmap den Inhalt des Videos zu rendern. Sogar die Anzeige geht ganz anders über ein XAMLCaptureElement Control. Zunächst der Screen mit Start Button, einem ToggleSwitch für die vordere oder Rückwärtige Kamera. Ein Slider für die Helligkeitsregelung und ein Knopf für den Datei Upload. Dazu noch der XAML Code um im nachfolgenden Visual Basic Beispiel die Namen der Steuerelemente zu finden. <Button Content="Button" HorizontalAlignment="Left" Margin="523,-2,-198,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/> <ToggleSwitch x:Name="toggle1" Header="Kamera" Visibility="Collapsed" OffContent="Back" OnContent="Front" HorizontalAlignment="Left" Margin="532,42,-286,0" VerticalAlignment="Top"/> <CaptureElement x:Name="Capture1" HorizontalAlignment="Left" Height="358" Width="490" Margin="10,10,-100,-68" VerticalAlignment="Top"/> <Slider x:Name="sldHell1" HorizontalAlignment="Left" Margin="523,105,-223,0" VerticalAlignment="Top" Width="100"/> <Button Content="upload" HorizontalAlignment="Left" Margin="523,179,-198,0" VerticalAlignment="Top" Width="75" Click="Button_Click_2"/> <Image x:Name="img1" HorizontalAlignment="Left" Height="111" Margin="523,236,-262,-47" VerticalAlignment="Top" Width="139"/> .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; }   Beim starten der METRO App werden die verfügbaren Kameras aufgelistet. In der Regel haben Windows 8 Tablet Computer eine Front und eine Back Kamera. Private mc As MediaCaptureDim cams As DeviceInformationCollectionPrivate Async Sub UserControl17_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded cams = Await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture) toggle1.Visibility = Windows.UI.Xaml.Visibility.Visible 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; } Die Kamera muss gestartet werden, dazu gibt es den Button und das asynchrone Event zum start der Kamera. In appxmanifest muss die Capability Webcam aktiviert werden Der Benutzer erhält als zusätzliche Sicherheitsschranke beim Start noch einen Genehmigungsdialog. Mit dem ToggleButton wird gesteuert welche Webcam angezeigt werden soll. Da ich dieses WinRT Code Beispiel abends geschrieben habe, war mein Bild schwarz. Erst dachte ich mein Code ist fehlerhaft. Dann habe ich zum debuggen mit der Taschenlampe gegriffen und einfach in die Kamera geleuchtet.  Die Settings waren einfach nur zu dunkel. Ganz neue Fehlersucherfahrungen. Jedenfalls habe ich dann noch einen Slider eingebaut um die Helligkeit zu regeln. Die Grenzwerte sind offensichtlich nicht fix, sondern müssen erst aus dem Video Device ausgelesen werden. Private Async Function Button_Click_1(sender As Object, e As RoutedEventArgs) As Task Try Dim mcs = New MediaCaptureInitializationSettings() mcs.VideoDeviceId = cams(CInt(toggle1.IsOn) + 1).Id mc = New MediaCapture() Await mc.InitializeAsync(mcs) Capture1.Source = mc Await mc.StartPreviewAsync() sldHell1.Maximum = mc.VideoDeviceController.Brightness.Capabilities.Max sldHell1.Minimum = mc.VideoDeviceController.Brightness.Capabilities.Min sldHell1.StepFrequency = mc.VideoDeviceController.Brightness.Capabilities.Step Catch ex As Exception Dim msg As MessageDialog = New MessageDialog(ex.Message) msg.ShowAsync().AsTask() End Try 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; } Die Helligkeit regelt man dann, im Slider Event. Der Weg über TrygetValue muss vermutlich genommen werden, weil wir genau an dieser Stelle die Grenze zwischen dem .NET Profil und der WinRT überschreiten (Projection genannt). Intuitiv ist was anderes. Private Sub sldHell1_ValueChanged(sender As Object, e As RangeBaseValueChangedEventArgs) Handles sldHell1.ValueChanged Dim b As Boolean = mc.VideoDeviceController.Brightness.TrySetValue(sldHell1.Value) End Sub   Wenn wir schon bei Intuitiv sind, die folgende Zeilen sind ziemlich aufwändig. Das Beispiel macht das Bild von der Kamera (Screenshot) und lädt es auf einen Webserver hoch. Zum Vergleich ein Silverlight Artikel der WriteableBitmap verwendet. Den Namen des Bildes liefere ich als Querystring mit. Der Upload wird von einem ASP.NET Handler (ashx) am Web Server IIS entgegen genommen. Um das Bild als JOG zu kodieren, kommt die ImageEncodingPropertys Klasse zum Einsatz. Warum man hier als Zeichenkette JPEG übergeben muss und nicht auf eine Auflistung zurück greifen kann, ist mir schleierhaft. Mögliche Werte sind "BMP","ICO","GIF","JPEG","PNG","TIFF","WMP". Der Nebel wird dichter wenn man sich um die Streams kümmert. Da z.B. Datei Operationen asynchron erfolgen müssen, ist vieles aus System.io gestrichen worden und komplett neu und anders wieder in Windows.Storage.Streams auftauchen. Das Beispiel von InMemoryRandomAccessStream bis zum Bytearray habe ich mir mühsam aus der Doku zusammengesucht und dann von meinem Microsoft TAP Betreuer sogar absegnen lassen, weil ich es optisch misslungen finde. Code reuse aus .NET ist hier Fehlanzeige. Der letzte Teil behandelt den Upload per HTTP POST, wie ich es hier im Blog schon mehrfach beschrieben habe. Private Async Function Button_Click_2(sender As Object, e As RoutedEventArgs) As Task Dim http As New HttpClient() Dim url As New Uri("http://win8-pre9/lab1/upload1.ashx?datei=" & Date.Now.Ticks) Dim img As ImageEncodingProperties = New ImageEncodingProperties() img.Subtype = "JPEG" img.Width = 320 img.Height = 240 Dim rs As IRandomAccessStream = New InMemoryRandomAccessStream() Await mc.CapturePhotoToStreamAsync(img, rs) Dim rd As DataReader = New DataReader(rs.GetInputStreamAt(0)) Await rd.LoadAsync(rs.Size) Dim b As Byte() = New Byte(rs.Size - 1) {} rd.ReadBytes(b) Dim sc As ByteArrayContent = New ByteArrayContent(b) Dim req As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Post, url) req.Content = sc req.Headers.TransferEncodingChunked = True Dim resp As HttpResponseMessage = Await http.SendAsync(req) Dim msg As MessageDialog = New MessageDialog(Await resp.Content.ReadAsStringAsync) Await msg.ShowAsync() 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; } Letztendlich noch der ASHX Handler auf dem Web Server. Im IIS braucht der APPpool Schreibrechte, die er z.B auf APP_DATA hat. Hier funktionieren die Streams für Datei Zugriff wie gewohnt. Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest Dim f As String = context.Request.QueryString("datei") Using fs As FileStream = File.Create(context.Server.MapPath("~/images/") & f & ".jpg") SaveFile(context.Request.InputStream, fs) End Using context.Response.ContentType = "text/plain" context.Response.Write("Erfolg") End Sub Private Sub SaveFile(ByVal stream As Stream, ByVal fs As FileStream) Dim buffer(4096) As Byte Dim bytesRead As Integer bytesRead = stream.Read(buffer, 0, buffer.Length) Do While bytesRead <> 0 fs.Write(buffer, 0, bytesRead) bytesRead = stream.Read(buffer, 0, buffer.Length) Loop 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; }

ASP.NET Web API insert mit METRO APP

Folgendes VB.NETCode Beispiel zeigt konzeptionell wie man aus einer Windows 8 METRO styled APP heraus einen neuen Datensatz in per ASP.NET Web API einfügen kann. Ich  muss zugeben, das es sehr mühsam war. Man kann leicht und viele Fehler machen und das debuggend ist extrem aufwändig. Zunächst klappt der es nicht mehr per Fiddler Zugriffe auf localhost zu tracen. Es half nur einen echten IIS 8 zu installieren und den Web Service Zugriff auf den Maschinennamen zu ändern. Dann muss man sich noch drum kümmern, das die Web Anwendung auch per Debugger erreichbar ist, Ich habe ein wenig mit den Datentypen experimentiert. Diese müssen am Server nicht ident, aber ähnlich sein. Also zusätzliche propertys (hier im ASP.NET Service  fake) stören nicht Public Class Customer1 Public Property CustomerID As String Public Property CompanyName As String Public Property Address As String Public Property Region As String Public Property fake As String 'unterschied fuer server End Class .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 man allerdings in der Post Methode String statt Customer1 nimmt, ist das Rückgabe Objekt leer. Keine Fehlermeldung, im Fidddler der trace OK. ' POST /api/<controller> Insert Public Sub PostValue(ByVal value As Customer1) .... End Sub   METRO Client Nun wenden wir unser der Client Seite zu. Auch hier kann man einiges falsch machen. Zunächst serialisert man das Customer1 Objekt nach Json. Ganz wichtig ist den Contenttype korrekt zu setzen. Ein weiterer Fehler ist mir passiert mit dem positionieren des Memorystream Zeigers. Vergessen und schon kein Ergebnis, ohne Fehlermeldung. .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; } Dim uri As Uri = New Uri("http://win8-pre9/lab1/api/customers) Dim cust As New Customer1 cust.CustomerID = txtID.Text cust.CompanyName = txtFirma.Text cust.Address = txtName.Text cust.Region = txtRegion.Text Dim ms = New MemoryStream() Dim djs = New DataContractJsonSerializer(GetType(Customer1)) djs.WriteObject(ms, cust) ms.Position = 0 Dim sc = New StreamContent(ms) sc.Headers.ContentType = New MediaTypeHeaderValue("application/json") Dim res = Await http.PostAsync(uri,sc) res.EnsureSuccessStatusCode() .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; } {"Address":"TestPerson","CompanyName":"TestFirma","CustomerID":"TEST1","Region":"TestRegion"} Nachdem das Beispiel nun funktioniert, habe ich den Content aus Fiddler kopiert und baue das Json Objekt einfach testweise per Hand zusammen. Dim uri As Uri = New Uri("http://win8-pre9/lab1/api/customers") Dim s As New StringBuilder s.Append("{""CustomerID"":""") s.Append(txtID.Text) 's.Append(""",""CompanyName"": """) 's.Append(txtFirma.Text) s.Append(""",""Address"": """) s.Append(txtName.Text) s.Append(""",""Region"": """) s.Append(txtRegion.Text) s.Append("""}") Dim c As HttpContent = New StringContent(s.ToString) c.Headers.ContentType = New MediaTypeHeaderValue("application/json") Dim res = Await http.PostAsync(uri, c) res.EnsureSuccessStatusCode() .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 klappt auch wunderbar. Selbst wenn man Eigenschaften aus dem Json Stream rausnimmt um z.B, die Datenmenge zu reduzieren. Das wäre auch ein Argument für diesen Lösungsansatz. Durch die Verwendung des Post Async Kommandos landet der Request im ASPNET Web Service auch im post Zweig. Dieses .net Beispiel hat den Inhalt im Text Body übermittelt. In der Praxis geht das auch per Querystring über FormUrlEncodedContent. Update und Delete funktionieren dann analog.

ASP.NET Web API mit WinRT METRO App Paging

Einer der sieben Todsünden ist Völlerei, übersetzt wohl auch als Verschwendung zu bezeichnen. Was heute von den Hard uns Softwareherstellern an unnötigen Datenmengen im Netzwerk erzeugt wird, kann man ohne Übertreibung als Überflüssig bezeichnet werden. So basieren die meisten Service Architekturen auf dem Muster “Hole Daten”  - “Ändere Datensatz”. Das wollen wir ändern in “Hole nur die Daten die Du jetzt brauchst”. Altbekannt als Paging. In der ASP.NET Web Service API ist dies vorgesehen, erfordert aber ein wenig Code (hier VB.NET). Zum Vergleich auch den vorigen Blog Artikel Public Function GetValues() As IQueryable(Of Customer1) Dim ctx As New NorthwindEntities Dim q = From c In ctx.Customers Order By c.Customer_ID Select New Customer1 With {.CustomerID = c.Customer_ID, .CompanyName = c.Company_Name, .Address = c.Address} Return q.AsQueryable 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; } Immer wenn man Paging implementiert, muss auch sortiert werden, hier im LINQ Statement mit order by erledigt. Ein Stolperstein ist, das man eine Entity Framework Entität nicht direkt als Rückgabewert verwenden kann, wenn darin eine Relation angegeben worden ist. In meinem Beispiel ist im EF Model Customers und Order referenziert. Fehlermeldung cannot be serialized to JSON because its IsReference setting is 'True' Also muss ich einen manuellen Typ Customer1 zwischenschalten, der per LINQ gefüllt wird. Das hat den Vorteil, das auch das Paging und Grouping wie von Geisterhand erledigt wird. Alternativ kann man andere Serializer verwenden, wie JSON.NET die das wohl können. Die WinRT METRO styled APP Auf der Client Seite wird dann der HTTPRequest abgesetzt mit der Odata REST Query Syntax. Es werden also immer 10 weitere Records abgeholt. Weil ich den DataContract Serializer verwende, muss ein Typ Stream vorliegen. Die Klasse Customer1 habe ich manuell ins Windows 8 METRO Projekt kopiert. Private Async Function vorclick(sender As Object, e As RoutedEventArgs) As Task Dim uri As Uri = New Uri("http://localhost:12933/api/customers?$top=10&$skip=" + (seite * 10).ToString) Dim http As New HttpClient() Dim js = (Await http.GetStreamAsync(uri)) Dim ser As New DataContractJsonSerializer(GetType(List(Of Customer1))) Dim c As List(Of Customer1) = CType(ser.ReadObject(js), List(Of Customer1)) Listview1.ItemsSource = c seite += 1 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; }   Natürlich kann man auch die Methode für zurück implementieren. Mein Windows 8 Prototyp hat den auch in der Tat. <ListView HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="Listview1" Width="200" DisplayMemberPath="CompanyName"/> .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; }