Höhe in Windows 8 UI Element animieren

Heute erreicht mich eine Mail Support Anfrage eines Schulungskunden. Wir bieten ein Jahr kostenfreien Support per eMail nach einem Training an. Jedenfalls fragt mich ein Kursteilnehmer warum er per Animation die Höhe eines XAML UI Elements nicht verändern kann. Soweit ich mich erinnere geht das in WPF und Silverlight. In der Tat läuft folgende Animation nicht ab. 1: <Page.Resources> 2: <Storyboard x:Name="Storyboard1"> 3: <DoubleAnimation Duration="0:0:1" To="100" Storyboard.TargetProperty="Height" Storyboard.TargetName="button" d:IsOptimized="True" /> 4: </Storyboard> 5: </Page.Resources> 6:   7: <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> 8: <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Height="82" Margin="500,100,0,0" VerticalAlignment="Top" Width="242" Click="Button_Click" RenderTransformOrigin="0.5,0.5"> 9: <Button.RenderTransform> 10: <CompositeTransform/> 11: </Button.RenderTransform> 12: </Button> 13:   14: </Grid> Expression Blend liefert mir allerdings den passenden Tipp Es gibt eine Eigenschaft EnableDependentAnimation die auf True gesetzt werden muss und dann klappt es mit der Hight Animation. Laut Doku dient dieses gegenüber Standard XAML geänderte Verhalten der Optimierung. •Die Festlegung der EnableDependentAnimation-Eigenschaft ermöglicht die Ausführung einer abhängigen Animation im UI-Thread. Konvertieren Sie diese Animationen in eine unabhängige Version. Animieren Sie zum Beispiel ScaleTransform.XScale und ScaleTransform.YScale anstelle von Width und Height eines Objekts. Wenn also die Hardware/Grafikkarte keine Animation rechnen kann, macht das Windows 8 nicht, außer ich zwinge ihn zu seinem Glück.   .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; }

Basic Authentifizierung mit HTTPClient und ASP.NET Web API

In meinem letzten Windows 8 Training, entwickelte sich  das Thema Web Services zum beherrschenden Thema. WCF oder Rest? Welche REST Stack (Web API, Dataservices oder gar RIA Services)? Wie Benutzer anmelden? Wir haben uns dann letztendlich für Basic Authentifizierung entschieden, gesichtet über eine HTTPS Verbindung. Das geht relativ einfach und problemlos und ist sicher. Wenn man sich dem HTTPClient hingibt, den es jetzt ja auf allen .NET Plattformen gibt, ist das seltsamerweise sehr komplex. Zunächst muss man dem Webserver mitteilen welche Authentifizierungsverfahren er unterstützen soll. Dabei gibt es drei Stellen in die man eingreifen kann. Die zentralen Config Dateien, die Web.Config der Web Anwendung und die Settings in Visual Studio. Im folgende Beispiel ist Basic Authentifizierung generell erlaubt. In IISExpress SSL und Windows aktiviert In der Web Config <authentication mode="Windows"></authentication> Eine ASP.NET Web Api Service Methode wird mit dem Authorize Attribut abgesichert. 1: <Queryable()> 2: <Authorize()> 3: Function GetDriveMappings() As IQueryable(Of DriveMappings) .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 erste check ist nun der Aufruf des Services im Browser. Dummerweise klappt das auf anhieb. Warum? Es sollte doch eigentlich nur ein angemeldeter Benutzer möglich sein? Ist er auch. Ein Breakpoint in der Web Api Routine und eine Abfrage von User.Identity.Name offenbart den aktuellen Windows Benutzer. Wie funktioniert das nun? Der Browser fordert die URL an und der Server antwortet einer 401 Statusmeldung und den möglichen Authentifzierungsverfahren. Hier Basic, NTLM und Negotiate für Kerberos. Der Browser verwendet die für ihn sicherste Methode, braucht aber noch einen zusätzlichen Request um eine verschlüsselte Sicherheitstoken Kommunikation sicher zustellen. Also am Ende drei Requests. Für einen Windows 8 (Modern UI/Metro) Client wird in der Regel nicht automatisch der angemeldet Windows Benutzer verwendet. Die Hilfsklasse Netzwork Credentials enthält auch eine Eigenschaft UseDefaultCeredentials mit der auf den angemeldete User zurückgegriffen wird. Im folgenden VB.NET Sample Code Snippet kommt ein Demo User zum Einsatz. 1: Dim h As HttpClientHandler = New HttpClientHandler() 2: Dim c = New Net.NetworkCredential("demo", "demo") 3: h.Credentials = c 4: Dim http As New HttpClient(h) 5: 'http.BaseAddress = New Uri("https://localhost:44301") 6: http.BaseAddress = New Uri("http://localhost:27536/") .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 hier ist es so, das die Verschlüsselung ausgehandelt wird. Man kann also nicht BASIC erzwingen, außer man schaltet am Webserver “Windows” wieder aus. Nicht zu verwechseln mit dem Eintrag in der Web.Config, der nur darauf hinweist, Windows als Benutzerdatenbank zu verwenden. Der zweite request sendet dann schließlich Username und Passwort mit Nie vergessen, Username: Passwort ist nur Base64 codiert und lässt sich decodieren. Also immer die Kombi Basic und HTTPS verwenden.

Authentifizierung eines ASP.NET Web API REST Services mit Basic Clear Text

Mit einem Methoden Attribut, lässt sich ein kompletter REST ASP.NET Web Api Controller oder explizit eine CRUD Methode vor unerlaubten Zugriff schützen. 1: <Authorize(Roles:="Benutzer")> 2: Function GetDriveMappings() As IQueryable(Of DriveMappings) .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; } Dabei wird nur geprüft ob die Rechte des angemeldeten (authenticated) Users ausreichen. Um die Benutzer Anmeldung abzuhandeln kann man sich der eingebauten Methoden (Modules) des IIS bedienen. Per web.config wird eingestellt, das die Windows Benutzerdatenbank verwendet wird, um die Konten zu verwalten. 1: <authentication mode="Windows"></authentication>   In einem anderen Blog Artikel habe ich gezeigt, wie man den Membership Provider statt dessen  als Benutzer Datenbank verwendet. Wenn ein Browser einen geschützte Seite anfordert, antwortet der Server mit einer 401 Statusmeldung und liefert im Header das gewünschte Authentifizierungsverfahren mit. Es braucht also intern zwei Requests und eine Benutzereingabe von User und Passwort um die gewünschte Seite zu bekommen. Für einen Service Aufruf ist das ungeeignet. Man muss die Credentials schon beim Initialen Request mitgeben. Wie das auszusehen hat, kann man gut erkennen, wenn man per Fiddler den zweiten, erfolgreich authentifizierten Request, betrachtet. Benutzer und Passwort werden im HTTP Header Base 64 codiert mitgeliefert, getrennt durch einen Doppelpunkt. Hier wurde einfach der Header aus dem Web Request kopiert. Es gibt auch Online Base64 En-Decoder. In der Praxis wird man dafür ein wenig .net code verwenden. 1: Dim encodedText = Convert.ToBase64String(Encoding.UTF8.GetBytes("demo" + ":" + "demo")) .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 diesen Basic authentication String aus einer WinRT Anwendung per HTTP Request mitzugeben, greift man direkt in den HTTP Header des HTTPClients ein. 1: Dim http As New HttpClient() 2: http.DefaultRequestHeaders.Authorization = 3: New Headers.AuthenticationHeaderValue("Basic", "ZGVtbzpkZW1v") 4: Dim jsondata = 5: Await http.GetStringAsync(New Uri("http://localhost.fiddler:27536/api/DriveMappings/")) .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 alles gut geht, sollte Fiddler dann einen 200 Code anzeigen. Hinweis: immer https anschalten. Basic Clear Text heißt nicht umsonst so. Murphy Es gibt allerdings eine Menge Dinge die dabei schief laufen können. Wenn man IISExpress verwendet, muss Basic Authentifizierung erst aktiviert werden. In den Einstellungen in Visual Studio sucht man das vergeblich. Im IIS 7 wird die Authentifizierung über ein User Interface eingestellt. Beim Express muss man direkt in die applicationhost.config Datei. Diese findet man unter C:\Users\USER\Documents\IISExpress. Dort im Bereich authorization 1: <authentication> 2: <anonymousAuthentication enabled="true" userName="" /> 3: <basicAuthentication enabled="true" /> .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; } Fiddlertipp Um den Traffic bei Win8 RT (Modern UI, METRO) Apps mitzubekommen muss man in den Fiddler WIn8 Settings die APP erlauben und nicht localhost verwenden.( Zeile 5 HTTPClient Sample)

Windows 8 TextBox clear Kreuz

Das Kreutz mit dem Kreuz. In meiner letzten Windows 8 Store App (METRO) Schulung fragte mich ein Kurs Teilnehmer, warum in der Textbox kein Kreuz zu sehen ist um den Textinhalt zu löschen. Ich war mir sicher früher war das da Technisch gesehen muss es sich dabei um einen Button innerhalb der Textbox handeln. Da ich mir nicht sicher war ob da wieder XAML stiefmütterlich von den Entwicklern aus Redmond behandelt wurde, der Cross Check mit JavaScript und HTML (mit Überwindung). Und in der Tat, sobald man in der laufenden Anwendung Text eintippt erscheint das x mit dem der Inhalt der Textbox geleert werden kann. Nun zurück zur XAML Store App. Mit Expression Blend kann man die Textbox gut in seine Bestandteile zerlegen und so den Deletebutton ausfindig machen. Dieser Button ist in den Eigenschaften als Collapsed definiert und damit nicht sichtbar. Man kann im XAML Source Code gut erkennen das einen eigene Stategroup  ButtonStates im VisualStateManager deklariert ist. Jetzt gilt es nur mehr das Property zu finden das den Statuswechsel intern in der Textbox Logik auslöst und das Kreuz sichtbar macht. 1: <TextBox HorizontalAlignment="Left" Margin="55,37,0,0" 2: TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="149"/> .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; } Und der Übeltäter ist Textwrapping. Ohne diese oder mit NoWrap ist das x wieder da. War das schon mal anders, in der Beta? Keine Ahnung, könnte sein. Ganz logisch ist es nicht.

XAML WebView Control

In einer Windows 8 Development Schulung kam die Frage auf das WebView Control. Dies eignet sich zum Anzeigen von HMTL und basiert auf dem IE 10. Nichts desto trotz ist es erheblich in seiner Funktion beschnitten. Mutmaßlich aus Gründen der Sicherheit, kann man in keiner METRO App ein XAML UI Element über das WebView Control legen. So wird ein Charme, der ja vom Betriebssystem kommt noch im Vordergrund erscheinen. Der weitere Settingsdialog ist aber hinter dem Webview Control und damit nicht sichtbar. Einziger Workaround für AppBar und Settings ist, das Webview einfach zu verkleinern, bzw. zu verschieben. Während man ein Popup dargestellt, sind diese beiden UI Elemente  dann nebeneinander. Wenn man einen MessageDialog braucht, ist dieser Ansatz kaum zu machen. Üblicherweise werden dann statt dem Webview ein Bild der aktuellen Darstellung überblendet und das Webview deaktiviert. Dabei hilft der WebViewBrush. Das folgende VB.NET Code Schnippsel ist aus dem Windows 8 Winrt SDK entnommen If Rect1.Visibility = Windows.UI.Xaml.Visibility.Visible Then Dim b As New WebViewBrush() b.SourceName = "WebView6" b.Redraw() Rect1.Fill = b WebView6.Visibility = Windows.UI.Xaml.Visibility.Collapsed 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; } .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; } Als nächstes habe ich mich den Innereien zugewandt. Üblicher HTML und JavaScript Code funktioniert meist recht gut. Allerdings hat der IE 10 nach wie vor erhebliche Meinungsunterschiede wie der Standard HTML5/CSS3 und Java Script interpretiert werden sollen. Popup Dialoge wie alert oder confirm werden komplett unterdrückt. In meiner Aufgabenstellung wollte ich ein Stück Text markieren und in der WinRT App weiter verwenden.  Es gibt zwar ein Event ScriptNotify, aber man muss vorher deklarieren für welche Quellen das gilt. Wenn man das vergisst wartet man vergeblich auf das Notification Event.Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded Dim uris = New List(Of Uri) uris.Add(New Uri("http://localhost:21916/")) webview1.AllowedScriptNotifyUris = uris AddHandler webview1.ScriptNotify, AddressOf JScriptEvent End Sub Private Sub JScriptEvent(sender As Object, e As NotifyEventArgs) txtHTML.Text = e.Value 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; } Im JavaScript Teil ist der Aufruf mit einer Zeile Quellcode erledigt. Hier wird ein Button dazu verwendet. Allerdings musste ich Code klauen um den selektierten Text in den gängigen Browsern zu erhalten.function ButtonSelectedText() { window.external.notify(getSelText().toString()); } function getSelText() { var SelText = ''; if (window.getSelection) { SelText = window.getSelection(); } else if (document.getSelection) { SelText = document.getSelection(); } else if (document.selection) { SelText = document.selection.createRange().text; } return SelText; } .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; } .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; } Bisher habe ich keine Möglichkeit gefunden aus WinRT heraus JavaScript im WebView Control zu debuggen. Weiters sollte der selektierte Text per Context Menü übernommen werden können. Was im Browser noch funkltioniert verweigert im Webview seinen Dienst. Deswegen habe ich hier den Umweg über ein MouseUp Event verwendet um den markierten Text in einen Zwischenvariable gelegt.<div class="context-menu-html5menu box menu-1"> Context Menü das ist ein wenig mehr text und wir werden sehen</div> <div id="out"></div> <menu class="showcase" id="html5menu" type="context" style="display: none;"> <command onclick="ContextMenuSelectText()" label="Markiert"></command> <command onclick="some code ” label="Entfernen"></command> </menu> <script> var txt=''; $(document).ready(function () { $(document).bind("mouseup", function () { txt = getSelText().toString(); }); }); $(function () { $.contextMenu({ selector: '.context-menu-html5menu', items: $.contextMenu.fromMenu($('#html5menu')) }); }); function ContextMenuSelectText() { window.external.notify(txt); } .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; } .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; }        .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; }

Startbutton Total Egal

Gestern Abend habe ich mein sehnlichst erwartetes Surface Tablet in den Schrank gelegt und meinen knapp 3 Jahre alten Fujitsu T900 wieder hervorgeholt. Mit knappen Worten: das ist es nicht, zumindest für mich. Doch mal von vorne. Als bekennender Fan von Microsoft verdiene ich meine Brötchen damit, Werte auf die Produkte aus Redmond aufzustöpseln (added value) und damit diese beim Kunden zu Lösungen zu machen. Wie viele Millionen andere Menschen täglich. Ein Geschäftsmodell, dem ich ziemlich viel abgewinnen kann. Apple mit seinen strengen Regeln und Geheimnistuerei hat mich immer abgestoßen. LINUX mit seiner elitären leicht kommunistischen Eigendarstellung entspricht nicht meinen Bild von Wirtschaft. Eine Plattform, smarte Leute, die Freiheit damit bauen zu können was man will. Ein bisschen Lego halt. Meine Welt. Als bekennender Fan ( ich glaub der Leser hat Anrecht die politische Einstellung des Autors zu kennen) pilgerte ich genau vor einem Jahr zur //BUILD nach Los Angeles. Windows 8 erstmalig und beeindruckend. Dazu ein super Device um damit entwickeln zu können. Allerdings waren erste Schatten erkennbar. Warum dieser Hype um dieses schrottige JavaScript? [FlashBack] Was habe ich gekotzt für ASP.NET JavaScript in den verschiedenen Browsern zu schreiben. Miese Editoren,  Debugging Hölle, Browser, langsam, groß, Sprachen, alles. Mit Silverlight 1 wurde in JavaScript “programmiert”. Wie toll war dagegen die Version 2 mit .net. [/FlashBack] Warum dieses Geheimnistuerei? Warum ein closed Store Konzept? Was tut die ISV Branche die komplett eigene, teilweise konträre Geschäftsmodelle hat? Hat mein bei WP7 nicht gelernt, das APPLE kopieren gar nicht funktioniert? Und überhaupt wo ist die Liebe, die liebe zu uns Microsoft XAML Guys. Irgendwie hat man das Gefühl Microsoft schielt ausschließlich auf die andere Seite. Fachbegriff compete Dann begann eine lange Zeit des Leidens. Viele Bugs. Doch wesentlich größere Unterschiede im Code als ursprünglich vermutet. Viele Kollegen und auch Microsoft Mitarbeiter sprechen unter der Hand mit mir und teilen die Einschätzung und wesentlich mehr. Infos die ich nicht teilen kann, weil Vertraglich verboten oder einfach aus Respekt vor meinem Gegenüber. Wir haben investiert. Viele Samples geschrieben. Jede Menge Blog Einträge verfasst. Wir waren bereit. Wir wollten Investieren. Alle unsere Schulungsräume auf Windows 8 Ready mit neuen Touch Monitoren ausstatten (und das sind hunderte). Schlicht es gibt keine zu kaufen. (Mir hallt noch der Spruch von der //BUILD Keynote – A device without touch – is a broken device).  Wir haben als treuer Windows 7 Fans ( auch das konnte man bereits ganz gut touchen) eine größere Anzahl All-In-One Devices von HP und Lenovo angeschafft. Mal erkennt der Touch Treiber nur einen statt zwei Punkte. Mal geht die Cam nicht. Support? Alles läuft wie bisher nur besser- broken. Mein täglicher Begleiter der Fujitsu T900 mit Windows 8. SmartCase Logon +: crasht. Meine Webcam, geht native, aber nicht unter Windows 8 APPs. Mein Schocksensor- kein Treiber,mein wunderbares Dual Digitizer Display: Handballen Erkennung futsch: broken. Gestern bei ALDI Nord ein neues Windows 8 Notebook für 499- ohne Touch Display: broken. Besuch bei Mediamarkt… wunderbarer Apple Stand, Windows 8: lassen wir das. Kommen wir zum Shopping Erlebnis, Windows Surface bestellen. Die User Experience des Microsoft Stores benötigte von mir 44! Anläufe um das Gerät zu ordern. Der Service egal Telefon oder Chat: broken. Jetzt ist es da und es fehlt das Keyboard. Einhellige Meinung im Web: ihr hättet es Amazon machen lassen sollen. Die können das ( ein typischer Projekt Managment Fehler, Tasks im Projekt die man noch nie gemacht hat). Zeitsprung: In der Zwischenzeit habe ich eine Windows 8 APP geschrieben (eigentlich viele), aber diese eine, wollte ich der Welt gratis zur Verfügung stellen. Nicht das übliche irgendein Service im Grid darstellen, sondern ein Share 2 Sharepoint. Also ein Foto ganz einfach nach SharePoint hochladen. Die Entwicklung des Prototypen in Winforms hat ziemlich genau 40 Minuten gedauert. Das Umsetzen nach WinRT knapp 8 Stunden. Klar, es war dann noch der SettingsDialog dabei. Das Anwenden der Store Richtlinien weitere 2 Tage. Nach 7 erfolglosen versuchen, mit jeweils rund 7 Tagen Wartezeit, die APP zu veröffentlichen ist diese noch immer nicht im Store: broken. Der ganze Submission Prozess, entspringt einen zentralistischem, wir kontrollieren alles von USA aus Gedanken: broken Gestern Nachmittag war es dann soweit. Das erste Mal Surface Tablet in den Händen halten. Auspacken, fühlen einschalten. Konfigurieren. Alles Super, Um Ecken besser wie meine erste iPad Experience [Flashback] Alle reden davon, da muss was dran sein. Ein iPad muss her. Auspacken, einschalten. 64 MB downlod für Itunes –keine DVD?, Account konfigurieren, Kreditkarte eingeben- sonst keine Nutzung möglich WTF? Das Ding spiegelt wie Sau und was soll ich damit? Nach 2 h ins Eck [/Flashback] Klein leicht, schick sexy, 2 Kameras, Ständer, USB, Bluetooth, usw. Alles cool. Ausstattung, Software passt. Überraschenderweise sogar Desktop APP. Die Kollegen sind begeistert. “Will auch haben” [Zuhause] Hmm was mach ich jetzt damit. Ein bisschen Surfen. Website da, Website dort- leichte Darstellungsprobleme, kein Silverlight. (was hat der Manager geraucht, der entschieden hat Flash für ARM zu delivern, aber kein Silverlight). Word ausprobiert. Doc aus Skydrive geladen. Alles gut. Just Working. Dann kam der Zeitpunkt des Vermissens. Aufgrund schwerer persönlicher Störungen zwischen der Windows Suche und mir, haben wir uns im gegenseitigen Einvernehmen vor Jahren getrennt. Neuer Partner Agent Ransack. Volltextsuche auf SSD macht Laune. Aber nicht auf ARM. Visual Studio natürlich auch nicht. Also Tablet auf die Seite gelegt,  T900 aufgemacht, durchgeatmet und zuhause gefühlt. [Update]das Keyboard ist heute gekommen ( in weiß). Cooles innovatives Teil. Und heute Abend werde ich einmal Remote Debugging probieren. [/UPDATE] [Disclamer] Ich weis das unheimlich viele Leute meine Blogs und Facebook Einträge lesen und dies nicht kommentieren. Es ist ein Zeichen persönlicher Wertschätzung. Ich schätze es sehr, wenn Du lieber Leser auf einem der beiden Kanäle dazu beitragen. [/Disclamer] [Loveletter] Ich bin ein Fan von Microsoft, noch immer. Man sieht, sie lernen. Windows 8 macht Sinn und ist in weiten Teilen sehr gut geworden. Touch macht Sinn. Das Surface (wie so gut wie jedes Stück Micrsoft Hardware): Gratulation gelungen. [/Loveletter]

Windows Forms Call WinRT

Vor längerer Zeit hat Holger Schwichtenberg schon einmal das Thema aufgegriffen und gestern Scott Hanselman in seinem Blog. Wie kann man Funktionen von Windows 8 in Windows Forms (oder WPF etc.) nutzen. Ganz konkret API calls aus WInRT. Das ist eigentlich von Microsoft nicht vorgesehen und deshalb mit Vorsicht zu genießen. In diesem VB.NET Beispiel wird der Lichtsensor angesprochen. Das kann man seit Windows 7 über die location und Sensor API tun. Es gibt sogar eine passende Erweiterung für Silverlight die native Extensions. Trotz all dem soll die Einbindung von Winrt in Windows Forms mit Visual Basic demonstriert werden. Um eine Referenz erstellen zu können muss in vbproj File eine Ergänzung vorgenommen werden. <PropertyGroup> ... <TargetPlatformVersion>8.0</TargetPlatformVersion> </PropertyGroup> .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 nun den Lichtsensor anspricht, weist einen Visual Studio Intellisense darauf hin, das die Referenzen fehlen. Es muss natürlich noch die Reference auf die WinRT API erstellt werden. Nun kann ein Objekt für das Sensor Handling instanziiert werden. Public Class Form1 Dim light As LightSensor = LightSensor.GetDefault() .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; } Als nächstes wird auf die Änderungen des Sensors reagiert und das passende Event registriert. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load If light IsNot Nothing Then Dim minReportInterval As UInteger = light.MinimumReportInterval Dim reportInterval As UInteger = If(minReportInterval > 16, minReportInterval, 16) light.ReportInterval = reportInterval AddHandler light.ReadingChanged, AddressOf licht End If 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; } Nun kann man zur Ausgabe der Daten im UI schreiten. Da WinRT typisch die Helligkeitswerte nicht im UI Thread kommen, per Invoke die Daten mit der UI synchronisiert. Die Klasse Dispatcher ist in Winforms nicht vorhanden. Private Sub licht(sender As Object, e As LightSensorReadingChangedEventArgs) Me.Invoke(Sub() Label1.Text = e.Reading.IlluminanceInLux) 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; }

Dont make me think. Textbox Eingaben Filtern

Üblicherweise kennt man seine Pappenheimer. Im Falle von Windows 8 Store APPs, kennt man sie aber nicht. Nicht mal den Tester kennt man, geschweige denn könnte man nachfragen oder ihn gar anrufen. Die Aufgabe die ein Benutzer bekommt. Schreibe hier deinen Server Namen hin. Ergebnis bisher: http://Server.de //server.de https://server.de //server Natürlich könnte man episch den Windows Dialog füllen mit umfangreichen beschreibenden Hinweisen. Ich bin allerdings der Meinung das “Server Name” reichen muss. Kürzere Texte lassen sich schneller lesen. Darüber hinaus liest doch ohnehin keiner mehr was. Oder man baut umfangreiche Logik, die die Daten sozusagen normalisiert. Besser wäre es, wenn der Benutzer genau weis, was da rein gehört, bzw. einen eindeutigen Hinweis erhält wenn es nicht stimmt. Ich habe schon beschrieben, das eine Fehlermeldung direkt über der Textbox das beste Feedback an den User gibt. Jetzt stellt sich die Frage, wie filtert man die Tastatureingaben in der Textbox? Es gibt jedenfalls keine Möglichkeit, direkt z.B. mit regular Expressions Tastaturcodes zu filtern. Es gibt auch kein FilteredTextbox Steuerelement direkt von Microsoft. Der nächste Ansatz lässt einen das Keydown Event der Textbox vermuten, wie folgender einfacher VB.NET Code zeigt. Private Sub text1_KeyDown_1(sender As Object, e As KeyRoutedEventArgs) output1.Text = e.Key.ToString + "-" + e.KeyStatus.ScanCode.ToString 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; } Allerdings sind das wirklich die Keys. Also sowohl Doppelpunkt : als auch Punkt . liefern auf einer deutschen (und nur dort) den gleichen Code, nämlich 190. Manche Szenarien, wie die Beschränkung auf numerische Eingaben lassen sich in der Tat so lösen. Einfach nur im Filterfall e.handled=true und schon verschwindet das Zeichen. (Scancode ist 52 und ebenfalls ident). Man kann allerdings die Zeichen auslesen mit dem CharacterReceived Event. Das ist einigermaßen kompliziert über Umwege in .NET WinRT zu erreichen. Im folgenden VB Beispiel ergibt sich so für den : 58 und für den . 46. Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded Dim c As CoreWindow = CoreWindow.GetForCurrentThread AddHandler c.CharacterReceived, AddressOf somenewkeyarrived End Sub Private Sub somenewkeyarrived(sender As CoreWindow, args As CharacterReceivedEventArgs) output1.Text = args.KeyCode args.Handled = True 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; } Leider hilft uns das kaum weiter. Wir wissen zwar das böse / oder : gekommen sind, aber was tun? Kommen wir zum Dritten Ansatz, das Event TextChanged. Hier wird einfach der gesuchte Character per Replace wieder ersetzt. Allerdings steht dann der Cursor in der Textbox am Anfang und muss mit zwei Zeilen wieder in die vorige Position gesetzt werden. Private Sub text1_TextChanged_1(sender As Object, e As TextChangedEventArgs) If sender.text.contains(":") Then output1.Text = sender.text Dim i = sender.selectionstart sender.text = sender.text.replace(":", "") sender.selectionstart = i - 1 End If 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; } Das funktioniert zwar, benötigt aber eine Menge Code im User Interface. Wünschenswert wäre ein XAML Attribut ala FilterCharacters. Wenn man sich mit MVVM beschäftigt, bleibt ohnehin nur das Event Lostfocus. Erst danach wird ein gebundenes Datenobjekt aktualisiert und kann auf Gültigkeit überprüft werden. Einen UpdateSourceTrigger wie in WPF gibt es jedenfalls auch nicht in WinRT XAML.

ASMX Service unter Windows 8 Store APP nutzen

Die guten alten ASP.net Web Services sollten eigentlich schon seit .net knapp 10 Jahren durch WCF abgelöst werden. Aber ASMX findet sich auch heute noch, teilweise auch neu implementiert in vielen Software Projekten. Es ist einfach zu programmieren, zu hosten und zu troubleshooten. Selbst SharePoint bietet eine Reihe von Schnittstellen als ASP.NET Web Service an. Als Basis dient folgender sehr einfacher ASMX Service, der in einem eigene Projekt innerhalb der Windows 8 Store APP Visual Studio 2012 Solution angelegt wird (vormals METRO). Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.ComponentModel System.Web.Services.WebService(Namespace:="http://tempuri.org/")> _ <System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ <ToolboxItem(False)> _ Public Class WebService1 Inherits System.Web.Services.WebService <WebMethod()> _ Public Function GetKunden() As List(Of kunde) Dim l As New List(Of kunde) l.Add(New kunde With {.ID = "1", .isMale = True, .FamName = "Mayer", .Vorname = "Klaus"}) l.Add(New kunde With {.ID = "9", .isMale = False, .FamName = "Becker", .Vorname = "Verona"}) l.Add(New kunde With {.ID = "13", .isMale = True, .FamName = "Weber", .Vorname = "Gerhard"}) l.Add(New kunde With {.ID = "31", .isMale = True, .FamName = "Müller", .Vorname = "Thorsten"}) Return l End Function End Class Public Class kunde Property ID As String Property Vorname As String Property FamName As String Property isMale As Boolean 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; } Um diesen Service zu nutzen erzeugen die meisten im Visual Studio Projekt per Add Service Reference und URL des Services die Proxy Klassen im Projekt. In einer Winforms Anwendung (nach wie vor) kann man das mehr oder minder direkt über den Zusatzdialog “Advanced” tun. Diese Option fehlt bei Windows 8 Apps, schlicht weil es dort kein .net 2.0 mehr gibt. Auf den ersten Blick ist das auch nicht nötig, weil man das auch direkt ganz gut hinbekommt. Allerdings muss im Gegensatz zu Windows Forms in der WinRT, die Kommunikation Asynchron abgehandelt werden. Das erzeugt ganz neue Problemstellungen für den .NET Code bzw. der VB.NET Source kann nicht direkt wieder verwendet werden. Dim svc As New ServiceReference1.WebService1SoapClient Private Async Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded Dim resp = Await svc.GetKundenAsync() liste1.ItemsSource = resp.Body.GetKundenResult 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 nötige XAML Code mit einem Listeview Steuerelement in der Page <ListView x:Name="liste1"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding VorName}" Padding="0,0,5,0"/> <TextBlock Text="{Binding FamName}" FontWeight="Bold" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> .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 die Ausgabe der Windows 8 APP Noch ein Paar Hintergrund Infos. Da Windows 8 .NET 4.5 verwendet, kommen eigentlich WCF Proxys zum Einsatz. Microsoft hat allerdings im WCF Bereich ob des konfigurations Desasters auf die App.Config verzichtet. Statt “Contract First” lautet nun die Devise Konvention über Konfiguration. Also wo findet man nun die nötigen Parameter. Im Code- der reference.vb. Den kann man natürlich ändern, muss aber in Kauf nehmen, das wenn die Service Reference aktualisiert wird, die Änderungen weg sind.

Eingabefehler abfangen in Windows 8 Store APP’s

Anders als in WPF oder Silverlight, hat das XAML UI Framework von WInRT keine Templates oder Mechanismen um Fehler an den Benutzer zu melden oder ihn zu leiten. Also ist in WinRT Handarbeit angesagt für das Errorhandling. Optisch erscheint es sinnvoll, über eine Textbox einen rot eingefärbten Textblock zu platzieren, die über das Visbility Attribut gesteuert wird. Um es einheitlich im Layout handhaben zu können, bietet sich eine Gruppierung mit Stackpanel an. Nun stellt sich díe Frage, wie man die Logik unterbringt. VB.NET Entwickler werden aus jahrelanger Erfahrung gerne zum lostfocus Event greifen. Das Problem ist, das man auch das Event GotFocus nutzen muss, um die Fehlermeldung wieder auszublenden, wenn der Benutzer den Cursor in die Textbox zurück setzt. Folgender VB.NET Code prüft auf ein X und meldet dann einen Fehler. Private Sub TextBox_LostFocus_1(sender As Object, e As RoutedEventArgs) If sender.text.contains("x") Then errorTextbox1.Visibility = Windows.UI.Xaml.Visibility.Visible End If End Sub Private Sub TextBox_GotFocus_1(sender As Object, e As RoutedEventArgs) errorTextbox1.Visibility = Windows.UI.Xaml.Visibility.Collapsed 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 Code sieht etwas seltsam aus (wo ist der Typcast) funktioniert aber so unter Windows 8. Wenn man allerdings Databinding verwendet, ergeben sich neue Problemstellungen. Ich setze hier eine Klasse class1 mit einem Property prop vom Typ String. <TextBlock Foreground="Red" Text="Bitte die Eingabe überprüfen" Visibility="Collapsed" x:Name="errorTextbox1"></TextBlock> <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding prop,Mode=TwoWay}" GotFocus="TextBox_GotFocus_1" LostFocus="TextBox_LostFocus_1" VerticalAlignment="Top"/> .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 Zuweisung der Datenquelle erfolgt über den DataContext Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded Dim c As New Class1 c.prop = "test" Me.DataContext = c 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; } Um auf den Inhalt des Datacontext zugreifen zu können, wird man unter Umständen, die vorige Codezeile einfach umdrehen. Das klappt aber in Lostfocus nicht, bzw liefert den letzten Inhalt der Textbox und nicht den aktuellen. Also folgender Code erzeugt ein falsches seltsames verzögertes Verhalten. Private Sub TextBox_LostFocus_1(sender As Object, e As RoutedEventArgs) If Me.DataContext.prop.Contains("x") Then errorTextbox1.Visibility = Windows.UI.Xaml.Visibility.Visible End If 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; } In WPF lässt sich das mit der BindingExpression und UpdateSource umgehen. Ich habe aber keine WinRT .NET Entsprechung gefunden. Ist auch nicht essentiell, da es wahrscheinlich sinniger ist, die mit zwei Events nötige komplexe Logik in die Hände des Class1 Objektes zu geben. Diese bekommt dazu eine Eigenschaft isValid, die dann folgend an die ErrorTextbox gebunden wird. Da allerdings die Textbox zwar in das Objekt schreiben kann, aber nichts davon mitbekommt, wenn sich der Inhalt des isValid Propertys nachträglich ändert, muss diese davon unterrichtet werden. Das wird über ein Nachrichtensystem implementiert, das das Interface InotifyPropertyChanged mitliefert. Wenn sich isValid verändert wird dann per Code das Event erzwungen (raisevent). Ergänzend: in .NET 4.5 gibt es nun einen Methode CallMemberName, die etwas weniger Code benötigt. Public Class Class1 Implements INotifyPropertyChanged Private _prop1 As String Public Property prop() As String Get Return _prop1 End Get Set(ByVal value As String) If value.Contains("x") Then isValid = Visibility.Visible Else _prop1 = value isValid = Visibility.Collapsed End If End Set End Property Private _isValid As Windows.UI.Xaml.Visibility Public Property isValid() As Windows.UI.Xaml.Visibility Get Return _isValid End Get Set(ByVal value As Windows.UI.Xaml.Visibility) _isValid = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("isValid")) End Set End Property Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged 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; } Über die Implementierung von Überprüfungslogik kann man trefflich diskutieren. Wahrscheinlich wird man in der Praxis noch ein Fehlerobjekt mit Details mitführen. In Silverlight musste man z.B. Exceptions auslösen. Wie man sehen kann, hat man nun nur mehr Code an einer Stelle (im Datenobjekt) und keinen Code mehr in der Seite (im View). Richtig, das deckt sich mit dem Design Pattern MVVM. Die Bindung in XAML ist nun ein leichtes und beinhaltet keine Überraschungen. <StackPanel> <TextBlock Foreground="Red" Text="Bitte die Eingabe überprüfen" Visibility="{Binding isValid}" /> <TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding prop,Mode=TwoWay}" .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; } Als Steigerungsstufe (ab hier optionales weiterlesen) wird auch noch der Code aus dem Load Event des Views eliminiert. Auch das geht per Binding im XAML. <Page.Resources> <local:Class1 x:Key="class1"/> </Page.Resources> <StackPanel DataContext="{StaticResource class1}" .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; }