ASP.NET Gridview per JQuery befüllen

Folgende Ausführungen sind rein akademischer Natur. Über den Nutzen für die Praxis kann an der ein oder anderen Stelle diskutiert werden. Trotzdem sitze ich mit offenen Mund davor und frage mich noch immer –“das geht ?”

Wenn über moderne Web Anwendungen gesprochen wird, geht es immer um einen REST Service, JSON Daten und einen HTML View der z.B. mit Hilfe von Angular.js befüllt wird. Alles schön getrennt. Ich breche mit allem.

Als Backend kommt eine Webmethod zum Einsatz. Dieser wird direkt in die ASPX Seite eingebaut. Theoretisch auch im ASPX Teil direkt oder per Codebehind. Die Daten verpacke ich gleich in das Zielformat. Da dies eine HTML Table ist, TR und TD Element. Klassisches XML also. Dank HTTP Compression werden vermutlich auch nicht mehr Daten als im Vergleich zu JSON gesendet.

Die Daten kommen aus der Nordwind Datenbank und werden ganz klassisch per ADO.NET ausgelesen. Also auch hier kein Entity Model. Wichtig der SQL Parameter um SQLInjection zu unterbinden.

In meinem Beispiel wird immer nur ein Datensatz gelesen. Die ID wird über den Querystring eingesteuert und mit ein wenig Magie, dank neuer Funktion ASP.NET FriendlyUrls im Methodenrumpf zugewiesen.

Wer sich bei Zeile 13 die ungläubig die Augen reibt. Das sind XML Literals, ein VB.NET Sprachfeature.

   1:  Public Class WebForm8
   2:      Inherits System.Web.UI.Page
   3:   
   4:      <WebMethod()>
   5:      <ScriptMethod(UseHttpGet:=True, ResponseFormat:=ResponseFormat.Xml)>
   6:      Public Shared Function loadeins(<QueryString("id")> id As Integer?) As XElement
   7:          Dim con As New SqlConnection(ConfigurationManager.ConnectionStrings
("NorthwindConnectionString").ConnectionString)
   8:          Dim cmd As New SqlCommand("SELECT top 1 [ProductID],[ProductName], 
[CategoryID] FROM [Products] where productid=@id"
, con)
   9:          cmd.Parameters.Add(New SqlParameter("@id", id))
  10:          con.Open()
  11:          Dim dtr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
  12:          dtr.Read()
  13:          Dim tablexml = <tr><td><%= dtr(0) %></td>
  14:                             <td><%= dtr(1) %></td>
  15:                             <td><%= dtr(2) %></td>
  16:                         </tr>
  17:   
  18:          Return tablexml
  19:      End Function

 

Die Methode kann per GET aufgerufen werden und liefert XML zurück (Zeile5).

Als nächstes wird ein GridView in die HTML Seite deklariert. Da keine Daten direkt gebunden werden, muss das Attribut ShowHeaderWhenEmpty gesetzt werden.

   1:  <button onclick="loadmeins();return false" type="button">lade</button>
   2:  <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
   3:      ShowHeaderWhenEmpty="true">
   4:      <Columns>
   5:         <asp:BoundField DataField="ProductID" HeaderText="ID" />
   6:         <asp:BoundField DataField="ProductName" HeaderText="Name" />
   7:         <asp:BoundField DataField="CategoryID" HeaderText="i" />
   8:      </Columns>
   9:  </asp:GridView>

Jetzt kommt der einzige Teil bei dem ich mich schmutzig fühle. Code der einzig dem Zweck dient fehlendes zu umgehen. Ohne Daten tut das Gridview nämlich nichts. Also völlig sinn frei im Pageload  eine Liste zuweisen.

   1:   Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   2:      GridView1.DataSource = New List(Of String)
   3:      GridView1.DataBind()
   4:   End Sub

Nun fehlt nur noch das JavaScripts Zeugs, glattgebügelt mit JQuery. Der Aufruf der WebMethod erfolgt mit Page und Methodenname. Der Parameter für den gewünschten Datensatz wird im Data Block mitgegeben. Für die Anforderung muss der ContentType noch auf Json stehen, aber die Rückgabe ist XML. Trotzdem wird hier Text in DataType angegeben, weil dann result in Zeile 10 gleich ein Textstring und kein XMLDocument ist. Spart eine Zeile Code.

Am coolsten ist aber die Methode in Zeile 11. Mit einer einzigen Zeile wird der TR TD String an die Tabelle angehängt und zwar an den ersten TR. Also in der Reihenfolge genau anders rum wie gewohnt.

   1:  var id = 0;
   2:  function loadmeins() {
   3:    id++;
   4:    $.ajax({
   5:        type: "GET",
   6:        url: "webform8.aspx/loadeins",
   7:        data: { id: id },
   8:        contentType: 'application/json; charset=utf-8',
   9:        dataType: 'text',
  10:        success: function (result) {
  11:           $('#<%=GridView1.ClientID%> tbody tr:first').after(result);
  12:        },
  13:        error: function(result)
  14:           {
  15:                   alert("Fehler");
  16:                  }
  17:      });
  18:  }

 

Bei jedem Click auf “lade” wird eine neue Zeile eingefügt.

image

So und nun kommt der Teil wo ich nur staunen kann. Im HTML Source besitzt die Tabelle keinen Tbody. Im DOM allerdings schon (über F12 zu erforschen).

JQuery und Checkbox Rätsel

Liebe Community, ich habe einen JavaScript Fehler und die Lösung dazu. Allerdings suche ich noch nach der Erklärung. Die Lösung entweder per Facebook facebook.com/preishuber oder per Mail an hannesp AT ppedv.de,

Eine Website funktioniert manchmal und manchmal nicht korrekt, Konkret sind es Checkboxen in einer Tabelle. Die Tabelle kommt aus einem Web Server GridView Control. Dort sollen mittels einer Checkbox in der Header Zeile, alle Checkboxen in den Table Rows an bzw. abgehakt werden. Das funktioniert aber genau zweimal, nicht öfter.

Auf dem Weg zur Lösung wurde das Problem isoliert und mit identen Verhalten nachgestellt.

   1:  <asp:CheckBox ID="CheckBox1" runat="server"/><br />
   2:  <asp:CheckBox ID="CheckBox2" runat="server" /><--click
   3:  &lt;script>
   4:      $('#<%=CheckBox2.Clientid%>').click(function (args) {
   5:      $('#<%=CheckBox1.ClientID%>').attr('checked', args.target.checked);
   6:              });
   7:  </script>

 

Und in der Tat, es klappt nur einmal bzw zweimal, je nach Ausgangsdaten. Die obere Checkbox “folgt” der unteren.

image

Erst wenn man statt attr prop verwendet, klappt es dauerhaft. image

Weder debuggen, noch HTML Source Code noch die Browser Tools haben  geholfen den Fehler zu finden. Es war purer Zufall und ausprobieren. In diesem Fall war es auch nicht möglich der Suchmaschine ein vernünftige Frage zu stellen.

Also liebe Community- Warum ist das so?

[UPDATE} Stephan Hüwe weist mich auf die Doku hin

The difference between attributes and properties can be important in specific situations. Before jQuery 1.6, the .attr() method sometimes took property values into account when retrieving some attributes, which could cause inconsistent behavior. As of jQuery 1.6, the .prop() method provides a way to explicitly retrieve property values, while .attr() retrieves attributes.

Mein Statement dazu: kann man akzeptieren. Allerdings ist eine API die nicht konsistente Ergebnisse liefert –“not well designed” wie es die Briten sagen würden.

Open Source: Plötzlich spielt es eine Rolle

Vor rund zwei Jahren habe ich hier im Blog eine Diskussion losgetreten. Völlig frustriert von der miesen Qualität diverser Bibliotheken und fehlender Dokumentation habe ich Dampf abgelassen. Die Aussage: “Es ist mir scheißegal, ob Open Source oder nicht.”

Im Bezug auf die aktuellen Geschehnisse um OPENSSL und den #heartbleed Bug muss ich meine Meinung revidieren. Es ist plötzlich wichtig #OPENSOURCEFREE auf das Produkt-Paket gedruckt zu bekommen. Um es klar zu sagen, dies war kein Einzelfall:

  • Heartbleed, unentdeckt für ca 2 Jahre, schlimmster Sicherheitsunfall in der Geschichte des Internets
  • GnuTLS blieb 10 Jahre unentdeckt
  • Eine fehlende Codezeile im Random Number Bug über 2 Jahre
  • TCP-32764 Backdoor, dessen Urheber bis heute unklar ist

Damit gilt das oft zitierte Statement als widerlegt. In der Wissenschaft reicht ein Negativbeweis. Die Diskussion über Open Source als Sicherheitsrisiko, speziell im Unternehmenseinsatz, ist über 10 Jahre alt, wird aber von den Verfechtern negiert.

"Weil aber Open Source die Offenlegung des Quellcodes bedeutet, können Anwendungen von allen Nutzern ständig weiterentwickelt werden. Auch Fehler lassen sich so meist schneller beheben."

"Der Vorteil von Open Source: Schadsoftware, Bugs oder Sicherheitslücken können in der Regel schneller entdeckt werden. Es reicht allein der Verdacht, und Tausende von EntwicklerInnen nehmen den betreffenden Code ganz genau unter die Lupe", sagt Grote. Die größere Gefahr bestehe vielmehr in den unfreien Komponenten, etwa proprietären Modemtreibern oder Firmware, die meist von den Herstellern kommen und deren Sicherheit niemand überprüfen kann."

Nun stehen zwei Konzepte gegenüber: Open Source und Closed Source. Warum ich glaube, dass Closed Source die bessere Wahl sein kann:

Kosten

Es wird argumentiert, dass bei Open Source die Lizenzkosten wegfallen. Das Beispiel der Stadt München hat bewiesen, dass man mit mehr finanziellem Aufwand ein qualitativ weniger hochwertiges Ergebnis erreichen kann - bei höherem zeitlichem Aufwand. Open Source kann nur seriös eingesetzt werden, wenn das Know-how und die Ressourcen vorhanden sind, den Code zu pflegen. Niemals kann man sich darauf verlassen, dass jemand für immer und gratis seine Bibliothek weiter pflegt oder das jemand ganz automatisch mit Freude und völlig unentgeltlich diesen Job übernimmt. Persönlich schmerzlich erfahren mit dotnetopenauth.

Sicherheit

Ganz generell gesprochen gibt es keinen schlüssigen Zusammenhang zwischen Sicherheit und Open Source. Ein Vier-Augen-Prinzip erhöht die Sicherheit in jedem Softwareprojekt, allerdings ist es auch ein Leichtes, in ein Open Source-Projekt Programmierer einzuschleusen, die andere Ziele verfolgen. Dabei hat sich gezeigt, dass die Entdeckung von Sicherheitslücken über Parsing Tools  und nicht per Code-Analyse erfolgte.

Verantwortung

Der letzte Punkt ist mir erst in den letzten Tagen so richtig klar geworden. Niemand übernimmt die Verantwortung. Gerade in einem Land wie Deutschland, in dem sich jeder gegen jedwede Risken versichert, in dem unglaublicher Aufwand getrieben wird den kleinsten Parksünder dingfest zu machen, kann es sich eigentlich niemand leisten, nicht zu wissen, wer eigentlich Schuld trägt.

Das ist sowohl rechtlich in Fragen der Gewährleistung als auch gesellschaftlich ein unhaltbarer Zustand.

Stellen Sie sich mal vor eine Airline würde Metallteile verbauen, die sie auf der Straße findet.  Dann hätten wir nicht nur eine MH370, sondern jeden Tag zehn. Verantwortungslos.

Fazit

Open Source wurde immer durch Gebrauch des englischen Begriffs "free" mit gratis verwechselt. Nachdem dieser Irrtum erwiesen ist und die erheblichen Risiken nicht mehr zu leugnen sind, müsste man folgerichtig ein EU-Verbot fordern. Oder zumindest abschreckende Warnaufdrucke, ähnlich der Zigarettenschachteln. Die werden auch nicht weniger schädlich, wenn man das billige Kraut raucht.

PS: Auch Microsoft hatte seine Sicherheitsgaus - bis vor etwa 10 Jahren (SQLSlammer), danach nicht mehr.

Data Annotations Webforms und Bootstrap ausgereizt

Auf dem Weg zum Lazy Web Developer habe ich dieses Mal den Data Annotations auf den Zahn gefühlt. In Silverlight war das richtig geiler Scheiss.

Man kann einer Klasse per Property-Attributen Regeln mitgeben. Diese werden Data Annotations genannt. Die Regeln können sich auf Darstellung, Reihenfolge oder Gültigkeit eines Propertys beziehen.

   1:  Public Class daten1
   2:      <Key()>
   3:      Property id As Integer
   4:      <Required(ErrorMessage:="brauche Daten")>
   5:      <EmailAddress(ErrorMessage:="brauche email")>
   6:      Property email As String
   7:      <Required(ErrorMessage:="musse haben")>
   8:      Property name As String
   9:  End Class

In der richtigen Umgebung werden diese Klassenattribute ausgewertet.

ASP.NET nutzt die Klassenattribute über Dynamic Data. Eingebettet in ein Datensteuerelement wertet das DynamicEntity-Steuerelement das gebundene Objekt (Itemtype) aus.

   1:      <link href="../Content/bootstrap.css" rel="stylesheet" />
   2:      <script src="../Scripts/jquery-2.0.3.js"></script>
   3:      <script src="../Scripts/bootstrap.js"></script>
   4:  </head>
   5:  <body>
   6:      <form id="form1" runat="server">
   7:      <div>
   8:          <asp:FormView ID="FormView1" DataKeyNames="id" 
ItemType="daten1"
   9:               runat="server" DefaultMode="Insert">
  10:              <InsertItemTemplate>
  11:                  <asp:DynamicEntity runat="server" ID="eins" 
Mode="Insert"/>
  12:                  <asp:Button runat="server" CommandName="Insert" 
Text="insert" class="btn btn-default"/>
  13:              </InsertItemTemplate>
  14:          </asp:FormView>
  15:      </div>
  16:      </form>
  17:  </body>

 

Die Magie geschieht im Verzeichnis Dynamic Data. Je nach Mode (hier insert), wird das Template aus EntityTemplate geladen.

image

Dieses Template wurde von mir leicht angepasst, um den Bootstrap 3 Formular-Regularien zu folgen.

   1:  <asp:EntityTemplate runat="server" ID="EntityTemplate1">
   2:      <ItemTemplate>
   3:          <div class="form-group">
   4:              <asp:Label ID="Label1" runat="server" OnInit="Label_Init"
   5:                  OnPreRender="Label_PreRender" 
CssClass="col-sm-2 control-label" />
   6:              <div class="controls">
   7:                  <asp:DynamicControl runat="server" ID="DynamicControl"
   8:                      Mode="Edit" OnInit="DynamicControl_Init" />
   9:              </div>
  10:          </div>
  11:      </ItemTemplate>
  12:  </asp:EntityTemplate>

 

Das DynamicControl-Element wird dann passend zum Datentypen durch ein Template aus dem Verzeichnis FieldTemplate ersetzt. In diesem Beispiel Integer_Edit, Text_Edit und EMailAddress_Edit.

Folgendes deklarative ASP.NET Code-Beispiel zeigt das Text-Template. Wichtig ist Display Dynamic einzustellen, um den Platz in der Darstellung nicht zu verbrauchen, wenn keine Fehlermeldung angezeigt wird. Außerdem verwendet Bootstrap für sein error HTML Template die CSS-Klasse help-block. Ergebnis ist roter Text.

   1:  <asp:TextBox ID="TextBox1" runat="server" Text='<%# FieldValueEditString %>'
   2:       CssClass="form-control"></asp:TextBox>
   3:   
   4:  <asp:RequiredFieldValidator runat="server" ID="RequiredFieldValidator1" 
CssClass="help-block" ControlToValidate="TextBox1" Display="Dynamic" Enabled="false" />
   5:  <asp:RegularExpressionValidator runat="server" ID="RegularExpressionValidator1" 
CssClass="help-block" ControlToValidate="TextBox1" Display="Dynamic" Enabled="false" />
   6:  <asp:DynamicValidator runat="server" ID="DynamicValidator1" 
CssClass="help-block" ControlToValidate="TextBox1" Display="Dynamic" />
   7:   

Die ASP.NET Validation Controls haben allerdings ein Feature, das hier stört. Diese können mit einem Stern die Fehlerposition  und den beschreibenden Fehlertext in einem ValidationSummary Control anzeigen. Wir wollen aber den Fehlertext direkt unterhalb der Eingabe sehen.

Folgender übler Trick ersetzt per VB.NET Code im Page Load Event des ASCX Files den Stern und damit wird der volle Text gezeigt:

   1:  RegularExpressionValidator1.Text = ""
   2:  RequiredFieldValidator1.Text = ""
   3:  DynamicValidator1.Text = ""

Dann füge ich noch eine JavaScript-Datei hinzu, die CSS-Attribute austauscht für has-errors wenn die Eingabe nicht valid ist. Dies ist in einem anderen Blog-Artikel beschrieben.

   1:  var proxied = window.ValidatorValidate;
   2:   
   3:  window.ValidatorValidate = function () {
   4:   
   5:      var result = proxied.apply(this, arguments);
   6:      onAfter(arguments);
   7:   
   8:  };
   9:   
  10:  var onAfter = function (arguments) {
  11:      var control = document.getElementById(arguments[0].controltovalidate);
  12:      var validators = control.Validators;
  13:      var isValid = true;
  14:   
  15:      for (var i = 0; i < validators.length; i++) {
  16:          if (!validators[i].isvalid) {
  17:              isValid = false;
  18:              break;
  19:          }
  20:      }
  21:   
  22:      if (isValid) {
  23:          $(control).closest('.form-group').removeClass('has-error');
  24:      } else {
  25:          $(control).closest('.form-group').addClass('has-error');
  26:      }
  27:  };

Bei einem Submit des Formulars sieht das im Internet Explorer  so aus:

image

Sehr cool, was die ASP.NET Steuerelemente zusammen mit Bootstrap hier so zaubern. Allerdings sind sie einen Hauch zu intelligent. Es werden nämlich auch korrekt die neuen HTML5 Input-Typen gerendert (Type=”number” und Type=”email”). Moderne Browser validieren dann auch gleich mal selber was im IE bei falscher E-Mail-Adresse so aussieht:

image

Zuerst erscheint ein browserspezifisches Popup und wenn man das Feld per TAB verlässt, wird es rot eingerahmt.

Da wir uns hier in den Händen des Browsers befinden, sieht das dann Chrome auch deutlich anders. Die fehlerhafte Eingabe wird gar nicht mehr hervorgehoben und die Nummer wird von einem Updown Icon begleitet.

image

In den Bootstrap-Foren wurde das Thema auch diskutiert und dann geschlossen. Theoretisch wäre auch das Required-Attribut ein HTML5-Standard, hier erzeugt ASP.NET aber stattdessen ein DIV. Optisch besser aber nicht konsistent. Bei meiner Recherche habe ich ein Plugin gefunden, habe aber ein wenig Probleme für das Framework, das für die Unzulänglichkeiten von CSS erfunden wurde, ein Plugin zu verwenden, das die Unzulänglichkeiten des Frameworks fixed. Im ersten Test gibt es einen Konflikt mit meiner eigenen JS Library. Selbst wenn ich das fixe, kann schon morgen mit dem nächsten Nuget-Update wieder alles hin sein.

Form-Validierung für Bootstrap

Es gibt verdammt viele Möglichkeiten, um Benutzereingaben zu validieren. Im Webumfeld werden es sogar mehr statt weniger. Hinzu kommen Anforderungen an das Design. Client oder Server? Ganz schön viel für einen kleinen Blogartikel, der sich um ASP.NET-Formulare in Verbindung mit Bootstrap 3 kümmert.

Bootstrap ist ein Framework für einfacheres Webdesign. Responsive Design fast ohne CSS anzufassen. Microsoft setzt in Visual Studio 2013 und seinem One ASP.NET Template auf die aktuelle Version Bootstrap 3. Ist also schon drin. Ideal also für eine schnelle Validierung am Client.

Das HTML Template setzt sich aus geschachtelten DIV zusammen und vor allem dem Attribut has-error. In der  Version 2 lautete das noch control-group und error.

   1:   <div class="form-group has-error">
   2:          <label class="control-label" for="text1">Feldbeschreibung
</label>
   3:          <div class="controls">
   4:              <input type="text" class="form-control" id="text1">
   5:              <span class="help-block">Fehlermeldung</span>
   6:          </div>
   7:  </div>

und sieht dann so aus. Der Fehler ist sozusagen hart verdrahtet.

image

Nun stellt sich sowohl für den ASP.NET MVC und auch den Webforms Frontend Designer die Frage, wie man die Validation Controls bzw. HTML Validation Helper dazu bewegt da mitzuspielen. Das Problem liegt vor allem darin, dass die Fehlermeldung einen übergeordneten Node im HTML Dom verändern muss. Aus Programmierersicht ist es ohnehin verpönt, Elemente (oder Objekte) in Abhängigkeit zu stellen.

Die Alternative, komplett neue Steuerelemente zu entwickeln, erscheint wenig verlockend.

Betrachten wir die Eingabevalidierung mit ASP.NET Web Forms unter Anwendung der vorigen Bootstrap HTML Vorlage. Es soll geprüft werden, ob die Eingabe eine gültige E-Mail-Adresse ist. Visual Studio schlägt in den Control-Eigenschaften die passende Regular Expression vor.

   1:  <div class="form-group" id="test1">
   2:          <asp:Label ID="Label2" runat="server" Text="Beschriftung"
   3:           CssClass="col-sm-2 control-label" />
   4:          <div class="controls">
   5:              <asp:TextBox ID="TextBox1" runat="server" 
CssClass="form-control">
   6:  </asp:TextBox>
   7:              <asp:RegularExpressionValidator 
ID="RegularExpressionValidator1" runat="server"
   8:                  SetFocusOnError="true"
   9:                  ErrorMessage="RegularExpressionValidator" 
ControlToValidate="TextBox1"
  10:  ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
  11:  </asp:RegularExpressionValidator>
  12:          </div>
  13: </div>    

Als nächstes kommt JavaScript und JQuery Magic zum Einsatz. Mein großes Problem war herauszufinden, wo man während der Validierung einhaken kann. Das Validator Control blendet dynamisch ohne Postback die Fehlermeldung ein bzw. aus - dank moderner unobtrusive Validation von ASP.NET Webforms auch mit JQuery Validierung. Im HTML Code wird ein SPAN Element erzeugt. Data-Attribute werden für die Validierung verwendet.

   1:   <input name="ctl00$Inhalt$TextBox1" type="text" 
id="Inhalt_TextBox1"
   2:  class="form-control" />
   3:   
   4:  <span data-val-controltovalidate="Inhalt_TextBox1" 
data-val-focusOnError="t"
   5:  data-val-errormessage="RegularExpressionValidator" 
   6:  id="Inhalt_RegularExpressionValidator1" data-val="true" 
   7:  data-val-evaluationfunction="RegularExpressionValidator
EvaluateIsValid"
   8:  data-val-validationexpression="\w+([-+.&#39;]\w+)*@\w+
([-.]\w+)*\.\w+([-.]\w+)*"
style="visibility:hidden;">
RegularExpressionValidator</span>
   9:          </div>

Bleibt die Frage, wie man aus dem SPAN da, oder nicht da, auf ein übergeordnetes DIV kommt. Ginge, aber ich habe ein Event gefunden, das schon seit Ewigkeiten in der Library existiert, ValidatorValidate.

Diese Methode wird dann überschrieben, wie man das aus OOP so kennt, um seine eigene Logik (onAfter) hinten dran einzufügen. Dann wird die CSS Klasse has-error zugewiesen, wenn ein Fehler auftritt.

   1:  <script>
   2:   var proxied = window.ValidatorValidate;
   3:         window.ValidatorValidate = function () {
   4:          var result = proxied.apply(this, arguments);
   5:          onAfter(arguments);
   6:         };
   8:         var control = document.getElementById(arguments[0].
controltovalidate);
   9:         var validators = control.Validators;
   7:         var onAfter = function (arguments) {
  10:         var isValid = true;
  11:         for (var i = 0; i < validators.length; i++) {
  12:                  if (!validators[i].isvalid) {
  13:                      isValid = false;
  14:                      break;
  15:                  }
  16:              }
  17:             if (isValid) {
  18:                  $(control).closest('.form-group').
removeClass('has-error');
  19:              } else {
  20:                  $(control).closest('.form-group').
addClass('has-error');
  21:              }
  22:          };
  23:  </script>

 

Um ehrlich zu sein, bin ich mir nicht sicher, was ich mir im Detail damit einhandle. Falls es unter den Lesern welche gibt die dazu Feedback haben, per mail an hannesp at ppedv.de oder Facebook.com/preishuber.

Jedenfalls ist der Aufwand nun überschaubar. Einfach als JS-Datei ausgelagert und am besten in der Masterpage included.

Evolution statt Revolution

Nachdem der Staub sich rund um die alljährlichen Ankündigungsreigen der Redmonder Software-Schmiede zu legen beginnt, ist es Zeit ein erstes Fazit zu ziehen. Was bringt uns die Zukunft bzw. was stellt sich Microsoft darunter vor?

Wenn man es ganz nüchtern betrachtet, bleibt eigentlich nur ein Windows Update für 8.1, ein Phone Update von 8 auf 8.1 und eine Reihe von Detail-Änderungen übrig. Daneben noch die faktisch folgenlose Veröffentlichung des Quellcodes einiger neuer Bibliotheken, heute Open Source genannt.

Wer eine Dekade zurückblickt, wird sich erinnern, dass die TechEd Developer sich mit aktuellen und ganz nahen Technologien beschäftigte und die PDC, die Vorgängerin der BUILD, mit den über die nächsten ca. 18 Monaten kommenden. Die BUILD 2014 präsentierte am 2.April ein kleines Windows Update, das am 8.April öffentlich verfügbar sein wird. So ist die Welt heute. Gerade mal eine Woche Zukunft. Roadmaps konnte man in den Slides keine entdecken.

Vor rund fünf Jahren war Windows Mobile 6.5 angesagt. Mit einem kolportierten Marktanteil von ca. 19%. Allerdings war Apple unaufhaltsam auf dem Vormarsch und so entschlossen sich die Redmonder zu einem radikalen Schnitt: es den Jungs aus Cupertino gleich zu tun.

Ich erinnere mich noch gut an ein frühes Meeting bei Microsoft. Ein Kreis erlauchter externer Experten, eine Handvoll Microsofties und meine Wenigkeit. Das Produkt, das da vorgestellt wurde, hatte alle Nachteile von Apple gewonnen und die Vorteile von Microsoft verloren. Ich werde heute noch auf meine unbarmherzige Kritik angesprochen. Und ich habe mit jedem Strich und Punkt Recht behalten. Meine damaligen Vorschläge werden jetzt umgesetzt. Der Marktanteil dümpelt heute noch unterhalb des damaligen Windows Mobile rum. Dennoch hatte auch Microsoft Recht. Es musste ein harter Kurswechsel her. Windows Mobile hatte kein Marktpotential für die Zukunft.

Rund zweieinhalb Jahre später, auf der ersten BUILD, wurde dann Windows 8 angekündigt. Ich war persönlich in Anaheim dabei und habe mich von der Begeisterung anstecken lassen. In einem persönlichen Dialog mit einem Produktmanager wurde die Problematik des Rollouts angesprochen. Er war überzeugt, dass der Appstore-Zwang von den Kunden angenommen wird. Ich nicht. Ein anderer wichtiger Produktmanager war überzeugt, dass bald ohnehin nur mehr touchfähige Devices ausgeliefert werden. Ein folgenschwerer Irrtum, wie sich aus heutiger Sicht zeigt. Der wesentlichste Fehler ist uns und damit meine ich Microsoft und mich, in der  Einschätzung der Verbreitung von Windows 8 und Devices unterlaufen. Meine persönliche Prognose war: 100 Millionen Touch Devices bis 31.Dezember des Erscheinungsjahrs, sonst wird Balmer seinen Hut nehmen müssen. Es waren bei weitem nicht so viele und Balmer ist Geschichte.

Ganz wesentlich ist aber die falsche Consumer-Strategie. Consumerization von IT ist zwar Tatsache, wie auch bei HTML5, aber deren Folgen sind völlig falsch eingeschätzt worden. Die Unternehmen haben keine APPS erstellt. Punkt.

Windows 8 hatte nie ein Qualitätsproblem. Es war schön, modern, einfach zu bedienen. Auf Tablets für den Neueinsteiger völlig intuitiv. Eine Sensation ohne Startbutton. Es sind die über 1 Milliarde Microsoft-Benutzer, die nicht mitspielen. Folgerichtig kann man in den Blogs und Tweets im Tenor lesen, was es eh jeder schon die ganze Zeit sagt: Hört den Kunden zu. Steve Jobs und Steven Sinofsky waren anderer Meinung: Innovation kommt nicht aus Marktforschung.

Ganz anders die Keynotes der BUILD 2014. Wir haben zugehört, hier habt Ihr das Resultat. Auch ein Startmenü gibt es dazu. Und wir nehmen euch euer geliebtes .NET und XAML nicht weg. JavaScript erwähnen wir gar nicht, weil ihr es hasst.

Die Teilnehmer sind begeistert. Twitter quillt über von positiven Feeds: Beste BUILD ever. Vor allem die gemeinsame Codebasis für Phone und Windows wird gelobt. Auch die vereinfachte Sideloading Policy, die faktisch den Store aushebelt, stößt auf Begeisterung.

Die Realität bildet das aber nur zum Teil ab. Weite Teile des Developer Eco Systems beschäftigen sich heute mit HTML5 als Frontend-Technologie. Ich halte diese zwar für grottig, aber vorhanden. Da das Problem mit dem Standard und Browsern systemimmanent ist, erwarte ich keine relevante Besserung. In Angesicht dessen bin ich immer wieder erstaunt, welche großartigen Ergebnisse trotz widriger Umstände erzielt werden.

Diese Community wird auch mit dem Honig Open Source und Nuget kaum kleben bleiben. Das sind die Leute, die nicht auf die BUILD gehen.

Aus dem Applaus und den Besucherzahlen der aufgezeichneten Sessions lassen sich ein paar Trends erkennen. C# und .NET sind weiter top angesagt. Multiplattform-Entwicklung auf dem .NET-Technologiestack wird eindeutig gewünscht und von XAMARIN bedient. Azure ist trotz Scott Guthrie bei weitem nicht das führende Thema.

Schon im Vorfeld hat Microsoft eine Art Office für die Apple-Plattform gelauncht. Ein Eingeständnis der schwindenden Bedeutung von Windows. Dies wird auch mit einem Startmenü nicht besser werden. Die Fokussierung auf das angestammte Publikum, die Vereinheitlichung der Plattform Xbox, Windows und mobile werden helfen, nicht noch weitere Kunden zu vergraulen.

Die nächste Welle wird allerdings woanders an den IT-Strand laufen: das Internet of Things, oft ohne UI, aber damit mit hohem Bedarf an Service-Kommunikation. Davon sah man im BUILD-Programm recht wenig.

Hannes Preishuber besucht seit rund 15 Jahren Microsoft-Konferenzen. Er war Teilnehmer der legendären Hailstorm PDC, deren Visionen und Produkte nie Realität wurden. Auch Projekt Oslo und die Programmiersprache M kennt heute keiner mehr. Obwohl Clippy das selbe Schicksal erleiden musste, war er kein PDC Announcement. 2003 diskutierte er auf der PDC in Los Angeles mit dem noch unbekannten Miguel de Icaza über .net und dessen von Microsoft ungewünschte Implementierung als Mono. Die BUILD Konferenz 2014 besuchte er nur virtuell. Trotz entsprechender Gerüchte nicht wegen des Essens.

Webseiten mit Bild-Slider

Immer mehr (so beginnt jeder gute Artikel in einer Frauenzeitschrift) Webseiten beinhalten einen Image Slider. Und immer mehr gewinne ich den Eindruck, dass der aktuelle HTML5, CSS, JS-Weg das wiederholt was zu Visual Basic 4-Zeiten passiert ist: Komplexität in OCXen Widgets zu verstecken. Mein neuestes Fundstück ist in Bootstrap das Carousel.

Das Carousel Widget aus Bootstrap 3 kann ein oder mehrere Bilder durch Sliding-Effekte wechselnd darstellen. Da man mit HTML arbeitet, muss es kein Bild sein, sondern kann alles sein, was in ein DIV so passt (Item). Zur Steuerung bzw. zum direkten Sprung kann der Benutzer den Indikator verwenden. Dieser kann direkt wie ein Pager benutzt werden oder auch mit einer Next-Logik versehen werden. Dies macht Sinn, wenn es wesentlich mehr Bilder sind. Außerdem befindet sich rechts und links ein Carousel-Control. Dazu kommt eine Überschrift, die auch außerhalb des Bild-Bereiches platziert werden kann.

image

Das Ganze als HTML. Mit den Attributen wird Intervall in Millisekunden, und das Verhalten am Ende der Bild-Liste gesteuert.

Die Indikator-Elemente werden als direkte Navigationselemente verwendet.

   1:      <div class="carousel slide" id="carousel-example-generic"
data-ride="carousel" data-interval="3000" wrap="true">
   2:         
   3:          <ol class="carousel-indicators">
   4:              <li data-slide-to="0" 
data-target="#carousel-example-generic"></li>
   5:              <li class="active" data-slide-to="1" 
data-target="#carousel-example-generic"></li>
   6:              <li data-slide-to="2" d
ata-target
="#carousel-example-generic"></li>
   7:          </ol>
   8:   
   9:          <div class="carousel-inner">
  10:              <div class="item">
  11:                  <img alt="PX" src="img/px.png">
  12:                  <div class="carousel-caption">
  13:                      <h3>Vespa PX 200E</h3>
  14:                      <p>Modernere Bauart</p>
  15:                  </div>
  16:              </div>
  17:              <div class="item active">
  18:               <a href="http://www.ppedv.de">
  19:                  <img alt="LX" src="img/lx.png"></a>
  20:                  <div class="carousel-caption">
  21:                      <h3>Vespa LX</h3>
  22:                      <p>jüngere 50er und 125er</p>
  23:                  </div>
  24:              </div>
  25:              <div class="item">
  26:   
  27:                  <img alt="GS" src="img/gs.png">
  28:                  <div class="carousel-caption">
  29:                      <h3>Vespa GS 150</h3>
  30:                      <p>alter Klassiker</p>
  31:                  </div>
  32:              </div>
  33:          </div>
  34:   
  35:         
  36:          <a class="left carousel-control" 
href="#carousel-example-generic" data-slide="prev">
  37:              <span class="glyphicon glyphicon-chevron-left"></span>
  38:          </a>
  39:          <a class="right carousel-control" 
href="#carousel-example-generic" data-slide="next">
  40:              <span class="glyphicon glyphicon-chevron-right"></span>
  41:          </a>
  42:      </div>

Natürlich lässt sich per CSS das visuelle Erscheinungsbild noch verändern. Da die Styles natürlich in bootstrap.css vordefiniert sind, muss die Definition nachgelagert erfolgen.

   1:    <style>
   2:          .carousel-caption {
   3:              position:absolute;
   4:              top:-10px;
   5:              left:auto;
   6:              right:20px;
   7:              bottom:0;
   8:              height: auto;
   9:              z-index: 30;
  10:              background: none;
  11:          }
  12:   
  13:          .carousel-indicators {
  14:              top: 20px;
  15:          }
  16:   
  17:              .carousel-indicators li {
  18:                  width: 12px;
  19:                  height: 12px;
  20:                  margin: 0 3px;
  21:                  background: #555;
  22:                  opacity: 0.3;
  23:                  border: none;
  24:                  transition: all 0.3s;
  25:              }
  26:   
  27:                  .carousel-indicators li.active {
  28:                      width: 12px;
  29:                      height: 12px;
  30:                      margin: 0 3px;
  31:                      opacity: 1;
  32:                      background-color: #555;
  33:                  }
  34:      </style>

Warum erinnert mich das an die VB-Zeiten? Damals sprangen unzählige Control-Hersteller in die Unzulänglichkeiten von Microsoft ein. Schon mit Releasewechsel waren die Controls nicht mehr funktionsfähig. Die allermeisten Hersteller existierten nur sehr kurz. Ein Control in einem Projekt durch ein anderes zu ersetzen überstieg den Aufwand der ursprünglichen Implementierung. 

Was ist der Unterschied, VB 6-Anwendungen laufen auch noch nach 10 Jahren. Das wird keiner mit einer Bootstrap, JQuery und sonst was basierten Seite auch nur 2 Jahre schaffen.

EU Cookie Opt-in

Als wenn wir nicht schon genug Regeln hätten, gibt es seit 2009 die Richtlinie 2009/136/EG. Es wird viel geschrieben darüber und ich bin kein Experte. Es scheint so zu sein, dass jeder Staat eine eigene Umsetzung vorweisen muss. Kurz zusammengefasst geht es dabei um Datenschutz. Genauer um personenbezogene Daten.

Darunter fallen auch Tracking Cookies wie von Google Analyze. Ich bin ohnehin kein Freund davon, aber auch Piwik setzt Tracking Cookies ein. Solange dies für den Betrieb der Website notwendig ist, wie Authentifizierung oder Passwort, sollte auch keine Einwilligung des Benutzers nötig sein. Für alles andere muss er zustimmen bzw. die Möglichkeit haben dies abzulehnen.

Die EU fordert ein Opt-in Verfahren.

An vielen Stellen wird über Sinn oder Unsinn diskutiert. Letztendlich kann jeder in seinem Browser Cookies deaktivieren oder mittels inprivate Browser löschen.

image

Es ist eine dumme und ungenaue Regelung, die allerdings mit Strafe belegt ist. Und es kommt sicherlich auch bald einer der unzähligen Anwälte darauf Abmahnungen zu schreiben.

Also habe ich mich daran gesetzt und ein paar Zeilen JavaScript-Code geschrieben, der unabhängig von HTML und CSS ist. Sogar auf JQuery habe ich verzichtet.

   1:    <script>
   2:              window.onload = function () {
   3:                  var optin;
   4:                  var ca = document.cookie.split(';');
   5:                  for (var i = 0; i < ca.length; i++) {
   6:                      var c = ca[i].trim();
   7:                     if (c.indexOf('EUCookie') == 0) optin = true;
   8:                  }
   9:                  if (optin != true)
  10:                  {
  11:                      var ele = document.createElement("div");
  12:                      ele.setAttribute("id", "optincookie");
  13:                      ele.setAttribute("style", "width:100%;height:30px;opacity:0.3;
z-index:100;background:#000;"
);
  14:                      ele.innerHTML = "<p style='color:white;display:inline-block;
margin:5px;'>You accept Cookies</p><a href='datenschutz.aspx' >details</a>
<img style='padding:2px;' src='/img/close.png' id='closecookie'/>"
;
  15:   
  16:                      document.body.insertBefore(ele,document.body.childNodes[0]);
  17:                      var mylink = document.getElementById('closecookie');
  18:                      mylink.onclick = function (e) {
  19:                          document.body.removeChild(ele);
  20:                          var d = new Date();
  21:                          d.setTime(d.getTime() + (90 * 24 * 60 * 60 * 1000));
  22:                        
  23:                          document.cookie = "EUCookie=yes; " + "expires=" + d.toGMTString();
  24:                      };
  25:                  };
  26:              };
  27:   
  28:   
  29:          </script>
  30:        

Ein wunderbarer Anlass mich daran zu erinnern wie grottig ich JavaScript eigentlich finde. Am Stil erkennt man vermutlich, dass ich das vor rund 20 Jahren gelernt habe.

Noch ein sehr kritisches Wort zum Wettbewerbsrecht. Impressum, Datenschutzerklärung, Widerrufsrecht, Urheberrecht und vieles mehr dient vor allem den Anwälten. Ausländische Unternehmen scheren sich einen Dreck darum. Wer seinen Sitz in Deutschland hat und eine Website betreibt, kann unmöglich juristisch korrekt Geschäfte machen. Je größer und gewinnbringender der Website-Betreiber desto lukrativer ist es einen Rechtsstreit anzufangen. 30% der deutschen Abgeordneten sind Juristen.

Ein noch kritischeres Wort zur Gesetzgebung und zur EU. Offensichtlich finden die unzähligen gut bezahlten EU-Parlamentarier ihre Berechtigung darin, eine noch strengere Regelung voranzutreiben. Ein Frauenquote da, eine Datenschutzregel dort.

Ach ja wenn es um Datenschutz geht, ist das Ankaufen von gestohlenen CD’s völlig ok, auch der Verkauf von Meldedaten, der Zugriff auf Kontodaten und das Abhören von uns allen.

HTML5 Datei-Upload mit ASP.NET

Manches geht viel einfacher als man glaubt. File Upload ist eine übliche Aufgabe in Web-Anwendungen und das klappt super. Eine begeisternde Kurz-Beschreibung.

Bisher habe ich für einen Dateiupload immer einen ASHX Handler geschrieben. Braucht man aber nicht, klappt auch mit einer einfachen aspx Seite.

   1:  Imports System.IO
   2:  Public Class upload
   3:      Inherits System.Web.UI.Page
   4:      Protected Sub Page_Load(ByVal sender As Object, 
ByVal e As System.EventArgs) Handles Me.Load
   5:          If IsPostBack Then
   6:              UploadFile(sender, e)
   7:          End If
   8:      End Sub
   9:      Protected Sub UploadFile(sender As Object, a As EventArgs)
  10:          Dim fc = Request.Files
  11:          For i = 0 To fc.Count - 1
  12:              Dim upload = fc(i)
  13:              Dim fn = Server.MapPath(".\daten\") + Path.GetFileName(fc(i).FileName)
  14:              upload.SaveAs(fn)
  15:          Next
  16:      End Sub
  17:  End Class

 

Der VB.NET-Code beschreibt sich eigentlich von selbst.

Für den Designteil werden zunächst die CSS-Regeln definiert. Es wird ein Bereich angelegt, der sich blau bzw. rot und blau verfärben soll.

image

   1:  <style>
   2:          #uploadzone {
   3:              width: 100px;
   4:              border: dashed thin #999;
   5:              height: 200px;
   6:              text-align: center;
   7:          }
   8:   
   9:              #uploadzone.hover {
  10:                  border-color: #aaa;
  11:                  background-color: #9cd0fc;
  12:              }
  13:   
  14:              #uploadzone.error {
  15:                  border-color: #f00;
  16:                  background-color: #faa;
  17:              }
  18:      </style>
  19:      

 

Im HTML-Teil wird nur ein DIV als Ziel für Drag und Drop definiert. Der kurze JavaScript-Teil dient zum Registrieren des Drop Events und für die Prüfung der File-API-Fähigkeiten.

   1:   <form id="form1" runat="server" enctype="multipart/form-data">
   2:          <div id="uploadzone">Datei Drag&Drop</div>
   3:      </form>
   4:      <script>
   5:          if (window.File && window.FileList && window.FileReader) {
   6:              var z = document.getElementById('uploadzone');
   7:              z.addEventListener('dragover', handleDragOver, false);
   8:              z.addEventListener('drop', handleDropFile, false);
   9:          }
  10:          else {
  11:              alert('Kein HTML5 File API Support!');
  12:          }
  13:      </script>

 

Ein relativ großer JavaScript-Block kümmert sich um die visuelle Interaktion und führt letztendlich den HTTP-Post aus.

   1:      <script>
   2:          var files;
   3:          function handleDragOver(event) {
   4:              event.stopPropagation();
   5:              event.preventDefault();
   6:              document.getElementById('uploadzone').setAttribute("class", 'hover');
   7:          }
   8:   
   9:          function handleDropFile(event) {
  10:              event.stopPropagation();
  11:              event.preventDefault();
  12:   
  13:              files = event.dataTransfer.files;
  14:   
  15:              var form = document.getElementById('form1');
  16:              var data = new FormData(form);
  17:   
  18:              for (var i = 0; i < files.length; i++) {
  19:                  data.append(files[i].name, files[i]);
  20:              }
  21:              var xhr = new XMLHttpRequest();
  22:              xhr.onreadystatechange = function () {
  23:                  if (xhr.readyState == 4) {
  24:                      if (xhr.status == 200) {
  25:                          alert("upload erfolgreich");
  26:                          document.getElementById('uploadzone').
setAttribute("class", '');
  27:   
  28:                      } else {
  29:                          document.getElementById('uploadzone').
setAttribute("class", 'error');
  30:   
  31:                          alert("fehlgeschlagen");
  32:                      }
  33:                  }
  34:              };
  35:              xhr.open('POST', "Upload");
  36:              xhr.send(data);
  37:          }
  38:      </script>

Testbericht Fujitsu T904

Seit über 10 Jahren verwende ich Tablet PC’s. Mit Windows XP Tablet Edition im Markt eingeführt und von Fujitsu Siemens als Lifebook T3010. Stift und Touch Eingabe mit drehbaren Display. Das Display war in den späteren Versionen sehr gut für außen und innen geeignet, damals noch entspiegelt. 

Abgelöst wird mein knapp 4 Jahre alter T900, mit Zweitakku, 256GB SDD und 8GB RAM. Top Performance, leider etwas schwer und leider auch schon mehrfach runtergefallen. Der Verschluss fürs Display hin, jede Ecke mit Heißkleber renoviert, aber Windows 8.1 installiert.

Das neue T904 kann Leistungsmäßig kaum mehr. Mit dem I7 Prozessor in der Maximalausstattung bei knapp über 2000€. Auch die Ausstattung Wlan, Fingerprint Scanner, UMTS Modem, GPS, Smart Card Reader waren schon im T900 enthalten. Nur mit den Treibern war es ein gefummle.

Immerhin sechs Monate musste ich von der Ankündigung bis zur Verfügbarkeit meines neuen Dauerbegleiters warten. Weg musste dafür das Surface Pro. Als Consuming Device nehme ich den Dell Tablet Venu 8 pro. Beide eint ein super Display mit hoher Helligkeit, die trotz Hochglanz auch außen fast immer lesbar ist.

Den Geräten ist auch die lange Akku Laufzeit gemein. Das T904 soll laut Hersteller über 8h schaffen. In meiner Anwendung mit Visual Studio waren es immer mindestens 6h. Die positive Überraschung war die Ladebuchse, ident mit allen älteren Modellen und weit verbreiteter Standard. Auch die Ladezeit ist extrem schnell. Das mitgelieferte Netzteil ist zwar klein nutzt aber ein Kabel mit Schutzkontakt. Deswegen verwende ich mein altes flaches Netzteil mit Rasiererstecker. Kleines Detail, aber unverzichtbar für mich, der Akku lässt sich einfach entnehmen. Darunter findet sich auch der UMTS Kartenleser.

Das Gerät ist wirklich leicht geworden, entsprechend dem INTEL Ultrabook Standard auch superflach, trotz drehbarem Display. Mit rund 1,5 kg die Hälfte des Vorgängers. Allerdings hat nun kein DVD Rom mehr Platz. Eine ausgefallene Klappkonstruktion musste sich die Ingenieure für die RJ45 Buchse einfallen lassen. Damit spart man es sich einen Adapter (wo ist der grad abgeblieben?) mitzuführen.

 

Weiters vorhanden 2x USB 3 (mehr wären besser). Das ist echter Knüller im Vergleich zum Vorgänger wenn man mal von einem USB 3 Stich kopiert. Ein HDMI statt VGA. Da muss ich wohl nun doch einen Adapter mitführen und eine Kombibuchse Speaker und Mikrophon.

Auf das extrem hochauflösende Display von  2560x1440 auf 13,3” mit 350cd/m² könnte ich gut verzichten. Es ist zwar vermutlich das Beste was aktuell am Markt so zu haben ist und muss sich vor keinem Apple Gerät verstecken. Aber.. Windows 8.1 kommt mit High DPI nicht wirklich klar. Dies liegt vor allem an den Softwareherstellern die ihre Programme anpassen müssten. Für mich sehr unangenehm ist der Betrieb mit dem externen Monitor. Die Probleme sind systematisch und nicht wenige schreiben in Ihren Blogs, das sie entsprechende Geräte wieder returniert haben. Nichts was man Fujitsu und Microsoft direkt anlasten kann.

Vorinstalliert ist Windows 8.1 das bei mir leider nicht 100% stabil läuft. Ich habe schon Bluescreen gesehen und der Fingerprint Scanner zeigt ab und zu keine Reaktion. Auch der Wlan Adapter ist spurlos verschwunden und erst nach Reboot wieder sichtbar gewesen.

Alles in allem keine uneingeschränkte Kaufempfehlung aber für meine Bedürfnisse das beste Gerät im Markt.

Kurs: SQL Server 2014 Umstieg, SQL Server 2014 Neuerungen, SQL Server Schulung SQL Server Training, SQL Server Seminar, SQL Server Kurs,  SQL Server Schulung, Weiterbildung, Fortbildung, Datenbankadministration

Month List