ASP.NET Menu HTML Rendering

by Hannes Preishuber20. Mai 2013 10:37

Das HTML Menü Steuerelement steckt voller Magie. Es kann wie von Geisterhand auf und zuklappen, berücksichtigt die Rechte des angemeldeten Benutzers und kann sogar über einen Sitemapprovider Datengebunden werden. Aber Magier schlagen auch mal über die Strenge wenn sie von Ihren Fähigkeiten zu überzeugt sind und streben die totale Vernichtung an. So lernt man das jedenfalls aus Harry Potter. Der ach so nette Tom Riddle mutiert zu Voldemort, dem rachsüchtigen dunklen Magier der vor nichts zurückschreckt.

Nun bin ich kein fanatischer Verfechter von der Reinheit des Blutes und gebe auch Muggle Zauberern ihre Chance.

Ein Menü Control erzeugt Serverseitig HTML Code, samt CSS und Zauberscript. Als Startpunkt wird die Navigation der mobilen ppedv Kurs Anwendung verwendet.  Zwar könnte man mit einer einfach UL LI Liste das Ziel einfacher erreichen, aber der Erkenntniswert würde fehlen. Also los geht's mit “Zaubern mit Menu”

   1:<form id="form1" runat="server" data-role="page">
   2:<div data-role="content">
   3:
   4: <div>
   5: <asp:Menu ID="div1" runat="server"
   6: data-role="navbar">
   7: <Items>
   8: <asp:MenuItem NavigateUrl="~/S/2,12,14,19" Text="Windows & .NET" />
   9: <asp:MenuItem NavigateUrl="~/S/3,7" Text="Architektur&Dev" />
  10: <asp:MenuItem NavigateUrl="~/S/11" Text="Web Development" />
  11: <asp:MenuItem NavigateUrl="~/S/13,18,22" Text="SharePoint" />
  12: <asp:MenuItem NavigateUrl="~/S/5,6,20" Text="Server Administration" />
  13: <asp:MenuItem NavigateUrl="~/S/9,7" Text="Office u.a." />
  14: <asp:MenuItem NavigateUrl="~/S/4,21" Text="SQL & BI" />
  15: <asp:MenuItem NavigateUrl="~/kontakt" Text="Kontakt" />
  16: </Items>
  17: </asp:Menu>
  18: </div>
  19: 
  20:</div>
  21:</form>

Vor der vierte Folge (also bis ASP.NET 3.5) wurde daraus gar furchterbarer HTML Code, der sich dabei der dunklen Künste von HTML Tables bediente.

Wer diese dunklen Zeiten wieder heraufbeschwören möchte, kann dich dunkle Kammer von Webconfig aufsuchen und dort folgenden Zauberspruch murmeln

   1: <pages controlRenderingCompatibilityVersion="3.5" >

Seit der Stein der Weisen zurück nach Redmond gekehrt ist, serviert auch das Menu Control reines DIV, UL und LI, allerdings noch mit allerlei wundersamer Zutaten.

   1:<form method="post" action="WebForm24.aspx" id="form1" data-role="page">
   2:<div class="aspNetHidden">
   3:<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="2hB6MZYCbAUooxZUPVYswq
5ZiGyg0TutsRjTuqwqOWRyoGlxBHvPvMBmA7xs3dMHYdKdBZDFZgcT75zNssO11aaJ3fOdCsoYUd3eQBY1x7c="
/>
   4:</div>
   5: 
   6: 
   7:<script src="/WebResource.axd?d=fqV81KWLWhVg-lLAb4IT6z7dNzU96UkgnAUvd3TznA7r7jmBA8d3Ppk0
-TpQJTuNUNdLcYMr6iHiLhrQ1-FE8y8o-CmqZlIAp78DXeQ-4cw1&amp;t=634897471112455790"
type="text/javascript">
</
script>
   8:        <div data-role="content">
   9:
  10:            <div>

11: <a href="#div1_SkipLink" style="position:absolute;left:-10000px;
top:auto;width:1px;height:1px;overflow:hidden;"
>Navigationslinks überspringen</a>
<div data-role="navbar" id="div1">

  12:    <ul class="level1">
  13:        <li><a class="level1" href="S/2,12,14,19">Windows & .NET</a></li><li>
<a class="level1" href="S/3,7">Architektur&Dev</a></li><li><a class="level1" href="S/11">Web Development</a>
</li><li><a class="level1" href="S/13,18,22">SharePoint</a></li><li>
<a class="level1" href="S/5,6,20">Server Administration</a></li><li><a class="level1" href="S/9,7">
Office u.a.</a></li><li><a class="level1" href="S/4,21">SQL & BI</a></li><li>
<a class="level1" href="kontakt">Kontakt</a></li>
  14:    </ul>
  15:</div><a id="div1_SkipLink"></a>
  16:            </div>
  17: 
  18:        </div>
  19:
  20:<script type='text/javascript'>new Sys.WebForms.Menu({ element: 'div1',
disappearAfter: 500, orientation: 'vertical', tabIndex: 0, disabled: false });</script></form>

Fügt man nun noch ein wenig Jquery Mobile Kraut hinzu, erscheint ein Fenster (Aparecium).

image

Allerdings hat der dunkle Lord hier noch seine Finger im Spiel. Die Breite der Menuitems ist begrenzt. Um diesen Fluch aufzuheben, muss man einen Finite Incantatem (Gegenzauber) sprechen. Damit hebt man allerdings nur die Wirkung auf, das Sys.WebForms.Menu aus Zeile 20 bleibt.

   1:</asp:Menu>
   2:<script type="text/javascript"> Sys.WebForms.Menu = "";</script>

Um die Schönheit und Reinheit des HTML zu verbessern braucht es noch Zutaten derer Vier.

   1:   EnableViewState="false" IncludeStyleBlock="false" ClientIDMode="Static" SkipLinkText=""

Auf der Fiddler Karte erscheint so der direkte und damit optimale Weg.

image

Letztendlich  gibt es ein Happy End aber es ist das letzte Kapitel noch nicht geschrieben. Wir warten sehnsüchtig auf die  Zauberer aus Redmond.

   1: <script src="/WebResource.axd?d=fqV81KWLWhVg-lLAb4IT6z7dNzU96UkgnAUvd3TznA7r7jm
BA8d3Ppk0-TpQJTuNUNdLcYMr6iHiLhrQ1-FE8y8o-CmqZlIAp78DXeQ-4cw1&amp;t=634897471112455790"

type="text/javascript"></script>
   2:<div data-role="content">
   3: 
   4: <div>
   5: <div data-role="navbar" id="div2">
   6: <ul class="level1">
   7: <li><a class="level1" href="S/2,12,14,19">Windows & .NET</a></li>
   8: <li><a class="level1" href="S/3,7">Architektur&Dev</a></li>
   9: <li><a class="level1" href="S/11">Web Development</a></li>
  10: <li><a class="level1" href="S/13,18,22">SharePoint</a></li>
  11: <li><a class="level1" href="S/5,6,20">Server Administration</a></li>
  12: <li><a class="level1" href="S/9,7">Office u.a.</a></li>
  13: <li><a class="level1" href="S/4,21">SQL & BI</a></li>
  14: <li><a class="level1" href="kontakt">Kontakt</a></li>
  15: </ul>
  16: </div>
  17: </div>
  18: 
  19:</div>

Tags:

ASP.NET | JavaScript

mobile Websites SEO per Sitemap

by Hannes Preishuber16. Mai 2013 08:44

Ein recht junges Problem mobiler ASP.NET Websites ist die Indizierung durch Suchmaschinen wie BING oder Google. Persönlich mag ich den Begriff SEO (Search Engine Optimization) nicht, da er vorgaukelt, das man Websites für Google optimieren muss. Sollte man eigentlich nicht. Websites sollten optimal für den Benutzer sein. Der Job von Suchmaschinen wiederum ist es optimale Ergebnisse passend zu den Bedürfnissen des Users zu bringen.

In diesem Fall ist es aber anders. Parallel zu www.ppedv.at Website gibt es eine Site die fokussiert auf mobile Nutzung ist http://m.ppedv.at. Inhaltlich sehr unterschiedlich. Ruft man nun mit einem Browser die Website auf, sieht man quasi nichts. Auch der Spider (bot) der Suchmaschine sieht nichts und kann damit auch keinen Links folgen.

Laut google sollen mobile Sitemaps helfen. In den Webmaster Tools kann man sein Begehr schon mal anmelden.

image

Ich formulier das so, weil die Position und Verhalten von Google Königlich und ich Untertan (unterirdisch) bin.

In meinem Fall lasse ich nun so eine mobile Sitemap per Code generieren um immer aktuell zu sein. Entsprechende online und offline Sitemap Generatoren bieten das nicht.

Für die fixen Hyperlink wurde eine XML Vorlage angelegt. Darin kann man auch die XML Struktur gut herauslesen.

   1:<?xml version="1.0" encoding="UTF-8"?>
   2:<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
   3:xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0">
   4:<url>
   5:<loc>http://m.ppedv.at/</loc>
   6:<lastmod>2013-05-14</lastmod>
   7:<changefreq>monthly</changefreq>
   8:<priority>0.5</priority>
   9:<mobile:mobile/>
  10:</url>
  11:<url>
  12:<loc>http://m.ppedv.at/S/11</loc>
  13:<lastmod>2013-05-14</lastmod>
  14:<changefreq>monthly</changefreq>
  15:<priority>0.5</priority>
  16:<mobile:mobile/>
  17:</url>
  18:<url>
  19:<loc>http://m.ppedv.at/S/2,12,14,19</loc>
  20:<lastmod>2013-05-14</lastmod>
  21:<changefreq>monthly</changefreq>
  22:<priority>0.5</priority>
  23:<mobile:mobile/>
  24:</url>
  25:<url>
  26:<loc>http://m.ppedv.at/S/5,6</loc>
  27:<lastmod>2013-05-14</lastmod>
  28:<changefreq>monthly</changefreq>
  29:<priority>0.5</priority>
  30:<mobile:mobile/>
  31:</url>
  32:<url>
  33:<loc>http://m.ppedv.at/kontakt</loc>
  34:<lastmod>2013-05-14</lastmod>
  35:<changefreq>monthly</changefreq>
  36:<priority>0.5</priority>
  37:<mobile:mobile/>
  38:</url>
  39:<url>
  40:<loc>http://m.ppedv.at/S/4,21</loc>
  41:<lastmod>2013-05-14</lastmod>
  42:<changefreq>monthly</changefreq>
  43:<priority>0.5</priority>
  44:<mobile:mobile/>
  45:</url>
  46:</urlset>

Dann werden per VB.NET und XML Literals ( eine wunderbare Funktion die es in C# nicht gibt) neue Nodes anhand einer Tabelle hinzugefügt. Das ganze in einer HTML entkernten ASPX Seite.

   1:Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   2:Dim x As XDocument = XDocument.Load(Server.MapPath("~/app_data/sitemap.xml"))
   3:        x.AddAnnotation(SaveOptions.OmitDuplicateNamespaces)
   4: 
   5: 
   6:Dim pp As ppcompanyEntities = New ppcompanyEntities
   7:
   8:Dim query = pp.SEMINARE.Where(Function(s) s.isActive )
   9:For Each s In query
  10: Dim n = <url>
  11:                        <loc>http://m.ppedv.at/d/<%= s.SEID %></loc>
  12:                        <lastmod><%= Date.Now.ToString("yyyy-MM-dd") %></lastmod>
  13:                        <changefreq>daily</changefreq>
  14:                        <priority>0.5</priority>
  15:                        <mobile:mobile/>
  16:                    </url>
  17:            x.<urlset>.First.Add(n)
  18:Next
  19: 
  20: 
  21:        Response.Write(x.ToString)
  22: 
  23: 
  24:End Sub

Allerdings sind im urlset zwei Namensräume deklariert. Um den Code (hier speziell mobile: ) überhaupt kompilieren zu können braucht es passende Imports.

   1:Imports <xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0">
   2:Imports <xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

Danach werden diese XML Namensräume sozusagen doppelt und mehrfach aber bei jedem URL Element eingefügt. Zeile 3 verhindert dieses wiederum.

Dann bleibt nur noch abzuwarten, wann königliche Hoheit, mein Gnadenersuch um wohlwollende Aufnahme in den Index auch erhört.

Wer das komplette XML File benötigt, kann es natürlich unter http://m.ppedv.at/sitemap.xml abrufen.

Tags:

.Net | SEO | XML

Wissensmanagement - Wissensidentifikation

by Shila Ipek14. Mai 2013 15:15

Einige wichtige Themen meiner Bachelorarbeit sind die Wissensidentifikation und die Wissens(ver)teilung.

Hier ein Einblick in das Thema der Wissensidentifikation.

Heutzutage fällt es vielen Unternehmen schwer, den Überblick über sowohl interne, wie auch externe Informationen und Daten zu behalten, was zu mangelnder Transparenz und in weiterer Folge zu Ineffizienzen und falschen Entscheidungen führen kann. Die Wissensidentifikation bezieht sich vor allem auf die Analyse und Definition des Wissensumfeldes des Unternehmens, um interne und externe Transparenz zu wahren beziehungsweise zu schaffen und die Mitarbeiter des Unternehmens dadurch in ihren Suchaktivitäten zu unterstützen. (Probst, et al., 2006)

Aspekte beziehungsweise Themen im Bereich der Wissensidentifikation in einem Unternehmen beziehen sich auf:

  • Die interne Intransparenz – Oft vorzufinden in multinationalen Großunternehmen, welches häufig durch die Dezentralisierung verursacht wird.
  • Den Umgang mit der Informationsflut – Die Menge an Informationen zwingt zur Selektierung, wobei aber oft das gerade benötigte Wissen nicht beziehungsweise schwer auffindbar ist.
  • Den Grad der angestrebten Transparenz – Absolute Transparenz ist der Erfahrung nach nicht umsetzbar und häufig auch nicht erstrebenswert. Vielmehr benötigt ein Unternehmen angemessene Transparenz über kritische Wissensfelder, um Kompetenzen stärken, beziehungsweise neue aufbauen zu können.
  • Der personellen und strukturellen Transparenz – Die personelle Transparenz bezieht sich dabei auf das Erlangen eines Bewusstseins über die Fähigkeiten des Unternehmens, beziehungsweise der eigenen Mitarbeiter. Ebenfalls wichtig ist die Transparenz über das gemeinsame (kollektive) Wissen. Hier geht es darum, interne Spielregeln über Prozesse der Wissensteilung und Kommunikationsnetzwerke des Datenaustausches aufzudecken.
  • Der Beleuchtung des Wissensumfeldes des Unternehmens – Hier ist die Devise, über den Tellerrand hinauszuschauen. Chancen der Kooperation außerhalb des Unternehmens, in Form von externen Experten oder Netzwerken, sollten zum Wissensimport genutzt werden. Des Weiteren kann das Unternehmen dadurch eigene Wissenslücken und Defizite aufdecken beziehungsweise sichtbar machen.
  • Dem externen und internen Benchmarking – Auch die Methoden des externen Benchmarkings geben dem Unternehmen die Möglichkeit, Wissens- und Fähigkeitslücken aufzudecken. Das interne Benchmarking stellt des Weiteren eine der Voraussetzungen für den Austausch von Best-Practices dar.
  • Dem Bewusstsein, beziehungsweise dem Eingeständnis des eigenen Nicht-Wissens – Dies bildet die Grundlage, beziehungsweise den ersten Schritt, um einen Lernprozess anzustoßen und bestehende Wissens- bzw. Fähigkeitslücken zu schließen.

(Probst, et al., 2006)

Einflussfaktoren auf die Wissenstransparenz

Die oft nicht geregelten Zuständigkeiten im Bereich der Wissenstransparenz, beziehungsweise der Wissensidentifikation, stellt einen negativen Einflussfaktor dar. Gesammelte Informationen über Fähigkeiten der eigenen Mitarbeiter, werden oft nicht im gesamten Unternehmen verbreitet, sondern verbleiben meist in der Personalabteilung. Umstrukturierungen im Unternehmen, Job-Rotation und eine hohe Fluktuationsrate führen ebenfalls dazu, dass das Unternehmen den Überblick der Zuständigkeiten verliert.

Als positiver Einflussfaktor kann jedoch gesehen werden, dass heutzutage die technische Infrastruktur, um eine angemessene Wissensidentifikation gewährleisten zu können, in den meisten Unternehmen bereits vorhanden ist. Kaum ein PC ist heute nicht in irgendeiner Form vernetzt! Auch das vermehrte Einsetzen, beziehungsweise der Aufstieg von Wissensarbeitern, führte zu einer Verbesserung der Kommunikation in Unternehmen.

(Probst, et al., 2006)

Methoden zur Identifikation und Darstellung von Wissensbeständen, beziehungsweise Wissensträgern und Experten

Verschiedene Arten von Wissenskarten:

 

 

Wissens(land)karten stellen eine effektive, nicht aufwändige Methode zur grafischen Visualisierung von Verzeichnissen über Wissensbestände, Wissensträger, Wissensquellen, Wissensstrukturen und Wissensanwendungen dar. Wissenskarten erhöhen die Wissenstransparenz im Unternehmen und bieten die Möglichkeit, die Informationen auf einfachem Weg, einem großen Personenkreis (zeit- und ortsunabhängig) zugänglich zu machen, zum Beispiel über das Firmen-Intranet. (Probst, et al., 2006)

Einige Voraussetzungen sollten jedoch bei der Verwendung von Wissenskarten gegeben, beziehungsweise vorhanden sein.

  • Die Verantwortlichkeiten in Hinblick auf die Wissensidentifikation sind geregelt.
  • Jeder Mitarbeiter verfügt über die nötige Klarheit im Wissensmanagement.
  • Jeder Mitarbeiter ist in der Lage seine Wissenslandkarte selbst zu pflegen.
  • Das Unternehmen hat eine einheitliche Sprache bezüglich Wissensfelder, -träger und –ausprägungen definiert, beziehungsweise etabliert.

 

(Probst, et al., 2006)

Ein weiteres Beispiel einer Wissensträgerkarte sind die Yellow Pages (Gelbe Seiten bzw. Wissensbranchenbuch):

 

 

Yellow Pages eignen sich vor allem bei Unternehmen mit viel Erfahrungswissen, welches eher schwer zu speichern ist. Zu den, in den Yellow Pages enthaltenen Informationen über die Kernprozesse des Unternehmens, sind optimaler Weise die jeweiligen Wissens- und Entscheidungsträger mit deren Kontaktdaten hinterlegt. Mitarbeiter erhalten so die Möglichkeit, Experten schnell ausfindig zu machen. In weiterer Folge können die Yellow Pages über das Intranet publiziert werden. Verfügt jeder Mitarbeiter über eine eigene Seite im Intranet, können diese mit den jeweiligen Wissensgebieten der Yellow Pages verlinkt werden. Des Weiteren kann jeder Mitarbeiter seine eigene Seite regelmäßig selbst pflegen. (Probst, et al., 2006)

Wissenslücken

Wie bereits erwähnt wurde, werden im Zuge der Wissensidentifikation auch eventuell vorhandene Wissenslücken, beziehungsweise Defizite aufgedeckt. Durch das externe Benchmarking können in weiterer Folge externe Wissensquellen in Bezug auf ihre Verwertbarkeit im Unternehmen beurteilt und best-practices identifiziert werden.

 

 

Jedoch können durch das Benchmarking nur Lücken der ersten Ebene (siehe oben „Lücke 1") aufgedeckt und durch Maßnahmen des Wissenserwerbs (zum Beispiel durch Rekrutierung, Imitation, etc.) geschlossen werden. Die „Lücke 2“ stellt eine größere Herausforderung dar. Um schwer imitierbare, organisationale Kompetenzen aufzubauen, ist es notwendig neues Wissen zu entwickeln (zum Beispiel durch Forschung, Marktstudien, etc.) oder zu erwerben. Die Entscheidung über Wissenserwerb oder Wissensentwicklung sollte allerdings wohl überlegt sein. Diese Entscheidung sollte nicht nur von kurzfristigen finanziellen Erwägungen abhängig gemacht werden. (Probst, et al., 2006)

 

 



 

 

Tags:

Wissensmanagement

JQuery Mobile Ajax Callbacks

by Hannes Preishuber13. Mai 2013 21:13

ASP.NET Websites und das ASP.NET Scriptmanager Steuerelement können unter Umständen in Konflikt geraten. Grund ist, das Jquery Mobile automatisch clientseitige redirects per XMLHTTPRequest nachlädt.

image

Der Vorteil dieses Callbacks ist, das man dann den Seitenwechsel wunderbar animieren kann. Es reicht ein simples Attributim Hyperlink  und dann wird geslided  oder gefaded beim Übergang auf die nächste ASPX oder HTML Seite.

   1:<ul data-role="listview">
   2:<li><a href="WebForm22a" data-transition="slide">test</a></li>
   3:</ul>

slides

Wie erwähnt kann das aber auch Störungen hervorrufen. Um das Standard verhalten zu erzeugen, muss vor dem Laden des JQuery Mobile Scripts, AjaxEnabled auf false gesetzt werden.

   1: <script src="Scripts/jquery-1.8.2.min.js"></script>
   2: <script>$(document).on("mobileinit", function () { $.mobile.ajaxEnabled = false; }); </script>
   3: <script src="Scripts/jquery.mobile-1.3.0.js"></script>

image

Natürlich gibt es dann keine animierten Transitions mehr.

Tags:

ASP.NET | JavaScript | JQuery

Eigenes Website-Template mit Masterpage und CSS als Farmsolution bereitstellen

by MarkusH13. Mai 2013 14:15

Heute möchte ich zeigen, wie ein benutzerdefiniertes Website-Template inkl. eigener Masterpage und CSS-Datei als Farmsolution bereitgestellt werden kann. Im Beispiel habe ich das OrangeWebsite-Template von FreeSMP als benutzerdefinierte Masterpage verwendet.

  1. Zunächst ist in Visual Studio ein neues SharePoint Projekt vom Typ “Site Definition” als Farmsolution zu erstellen.
  2. Zum Einbinden der eigenen Masterpage ist dem Projekt im Solution Explorer mittels Kontextmenü –> Add –> New Item ein Modul hinzuzufügen. Diesem sollte gleich beim Erstellen ein aussagekräftiger Name verfgeben werden (im Bsp. “NewMasterPageModule”). Im neu erschienenen Modulordner ist anschließend ein Unterordner namens “_catalogs” und in diesem widerum einer names “masterpage” zu erstellen. Im Ordner Masterpage wird nun via Kontextmenü –> Add –> Existing Item die Masterpage importiert (vgl. Abbildung in Schritt 9).
  3. In der Elements.xml des Moduls sind weiterhin die Deployment-Pfade wie folgt anzupassen, damit die Masterdatei in den Catalogs-Bereich der Zielwebsite kopiert wird, wo auch die anderen Masterpages liegen.
       1:<?xml version="1.0" encoding="utf-8"?>
       2:<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
       3:<Module Name="NewMasterpageModule" Url="_catalogs/masterpage">
       4:<File Path="NewMasterpageModule\_catalogs\masterpage\OrangeWebsite.master" 
    Url="OrangeWebsite.master" />
       5:</Module>
       6:</Elements>
  4. Die Css-Datei wird nun über ein mapped-SharePoint Folder hinzugefügt. Dies geschieht im Kontextmenü des Projekts –> Add –> SharePoint Mapped Folder –> mit folgender Location: {SharePointRoot}\Template\LAYOUTS\1033\STYLES\. In diesen Mapped Folder wird nun die CSS importiert (Kontextmenü –> Add –> Existing Item).

  5. Gleiches Verfahren kann noch für ein Vorlagen-Image wiederholt werden. In dem Fall allerdings mittels Add –> SharePoint “Images” Mapped Folder und anschließend das Bild hier importieren. Das Vorlagen-Image dient später beim Erstellen einer neuen Website auf Basis des eigenen Website-Templates als Icon bzw. Symbol.

  6. Nun muss noch die CSS korrekt innerhalb der Masterpage referenziert werden. Dies geschieht mittels folgender Zeile:

    <SharePoint:CssRegistration name="OrangeWebsite.css" After="corev4.css"runat="server"/>

    Die Markierung zeigt die Stelle innerhalb der Masterpage, an der die Anpassung vorgenommen wird:
    image
  7. Damit die Masterpage innerhalb der Website-Vorlage überhaupt verwendet werden kann, muss sie innerhalb eines Features bereitgestellt werden. Wenn dem Projekt das erste Artefakt (in unserem Bsp. das Modul für die Masterpage) hinzugefügt wird, erzeugt Visual Studio automatisch ein Feature, welches als Paketierungseinheit für das hinzugefügte Modul dient. Aus Gründen der Übersichtlichkeit und besseren Lesbarkeit empfiehlt es sich, Features mit Hilfe eines aussagekräftigen Namens umzubenennen (z.B. “MasterPage Feature”). Dies geschieht – genau wie das Einstellen des Feature-Scopes auf “Web” – im Feature Designer (Doppelklick auf das Feature oder über Kontextmenü).
    image

  8. Anschließend muss der SiteDefinition (in der Datei Onet.xml) noch die Referenz auf die benutzerdefinierte Masterdatei eingebunden werden. Dies erfolgt zum einen durch das Einfügen des MasterUrl- und CustomMasterUrl-Properties im Configuration-Tag

    <Configuration ID="0" Name="OurTemplate" MasterUrl="_catalogs/masterpage/OrangeWebsite.master" 
    CustomMasterUrl="_catalogs/masterpage/OrangeWebsite.master">


    und zum anderen durch das Einbinden des MasterPage Features im Abschnitt “WebFeatures”. Die FeatureID kann im Feature-Designer unter “Manifest” ermittelt werden.

    <WebFeatures>
    <Feature ID="e627ebdf-38eb-4d87-94b1-bdd8e41a3771"></Feature>
    </WebFeatures>

     

  9. Optional: Wie in Punkt 8 anhand der Attribute zu erkennen ist, wird unterschieden zwischen Standard- (MasterUrl) und benutzerdefinierter (CustomMasterUrl) Masterpage. In diesem Beispiel soll die OrangeMaster sowohl Default als auch Custom Masterpage darstellen. Folgender Schritt ist daher optional und nur dann wichtig, wenn mit Veröffentlichungsseiten (Publishing Pages) gearbeitet werden soll und diese eine andere Masterpage verwenden, als “normale” aspx Pages.
    Soll bspw. die mitgelieferte “Default.aspx” die Custom-Masterpage verwenden, ist in Zeile 1  der Default.aspx das MasterPageFile Attribut auf "~masterurl/custom.master" zu setzen.


    Nachdem der Lösung alle Artefakte hinzugefügt und ggf. umbenannt wurden, sollten im Solution-Explorer zusätzlich folgende Einträge vorhanden sein: 
    image

  10. Zum Testen kann nun mit dem Debuggingvorgang begonnen werden (F5) und auf der Seite im Browser eine neue Website basierend auf dem eigenen Website-Template erzeugt werden. Der Eintrag dazu befindet sich standardmäßig in der Kategorie “SharePoint Customizations”.
    image
    Anschließend wird die Default.aspx als Startseite der neu erstellten Website aufgerufen basierend auf der eigenen Masterpage bzw. CSS-Datei. 
    image

  11. Wenn das Projekt den Anforderungen entspricht kann der Solution Status auf Release gesetzt und im Solution Explorer im Kontextmenü der Lösung mittels “Package” die wsp-Datei erzeugt werden. Diese befindet sich dann im Projktordner unter \bin\Release\ und kann wie üblich mittels Powershell deployt werden.
    image

Tags:

Sharepoint

KeywordQuery Class–deprecated

by Martin Groblschegg6. Mai 2013 20:24

Unlängst habe ich wieder einmal die KeywordQuery Klasse für ein SharePoint Webpart verwendet. Allerdings kam beim Kompilieren das Warning: “  [deprecated ]” was mich, ehrlich gestanden, etwas verwundert hat.

image

Interessanterweise wird jedoch im MSDN die Klasse im Detail beschrieben. (msdn)

Jedoch ist die Lösung einfach. Früher gab es die Klasse im Namespace “Microsoft.SharePoint.Search.Query”. Diese Klasse ist nun veraltet und wird in der nächsten Version nicht mehr enthalten sein. Sucht man im MSND nach dem Begriff “KeywordQuery” wird leider als erstes Suchergebnis die veraltete Klasse angezeigt.

Der richtige Namespace ist “Microsoft.Office.Server.Search.Query”. Bei der Auswahl der dll, die bei bei Referenzen anzugeben ist, muss darauf geachtet werden die richtige auszuwählen!

image

Leider liegen beide Dateien im ISAPI Verzeichnis des SharePoint Servers.

Tags:

Sharepoint

CDN mit ASP.NET Scriptmanager

by Hannes Preishuber1. Mai 2013 07:59

Performance, Performance hallt es durch die Hallen. Wenn man sich allerdings mit Fiddler die Details von Websites und Services ansieht wird man eher Waste, Waste, Waste rufen. Mich wundert es das die Grünen nicht schon lange eine Steuer auf Prozessortakte oder DSL Bits erheben wollen. Verbraucht alles Energieund erzeugt CO2.

Die beiden einfachsten Maßnahmen für einen Web Developer sind httpcompression und CDN (Content Delivery Network).  Bei CDN wird ein Konzept der Shared Ressources gefahren. Eine Resource die zwei Websites brauchen wird geteilt und so nur einmal in den Browser Cache geladen. Wenn man dazu eine öffentliche URL nimmt und alle Menschen dies tun reduziert das nochmals Rechenleistung, eventuell Bandbreite durch Caching. Solche CND Dienste bieten Google und Microsoft. Wenn die Website also nun die sehr verbreitete JQuery Java Script Library verwendet wird sie von einem CDN Server geladen. Dies geschieht meist ein wenig schneller, weil diese Server sehr gut angebunden sind. Wesentlich relevanter ist aber wenn der Benutzer eine andere Website aufruft die ebenfalls Jquery, logischerweise mit der gleichen CDN Referenz, verwendet. Dann wird gar nichts geladen, die Scripte kommen aus dem Browser Cache.

In ASP.NET gibt es den ScriptManager der das Management übernimmt. Zunächst wird eine mini JS Datei erzeugt mit dem Namen hannes.js.

   1:window.Hannes = (function( ) {
   2: 
   3:return "hannes";
   4:    })

Visual Studio kann auch die minimierte Versionen erzeugen und vor allem aktuell halten. Rechtclick auf js, Webtools, minifiy JS.

image

Wenn man nun in hannes.js eine Code Änderung vornimmt, wird die min Datei automatisch geändert.

Um diese Dateien einzubinden wird das ASP.NET Scriptmanager Control eingefügt. In der Praxis vermutlich in die Masterpage.

   1:<asp:ScriptManager ID="ScriptManager1" runat="server" EnableCdn="true" 
EnableCdnFallback="true"
   2: AjaxFrameworkMode="Disabled">
   3: <Scripts>
   4: <asp:ScriptReference Name="hannes" />
   5: 
   6: </Scripts>
   7:</asp:ScriptManager>
   8: 
   9:<div id="hannesdiv">
  10:</div>
  11:<script>
  12:            document.getElementById("hannesdiv").innerText = window.Hannes();
  13:</script>

Mit dem Attribut AjaxFrameworksMode wird verhindert das einige Microsoft Scripte (Stichwort Webbundle) geladen werden, die wir hier nicht brauchen. Relevant sind die beiden anderen Attribute mit CDN.

Wie im Code zu sehen ist wird das Script per Namen und nicht per Dateinamen referenziert (was auch möglich ist für klassisches JavaScript einbinden).

Die Script Referenz Hannes muss als Ressourcemapping definiert werden. Dazu erfinden wir ein eigenes CDN auf einem unser WebServerzu Demozwecken. Es werden Referenzen zur Debugversion  (web.config debug=true), lokalen Fallback Version und zum ppedv CDN definiert. Der Ausdruck LoadSuccessExpression wird später verwendet um den Fallback im Browser zu realisieren (Achtung Case Sensitiv)

   1: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   2:Dim Mapping = ScriptManager.ScriptResourceMapping
   3:        Mapping.AddDefinition("hannes", New ScriptResourceDefinition With
   4:        {
   5:            .Path = "~/Scripts/hannes.min.js",
   6:            .DebugPath = "~/Scripts/hannes.js",
   7:            .CdnPath = "http://www.ppedv.at/scripts/hannes.min.js",
   8:            .CdnDebugPath = "http://www.ppedv.at/scripts/hannes.js",
   9:            .CdnSupportsSecureConnection = True,
  10:                .LoadSuccessExpression = "window.Hannes"
  11:        })
  12: 
  13:End Sub

Im Praxistest wird der Request von meinem lokalen Computer auf den IIS Express durchgeführt. Im ersten Versuch mit Wlan im zweiten ohne Internet Verbindung.

image

Der Fallback greift dann auf das lokale Script zurück, das hier kleiner erscheint. Der Grund ist, das am ppedv.at IIS 8 offensichtlich HTTP Compression für die Extension JS (noch) nicht aktiviert ist.

Im Browser HTML Quellcode kann man dann erkennen wie das funktioniert und sieht den alten Bekannten aus LoadSuccessExpression wieder.

   1:<script src="http://www.ppedv.at/scripts/hannes.js" type="text/javascript"></script>
   2:<script type="text/javascript">
   3://<![CDATA[
   4:(window.Hannes)||document.write('<script type="text/javascript" src="Scripts/hannes.js"><\/script>');//]]>
   5:</script>

Im echten Web Projekt wird man dann allerdings häufiger Script Referenzen auf Jquery und co finden. Einige Bundles hat Microsoft schon vorkonfiguriert.

Tags:

ASP.NET | JavaScript | HTML

Entity Framework 5.0 in SharePoint 2013 nutzen

by Martin Groblschegg29. April 2013 21:23

Um den Datenzugriff innerhalb von Webparts einfach zu gestalten, kann das Entity Framework innerhalb eines SharePoint Projektes genutzt werden. Allerdings gilt es einige “Kleinigkeiten” zu beachten.

Zunächst wird ein “Entity Data Model” zum Projekt hinzugefügt, jedoch kann der Connections-String nicht in der App.Config gespeichert werden.

image

Hürde 1

das EF Assembly wird vom SharePoint Server nicht geladen, da diese nicht als SafeControl eingetragen ist. Um einen SafeControl Eintrag zu setzen, muss das Package-Manifest bearbeitet werden.

In der Karteikarte “Advanced” kann mit “add” ein Assembly eingetragen werden.

image

Hürde 2

Bis zum EF 4 konnte bei der Initialisierung des Context ein Connection String angegeben werden. EF 5 bietet diesen Konstruktor nicht mehr. Die Context Klasse ist jedoch als partial Class definiert, daher kann ein eigener Konstruktor, der einen Connections String übergeben bekommt definiert werden. In das Projekt ist eine weitere Klasse aufzunehmen, die folgenden Code enthält:

   1:public partial class Entities : DbContext
   2:    {
   3:public Entities(string ConnectionString)
   4:            : base(ConnectionString)
   5:        {
   6:        }
   7:    }

Nachdem die Klasse geschrieben ist, kann der Connection String formuliert werden. Wichtig ist, dass der vollständige, für das EF formulierte Connection String verwendet wird. Sollte nur ein “simpler” SQL Server Connection String verwendet werden, kann es zu einer UnintentionalCodeFirstException kommen. Der Connection String kann aus dem “Update Model from Database”-Dialog kopiert werden. Die im Connection String verwendeten doppelten Hochkomma können durch einfache Hochkomma ersetzt werden.

image

Im Connection String muss noch die User Information angepasst werden. Im Code BSP wird diese Information hardcoded im Source Code abgelegt. Eine Erklärung wie die Credentials im Secure Store Service abgelegt und wieder abgefragt werden können ist hier zu finden.

Der Code für die Datenabfrage sieht nun so aus:

   1: string co = @"
   2:                metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;
   3:                provider=System.Data.SqlClient;
   4:                provider connection string='data source=server1;
   5:                    initial catalog=Northwind;
   6:                    user Id=sa;
   7:                    password=ppedvAG
   8:                    MultipleActiveResultSets=True;
   9:                    App=EntityFramework'
  10:                ";
  11: 
  12:            Entities ctx = new Entities(co);
  13: 
  14:            var erg = from c in ctx.Customers
  15:                      select c;

Tags:

Sharepoint | EntityFramework

Listview forward Paging für mobile

by Hannes Preishuber27. April 2013 21:07

In mobilen Clients wird gerne eine Scrollbare Liste angezeigt, die der Benutzer mit einem Button am Boden erweitern kann. Bei jedem Click wird die Liste länger und länger. Als Beispiel die Suche nach Bahn oder Flug. Alle Flüge ab 08:00, dann werden 10  angezeigt. Mit click auf [weitere] kommen dann noch 10 hinzu usw.

Um das Layout zu erstellen, wird eine entkernte ASPX Seite erstellt, die dann einfach die TR und TDs liefert passend zum Paging. Dies ist damit die Layout Template Page.

   1:<%@ Page Language="vb" AutoEventWireup="false" 
CodeBehind="WebForm20.aspx.vb" Inherits="WebApplication2.WebForm20" %>
   2: 
   3:<asp:listview id="ListView1" runat="server" selectmethod="loadData" 
itemtype="WebApplication2.Customers">
   4:<ItemTemplate>
   5:<tr>
   6: <td><%#Item.Customer_ID%></td>
   7: <td><%#Item.Company_Name%></td>
   8: <td><%#Item.Country%></td>
   9: <td><%#Item.Address%></td>
  10:</tr>
  11:</ItemTemplate>
  12:</asp:listview>

Damit der Compiler das ohne Form Element schluckt muss man eine Methode überladen. Für das Paging wird ein Querystring verwendet und dann per LINQ gefiltert und per ASP.NET modelbinding (ab 4.5) gebunden.

   1:Public Class WebForm20
   2:Inherits System.Web.UI.Page
   3:Public Overrides Sub VerifyRenderingInServerForm(ByVal control As Control)
   4:Return
   5:End Sub
   6: 
   7:Public Function loadData(<QueryString> ByVal p As String) As IQueryable(Of Customers)
   8:Dim nw As New NorthwindEntities1
   9:Dim query = nw.Customers.OrderBy(Function(c) c.Customer_ID).Skip(p * 5).Take(5)
  10:Return query.AsQueryable
  11:End Function
  12:End Class

Nun wird die eigentliche ASPX Seite erstellt. In dieser wird dann der HTMLTable Kopf Bereich angelegt. Das ganze ist JQuery Mobile ready, so das man auch responsive mobile Web Pages erstellen kann um verschiedene Devices zu adressieren.

   1:<form id="form1" runat="server" data-role="page">
   2:<table id="GridView1" >
   3: <thead>
   4: <tr>
   5: <th data-priority="4">Customer_ID</th>
   6: <th data-priority="1">Company_Name</th>
   7: <th data-priority="2">Country</th>
   8: <th data-priority="3">Adress</th>
   9: </tr>
  10: </thead>
  11: <tbody>
  12: </tbody>
  13:</table>
  14:<a href="#" onclick="loadmeins()" data-role="button">mehr</a>

Per Ajax Callback wird die Template Page aufgerufen und der erhaltene TR String an die bestehende Tabelle angefügt

   1:function loadmeins() {
   2:            $.ajax({
   3:                type: "GET",
   4:                url: "WebForm20.aspx?p=" + page,
   5:                success: function (result) {
   6:                    $('#GridView1 tbody').append(result);
   7:                    page++;
   8:                }
   9:            });
  10:        }
  11:var page = 0;
  12:        loadmeins();

Der Benutzer kann nun mehr drücken, bis das LINQ Statement keine Daten mehr liefert (und darüber hinaus)

image

Im Vergleich zu meiner anderen Lösungwerden nun weniger als die Hälfte an Daten übertragen. Dazu kommt, man auch das ASP.NET Page Caching Feature verwenden kann.

Tags:

ASP.NET | Ajax | HTML | JQuery | JScript | JavaScript | Web

ASP.net Gridview Forward Only Paging

by Hannes Preishuber27. April 2013 19:41

Erinnern Sie sich noch an “Frames sind böse”? Oder HTML Layout Tabellen sind pfui. Dabei sind <table> Element immer noch völlig OK wenn es um die Darstellung von Daten in Tabellen geht. Streng genommen sind sie sogar erste Wahl, da die Struktur z.B. von Lesegeräten für Sehbehinderte ausgewertet werden kann. 

GirdView Bäh oder Super?

Eine HTML Tabelle sollte heute so aussehen. Ein Thead und tbody Element und die Header Spalten mit th.

   1:<div>
   2:<table cellspacing="0"id="GridView1" style="border-style:None;border-collapse:collapse;">
   3:<thead>
   4: <tr>
   5: <th scope="col">Customer_ID</th><th scope="col">Company_Name</th>
<
th scope="col">Contact_Name</th><th scope="col">Postal_Code</th>
   6: </tr>
   7:</thead><tbody>
   8: <tr>
   9: <td>ALFKI</td><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>12209</td>
  10: </tr><tr>
  11: <td>ANATR</td><td>Ana Trujillo Emparedados y helados</td><td>Ana Trujillo</td>
<
td>05021</td>
  12: </tr><tr>
  13: <td>ANTON</td><td>Antonio Moreno Taquer&#237;a</td><td>Antonio Moreno</td>
<
td>05023</td>
  14: </tr><tr>
  15: <td>AROUT</td><td>Around the Horn</td><td>Thomas Hardy</td><td>WA1 1DP</td>
  16: </tr><tr>
  17: <td>BERGS</td><td>Berglunds snabbk&#246;p</td><td>Christina Berglund</td>
<
td>S-958 22</td>
  18: </tr>
  19:</tbody>
  20:</table>
  21:</div>
  22: 

Man wird es kaum glauben. Obiges HTML wird so vom ASP.NET Gridview Control erzeugt. Einen Haken gibt es, um das Rendering des THEAD und TBODY  zu erzwingen muss man im Code zb Page_Load das Attribut  setzen GridView1.HeaderRow.TableSection = TableRowSection.TableHeader

Das GridView wird per ASP.NET Model Binding an die Northwind Customers gebunden

   1:<asp:GridView ID="GridView1" runat="server" SelectMethod="loadData" AutoGenerateColumns="false"
   2:ShowHeaderWhenEmpty="true" BorderStyle="None"ClientIDMode="Static"Showheader="true"
   3:EmptyDataText="empty" GridLines="None" ViewStateMode="Disabled">
   4:<Columns>
   5:<asp:BoundField DataField="Customer_ID"HeaderText="Customer_ID" ReadOnly="True"/>
   6:<asp:BoundField DataField="Company_Name" HeaderText="Company_Name" ReadOnly="True"/>
   7:<asp:BoundField DataField="Contact_Name" HeaderText="Contact_Name" ReadOnly="True"/>
   8:<asp:BoundField DataField="Postal_Code" HeaderText="Postal_Code" ReadOnly="True" />
   9:</Columns>
  10: 
  11:</asp:GridView>

Erster Checkpoint ist also bestanden. Das Gridview Steuerelement erzeugt sauberen HMTL 5 Code. Einzig ein seltsames DIV liegt noch außen rum. Ich kann also das Gridview als Server Seitiges Layout Template benutzen.

Teil 2 meiner Idee ist, für das Paging immer fünf Datensätze im Gridview anzeigen zu lassen und nur den Table Part per RenderControl am Server erzeugen zu lassen. Ein kleines Stück JavaScript soll dann eine Methode am Server aufrufen und die pure <TABLE><TR><TD> .. Sequenz einfach unten an die bestehende Tabelle anhängen.  Die Server Methode kann man per miniService in die Page per <WebMethod> einbetten. Leider liefert diese nur SOAP oder JSON. Die wunderbare native HTML Code Anweisung muss also in ein JSON serialsiert und am Client wieder deserialisiert werden. Ohne diesen Overhead (an dem ich später noch feile) wird dieser Ansatz wesentlich effizienter sein als ein Web Api, Knockout und HTML Binding Ansatz. Außerdem muss man dafür ein ganz eigenes Userinterface und Service bauen. Ziel soll es aber sein so nah wie möglich an Webforms dran zu bleiben.

Allerdings und nun folgen weitere Haken und Ösen ist das mit dem RenderControl weit weniger trivial als ich das in Erinnerung hatte. Erstens muss man das Gridview in ein UserControl packen. Außerdem muss das Pagegerüst außen rum  liegen. Also erst Page bauen, Control laden, rendern und am ende ein wenig überflüssigen HTML wieder wegschnippeln.

   1:  <WebMethod()>
   2:Public Shared Function getTable(seite As Integer) As String
   3:Dim p As New basepage
   4:Dim Grid1 As myGrid = DirectCast(p.LoadControl("myGrid.ascx"), myGrid)
   5:        Grid1.pageNumber = seite
   6:        Grid1.DataBind()
   7:Dim sw As System.IO.StringWriter = New System.IO.StringWriter()
   8:Dim hw As System.Web.UI.HtmlTextWriter = New HtmlTextWriter(sw)
   9:        Grid1.RenderControl(hw)
  10:Dim xml As String = sw.ToString()
  11:Dim s1 = xml.IndexOf("<tr>")
  12:Dim s2 = xml.IndexOf("</table")
  13:Return xml.Substring(s1, s2 - s1)
  14:End Function

Wer genau hinsieht, wird bemerken das ich kein Page verwende, sondern basepage. Dies um den erzeugten HTML Code vorab schon zu minimieren, weil eigentlich wäre d auch noch FORM nötig.

   1:Public Class basepage
   2:    Inherits Page
   3: 
   4:    Public Overrides Sub VerifyRenderingInServerForm(ByVal control As Control)
   5:        Return
   6:    End Sub
   7:End Class

Geht auch mit Page aber muss dann im Nachgang mehr HTML Code beschnitten werden. Das erscheint mir unsinnig.

Um die PageMethod aufzurufen, verwende ich JQuery.  Wenn man die PageMethod mit einem ASP.NET Scriptmanager referenziert, geht's auch mit dem erzeugten Proxy bzw nur einer Zeile Code

PageMethods.getTable

Wie gesagt ich verwende Jquery, ein wenig Paging Logik am Client für den Seitenzähler und einen “ich will mehr” Hyperlink. Weil mein GridView ClientIDMode Static gesetzt ist, kann man sich auch auf die generierte ClientID 100%tig verlassen und dann einfach den erhaltenen <Table> Wust anhängen.

   1:<script>
   2: function loadmeins() {
   3: var p = "{seite:" + page + "}";
   4:                $.ajax({
   5:                    type: "POST",
   6:                    url: "WebForm19.aspx/getTable",
   7:                    contentType: "application/json; charset=utf-8",
   8:                    data: p,
   9:                    dataType: "json",
  10:                    success: function (result) {
  11: 
  12:                        $('#GridView1 tbody').append(result.d);
  13:                        page++;
  14:                    }
  15:                });
  16:            }
  17: var page = 1;
  18: 
  19:</script>
  20:<uc1:myGrid ID="myGrid1" runat="server" />
  21:<a href="#" onclick="loadmeins()">mehr</a>

Das User Control das als Layout Container für das Gridview dient brauch noch ein wenig, ganz wenig, Code Logik um das Paging abzubilden und den Header zu unterdrücken.

   1: Public Property pageNumber As Integer
   2:Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   3:        GridView1.HeaderRow.TableSection = TableRowSection.TableHeader
   4:End Sub
   5:Public Function loadData() As IQueryable(Of Customers)
   6:If pageNumber > 0 Then
   7:            GridView1.ShowHeader = False
   8:End If
   9:Dim nw As New NorthwindEntities1
  10:Dim query = nw.Customers.OrderBy(Function(c) c.Customer_ID).Skip(pageNumber * 5).Take(5)
  11:Return query.AsQueryable
  12:End Function

Am Ende sieht das genauso aus wie am Anfang geplant.

forwardpaging

Obwohl es mit relativ wenig Aufwand möglich war mit dem ASP.NET Gridview ein Vorwärts Blättern zu realisieren, fühlt es sich seltsam an. Auch das verpacken in JSon gefällt mir nicht. Darüberhinaus ist es für ein einfaches Szenario mit anderen Controls wie Repeater oder Listview einfacher eine saubere und passende HTML Tabelle zu rendern.

Wer Tipps hat was man besser machen kann, soll wie immer hier ein Kommentar hinterlassen.

Tags:

ASP.NET | JavaScript | VB

About the author

Something about the author

Month List

Page List