SharePoint 2013: „Mehr anzeigen“ Link auf Formularen entfernen

Und hier gleich noch ein Beitrag zum Thema Anpassung von Aufgabenformularen in SharePoint 2013. Hier ging es darum, die Verwandten Elemente auch auf dem EditForm anzuzeigen. Jetzt soll noch gezeigt werden, wie man den meiner Meinung nach völlig überflüssigen „Mehr anzeigen“ Link von den Aufgabenformularen entfernen und gleich alle Felder darstellen kann.

Der Link selbst ruft nur eine JavaScript-Funktion rlfiShowMore auf. Das machen wir uns zunutze, indem wir diese Funktion einfach selbst aufrufen, sobald die Seite geladen ist. Auf dem Formular muß dazu nur diese Zeile Script eingefügt werden:

_spBodyOnLoadFunctionNames.push(„rlfiShowMore“);

SharePoint 2013: Verwandte Elemente auf dem EditForm anzeigen

Bei SharePoint 2013 haben Workflowaufgaben standardmäßig eine Spalte Verwandte Elemente (Related Items). Leider ist diese Spalte ohne weiteres Zutun nur auf dem Formular zur Anzeige (DispForm) vorhanden und auch nur dort pflegbar.

SharePoint 2013 Workflows fügen beim Erstellen einer Aufgabe dort einen Link zum Element ein, auf dem Workflow läuft. Wenn man also einen Workflow hat, der z.B. Aufgaben erstellt, mit deren Hilfe etwas genehmigt werden soll, hat es durchaus Sinn diesen Link auch auf dem Änderungsformular (EditForm) darzustellen. Die genehmigende Person hat dann direkt auf dem Formular, mit dem sie arbeitet, auch einen Link zum Element, das genehmigt werden soll.

Das ist mal wieder ein typischer Fall für JavaScript. Das notwendige Script dazu kann man, wie fast immer, entweder in einem Script Editor Webpart oder per SharePoint Designer direkt im Code der Seite unterbringen.

SharePoint stellt zum Zugriff auf die Verwandten Elemente das Objekt SP.RelatedItemManager zur Verfügung. Es befindet sich in der Datei sp.js und kann erst verwendet werden, wenn diese geladen ist. Wir beginnen das Script also so:

ExecuteOrDelayUntilScriptLoaded(function() {
    // hier der Code
}, "sp.js");

Wir benötigen den ClientContext, den man auf die bekannte Art bekommt. Außerdem die ID der Aufgabenliste, die man aus dem _spPageContextInfo-Objekt bekommt. Und schließlich die ID der Aufgabe, die man aus der URL holen kann. Wir bedienen uns hierzu der altbekannten Methode getQueryStringParameter:

function getQueryStringParameter(paramToRetrieve) {
  if (document.URL.indexOf("?") > 0) {
    var params = document.URL.split("?")[1].split("&"),
        i,
        singleParam;
         
    for (i = 0; i < params.length; i++) {
      singleParam = params[i].split("=");
      if (singleParam[0] == paramToRetrieve)
        return singleParam[1];
    }
  }
  return "";
}

Mit diesen Werten liefert uns SP.RelatedItemManager direkt die Verwandten Elemente zur aktuellen Aufgabe:

var ctx = SP.ClientContext.get_current(),
    itemId = getQueryStringParameter("ID"),
    relItems = SP.RelatedItemManager.getRelatedItems(ctx, _spPageContextInfo.pageListId, itemId);

Jetzt braucht es nur ein executeQueryAsync und die Verwandten Elemente können benutzt werden. Es handelt sich dabei um ein Array, das Objekte enthält. Die Objekte haben eine get_url() Methode, die die URL des Elements liefert. Sie kann direkt z.B. in einen Link eingebaut werden. Und es gibt eine get_title() Methode, die den Anzeigenamen liefert, der z.B. als Text für den Link verwendet werden kann.

Hier nur ein kleines Beispiel, wie damit per jQuery ein Link in einem vorher bereitgestellten Container mit der ID myContainer erzeugt werden kann. Dazu nochmal das gesamte Script zum Kopieren und Einfügen:

ExecuteOrDelayUntilScriptLoaded(function() {
    var ctx = SP.ClientContext.get_current(),
        itemId = getQueryStringParameter("ID"),
        relItems = SP.RelatedItemManager.getRelatedItems(ctx, _spPageContextInfo.pageListId, itemId);
    ctx.executeQueryAsync(function() {
        var html;
        if (relItems.length > 0) {
            html = "<a href=’" + relItems[0].get_url() + "‘ target=’_top‘>" + relItems[0].get_title() + "</a>";
            $("#myContainer").append(html);
        }
        }, function() {}
    );

    function getQueryStringParameter(paramToRetrieve) {
        if (document.URL.indexOf("?") > 0) {
            var params = document.URL.split("?")[1].split("&"),
                i,
                singleParam;
         
            for (i = 0; i < params.length; i++) {
                singleParam = params[i].split("=");
                if (singleParam[0] == paramToRetrieve)
                    return singleParam[1];
            }
        }
        return "";
    }
}, "sp.js");

SharePoint 2013: Liste innerhalb der Websitesammlung anzeigen

Eine Frage, die immer wieder aufkommt, ist die, wie man die Daten einer Liste oder einer Bibliothek in einer anderen Website anzeigen kann. In einem früheren Beitrag habe ich gezeigt, wie man das in SharePoint 2010 machen kann. Leider funktioniert es in SharePoint 2013 mit der Methode nicht mehr. Die Vorgehensweise ist zwar ähnlich, aber auch die Werkzeuge haben sich verändert, so daß es jetzt einen eigenen Beitrag dazu gibt, wie man eine Listenansicht in SharePoint 2013 für die gesamte Websitesammlung zur Verfügung stellt.

Ansicht vorbereiten

Man kann die Ansicht zwar auch später noch problemlos über die Webparteinstellungen bearbeiten, auch dann, wenn sie in einer anderen Website eingefügt wurde, aber es empfiehlt sich trotzdem sie bereits an der Quelle so aufzubereiten, wie sie später an den meisten Stellen verwendet wird (oder verwendet werden soll).

Man kann dazu entweder eine vorhandene Listen- oder Bibliotheksansicht verändern, auf der Liste oder Bibliothek eine neue Ansicht erzeugen (und später wieder löschen) oder auch auf einer beliebigen Seite eine Ansicht einfügen und verändern. Auch in diesem Fall kann man sie später problemlos wieder von der Seite entfernen (oder die ganze Seite löschen).

Ansicht exportieren

Die oben erstellte Ansicht muß jetzt exportiert werden. Warum Microsoft den dafür vorgesehenen Menüpunkt aus dem Webpartmenü entfernt hat, kann ich nicht sagen, aber mit SharePoint Designer geht es zum Glück immer noch. Man öffnet also die Website in SharePoint Designer und navigiert zur Seite mit der gewünschten Ansicht. Wenn sie sich als Ansicht direkt auf der Liste oder Bibliothek befindet, kann man dazu über Listen und Bibliotheken und dann über die Ansichten einer Liste oder Bibliothek gehen. Wenn sie sich auf einer beliebigen Seite befindet, muß man diese öffnen. Je nach Speicherort der Seite kann man z.B. über Site Pages oder auch über Alle Dateien dorthin gelangen.

In SharePoint Designer 2013 gibt es keine Designansicht mehr, so daß man nach dem Öffnen der Seite direkt in die Quellcodeansicht gelangt. Dort sucht man sich die gewünschte Ansicht (im Idealfall befindet sich nichts anderes auf der Seite). Man findet Listenansichten z.B. indem man nach WebPartPages:XsltListViewWebPart sucht. Jetzt setzt man einfach die Einfügemarke irgendwo innerhalb dieses Elements und im Ribbon oben erscheint unter der Überschrift WEBPARTTOOLS der Reiter FORMAT. Darin gibt es rechts zwei Optionen zum Webpart speichern:

Direktes Speichern in den Websitekatalog hat keinen Sinn, weil es damit in anderen Websites nicht funktionieren wird. Man speichert das Webpart also in eine Datei, z.B. als ListeInWebsite1.webpart und merkt sich den Ort, an dem man die Datei abgelegt hat.

Gespeicherte Ansicht editieren

In der Datei muß jetzt eine Änderung gemacht werden. Es handelt sich dabei um eine XML-Datei, die man mit einem geeigneten Editor bearbeiten kann. Zur Not geht es auch mit Windows-Notepad. Man sucht in der Datei nach WebId und stellt fest, daß diese als leere Guid hinterlegt wurde: 00000000-0000-0000-0000-000000000000

Dort muß jetzt die richtige ID der Website eingetragen werden, in der sich die Liste oder Bibliothek tatsächlich befindet. Man findet diese ID z.B. über den REST-Service, indem man im Browser an die Adresse der Website folgendes anhängt: /_api/web/id

Als Adresse steht dann sowas im Browser http://sharepoint/sites/web2/_api/web/id und die Ausgabe sieht so aus:

Der markierte Teil ist die gesuchte Guid der Website. Man überschreibt damit die leere Guid in der Datei und speichert sie. Falls der Browser die Daten nicht wie oben gezeigt darstellt: hier steht, wie man das ändert.

Ansicht importieren

Die in der Datei gespeicherte Ansicht wird jetzt in den Webpartkatalog importiert. Dazu geht man zuerst in die Rootwebsite der Websitesammlung. Dort dann über das Menü hinter dem kleinen Zahnrad rechts oben in die Websiteeinstellungen. Unter Web-Designer-Kataloge findet man den Link Webparts. Dort lädt man die Datei hoch und kann dann noch verschiedene Metadaten angeben wie z.B. eine Beschreibung. Man kann auch die Gruppe angeben, unter das Webpart später beim Einfügen in eine Seite gefunden wird. Wenn man hier nichts angibt, erscheint es unter Verschiedenes.

Das war’s auch schon. Die Ansicht ist jetzt als wiederverwendbares Webpart im Webpartkatalog gespeichert und kann innerhalb der gesamten Websitesammlung verwendet werden.

SharePoint 2013 Workflows Teil 2: Listenelemente iterieren

Im ersten Teil habe ich gezeigt, wie man in einem SharePoint 2013 Workflow mit Hilfe der Aktion Call HTTP Web Service auf Listenelemente zugreifen kann. Dort ging es erstmal nur darum, die Anzahl verbundener Elemente zu ermitteln. In diesem Teil soll jetzt gezeigt werden, wie man mit den in 2013 neuen Mitteln eine Schleife zum Durchlaufen der gefundenen Elemente realisieren kann.

Testumgebung

Als Testumgebung werden die beiden Listen aus dem ersten Teil verwendet. Die Child List wurde um ein Zahlenfeld Value erweitert. Die Parent List bekam ein zusätzliches Feld Item Sum. Per Workflow sollen jetzt die Werte aus Value aller verbundenen Child-Elemente aufsummiert und in Item Sum des Parent-Elementes geschrieben werden.

Es geht hier nur um das Prinzip und natürlich sind auch viele andere Aktionen denkbar. Man könnte innerhalb der Schleife z.B. auch alle verbundenen Child-Elemente aktualisieren.

Schleifenaufbau

Wir erweitern also unseren bestehenden Workflow um eine neue Stufe. Damit wir die Schleife realisieren können, benötigen wir eine Schleifenvariable. Wir legen also eine Integer-Variable LoopCounter an und initialisieren sie mit 0 (Null). Außerdem benötigen wir eine Variable, in der wir die Werte der einzelnen Child-Elemente aufsummieren können. Wir legen also noch eine Number-Variable ItemSum an und initialisieren sie ebenfalls mit 0. Jetzt fügen wir eine Schleife vom Typ Schleife n-mal ein und konfigurieren sie so, daß sie so oft ausgeführt wird, wie unsere Variable ItemCount aus dem ersten Teil vorgibt. Die Variable enthält die Anzahl der gefundenen Elemente. Bis jetzt siehst es also so aus:

Zugriff auf ein Element

Als nächstes holen wir uns die ID eines Listenelementes aus den Ergebnissen der REST-Abfrage. Dazu verwenden wir wieder die Aktion Get an Item from a Dictionary. Für den Parameter item by name or path verwenden wir d/results(<Counter>)/ID, wobei der Platzhalter <Counter> für die nullbasierte Laufvariable steht, in unserem Fall also die Variable LoopCounter. Im Ganzen sieht das so aus:

Wie haben jetzt eine Variable ChildItemID, die pro Schleifendurchlauf immer die Element-ID eines Child-Elementes enthält und wir können damit auf das jeweilige Element zugreifen. Wir verwenden im Beispiel die Aktion Do Calculation, um den Wert des Feldes Value zu unserer Variablen ItemSum zu addieren. Der Zugriff auf das das Feld des Child-Elementes erfolgt dabei so:

Schleifenabschluß

Damit die Schleife auch korrekt funktioniert, müssen wir jetzt noch unsere Laufvariable LoopCounter hochzählen. Dazu verwenden wir wieder die Aktion Do Calculation und addieren einfach 1 (eins). Dabei ist zu beachten, daß Do Calculation immer einen Gleitkommawert zurückliefert, den wir nicht wieder direkt unserer Integer-Variablen zuweisen können. Wir verwenden hier also eine Zwischenvariable vom Typ Number und eine weitere Aktion Set Workflow Variable um diese unserer Laufvariablen zuzuweisen. Hier funktioniert die Konvertierung dann.

Ich habe versucht, als Laufvariable selbst eine Number-Variable zu verwenden, aber das hat bei meinen Versuchen nicht zuverlässig funktioniert. Deshalb der kleine Workaround.

Zum Abschluß benutzen wir noch eine Aktion Set Field in Current Item (nach der Schleife!) und schreiben damit die ermittelte Summe in das Feld Item Sum des aktuellen Elements. Zur Erinnerung: der Workflow läuft auf der Parent List und das aktuelle Element im Workflow ist daher ein Element dieser Liste.

Hier nochmal die gesamte Schleife in der Übersicht:

SharePoint 2013 Workflows Teil 1: Call HTTP Web Service

Nachdem ich mich jetzt endlich etwas mit den neuen SharePoint 2013 Workflows beschäftigen konnte, möchte ich hier meine Erfahrungen festhalten. Auch damit ich selbst etwas zum Nachschlagen habe 😉

Im ersten Teil soll es darum gehen, wie man mit der neuen Aktion Call HTTP Web Service Daten aus SharePoint abfragen kann. Selbstverständlich kann man damit jeden beliebigen Webservice abfragen, aber mir ging es ganz speziell um Daten aus SharePoint. Prinzipiell kommt man damit an die Daten aus jeder beliebigen SharePoint-Website, auch wenn sie sich in einer anderen Farm befinden. Voraussetzung dafür ist, daß der Benutzer dazu berechtigt ist (und natürlich daß der Webservice erreichbar ist).

Testumgebung

Für den Test habe ich ein einfaches Szenario aufgebaut: innerhalb einer Website gibt es eine Parent List und eine Child List, die über ein Nachschlagefeld mit der Parent List verbunden ist. Die Parent List hat ein zusätzliches Zahlenfeld Child Count. Ein Workflow auf der Parent List soll jetzt alle zugehörigen Elemente der Child List finden und deren Anzahl beim Parent in Child Count eintragen. Zur Verdeutlichung hier die Parent List:

Und die Child List:

Workflow erstellen

Wir öffnen jetzt die Website in SharePoint Designer 2013 und erstellen einen neuen Listenworkflow für die Parent List. Die Vorgehensweise dazu ist dieselbe wie in 2010, aber beim Anlegen muß darauf geachtet werden, daß SharePoint 2013 Workflow ausgewählt ist. Nur dann stehen die neuen Funktionen auch zur Verfügung:

Service-URL

Im Workflow brauchen wir zunächst die Adresse des Services, den wir für die Abfrage der verknüpften Elemente verwenden wollen. Wir nehmen dafür den in SharePoint 2013 neuen REST-Service, der unter der Adresse <Website>/_api zur Verfügung steht. Der Platzhalter <Website> steht hierbei für die absolute URL der Website, also ungefähr http://sharepoint.firma.tld/Sites/Site1. Wenn es sich wie in unserem Beispiel um die aktuelle Website handelt, kann man dafür die vordefinierte Variable Workflowkontext: Aktuelle Website-URL verwenden.

Da wir hier die Daten einer ganz bestimmten Liste möchten, hängen wir an die URL noch folgendes an: /_api/lists/GetByTitle(‚Child List‘)/Items. Da wir uns im ersten Schritt nur für die ID und den Titel der verknüpften Elemente interessieren, teilen wir der API das mit: ?$select=Id,Title.

Das wichtigste ist aber der Filter, weil wir nur die Elemente haben möchten, die zu unserem aktuellen Element der Parent List gehören. Prinzipiell geht das so: $filter=<LookupField> eq <ID>. <LookupField> ist der interne Name des Nachschlagefelds, <ID> ist die ID des aktuellen Parent-Elements und eq steht für equals also zu deutsch ist gleich. Achtung: damit der Filter funktioniert, muß man das Feld auch beim $select-Parameter angeben und dort mit dem Suffix Id (also ParentId). Zum Ausprobieren wie die Abfrage genau aussehen muß und wie die Felder heißen, empfehle ich einen Test im Browser. Wie das geht, wird weiter unten unter Exkurs erklärt.

Hier nochmal zusammengefaßt die gesamte aufzurufende URL, die wir einer Variablen REST-URL zuweisen. Da man den Text wegen der Nachschlageinformationen ohnehin nicht kopieren kann hier nur als Screenshot:

Request Header

Damit der REST-Service die Daten in einem Format liefert, das vom Workflow verarbeitet werden kann, müssen für die Abfrage sogenannte Request Header definiert werden. Die Header Accept und Content-Type müssen beide auf den Wert application/json;odata=verbose gesetzt werden. Im Workflow geschieht das durch den ebenfalls neuen Variablentyp Dictionary.

Wir legen also eine neue Dictionary-Variable RequestHeaders an und verwenden die Aktion Build Dictionary um die beiden Header einzutragen:

Exkurs: SharePoint 2013 REST API

Man kann den REST-Service zum Ausprobieren auch jederzeit einfach im Browser aufrufen. Der Service liefert XML zurück, das der Internet Explorer als Feed interpretiert. Wie man ihn dazu bringen kann die rohen XML-Daten anzuzeigen, habe ich hier beschrieben.

Wenn man sich die vom Service gelieferten Json-Daten im Browser anschauen möchte, wird es schon schwieriger, weil man wie oben für den Workflow beschrieben, die an den Server gesendeten Header modifizieren muß. Beim Internet Explorer habe ich keinen Weg gefunden das zu erreichen. Ich habe dann einige Plugins für Firefox getestet, aber leider auch ohne Erfolg. Fündig geworden bin ich dann mit Google Chrome und dem Plugin Change HTTP Request Header. Wenn man damit die Header wie oben gezeigt modifiziert, kann man sich das Ergebnis einer REST-Abfrage auch im Json-Format anzeigen lassen. Wer es etwas schöner formatiert haben möchte, kopiert es in den Json Parser Online.

Ein guter Einstiegspunkt in die offizielle Dokumentation von Microsoft findet sich hier: Get startet with the SharePoint 2013 REST service.

Call http Web Service

Jetzt aber weiter im Workflow. Nachdem wir jetzt die URL des Services und die notwendigen Header haben, fügen wir eine Call HTTP Web Service Aktion ein. Für den ersten Parameter verwenden wir einfach unsere vorher festgelegte Variable und belassen die HTTP-Methode bei GET:

Von den restlichen Parametern der Aktion setzen wir nur den ResponseContent (das Ergebnis der Abfrage) auf eine gleichnamige Dictionary-Variable und den ResponseCode auf eine gleichnamige String-Variable. Der Response Code gibt Auskunft über den Erfolg der Abfrage und kann zur weiteren Steuerung des Workflows verwendet werden. Er lautet OK, wenn die Abfrage syntaktisch in Ordnung war. Natürlich kann die Abfrage trotzdem logische Fehler enthalten, so daß z.B. der Filter nicht wie erwartet funktioniert, aber das muß separat festgestellt werden. Alle anderen Parameter interessieren uns hier nicht.

Achtung: der aufmerksame Leser fragt sich jetzt wahrscheinlich was mit unserer Variablen für die Request Header passiert. Diese muß der Workflowaktion auf anderem Wege zugewiesen werden und zwar über das Eigenschaften-Fenster, das man bei jeder Workflowaktion über ihr Menü erreichen kann. Das Menü findet man, wenn man mit der Maus über die Aktion fährt und dann auf den DropDown-Pfeil ganz rechts klickt. Innerhalb des Fensters kann man jetzt auch die Variable für die Request Header setzen (3. Punkt von oben):

Warum das an dieser Stelle so umständlich zu handhaben ist, kann ich nicht sagen, halte es aber für einen Bug. Oder zumindest für ein Versäumnis.

Ergebnisverarbeitung

Das Ergebnis der Abfrage haben wir jetzt in einer Dictionary-Variablen ResponseContent. Hier soll nur gezeigt werden, wie man die Anzahl der gefundenen Elemente ermitteln und in ein Feld des Parentelements schreiben kann. Zunächst holen wir uns ein Subset des Ergebnisses in eine weitere Dictionary-Variable ResultItems. Dazu verwenden wir die Aktion Get an Item from a Dictionary und geben für den Parameter item by name or path den Wert d/results an. Mit der Aktion Count Items in a Dictionary bekommen wir jetzt die Anzahl der gefundenen Elemente in eine Integer-Variable ItemCount. Mit der Aktion Set Field in Current Item lassen wir uns diesen Wert jetzt noch ins aktuelle Parentelement ausgeben.

Hier nochmal der gesamte Workflow zusammengefaßt:

Im zweiten Teil wird gezeigt, was man mit dem Ergebnis der Abfrage noch anstellen kann. Dort geht es dann um eine Schleife, die alle gefundenen Elemente durchläuft.

SharePoint Designer 2013 stürzt beim Website öffnen ab

Das Problem: SharePoint Designer 2013 stürzt beim Klick auf Website öffnen ab. Dieses Problem hatte ich bereits vor längerer Zeit und konnte es nach einer relativ kurzen Suche im Web auch beseitigen. Jetzt trat es hier erneut auf und ich mußte wieder nach der Lösung suchen. Deshalb dieser Beitrag in der Hoffnung beim nächsten Mal schneller zur Lösung zu gelangen.

Der Fehler tritt nur auf, wenn auf einer Maschine mehrere Versionen von SharePoint Designer installiert sind. Da ich leider immer noch alle möglichen Versionen supporten muß, habe ich hier also SharePoint Designer 2007, 2010 und 2013, was normalerweise auch problemlos funktioniert. Die kürzliche Installation von SP2 für SharePoint Designer 2010 hat dann diesen Fehler wieder zutage gefördert. Die Lösung hatte ich hier bei Marc D Anderson gefunden, aber es gibt inzwischen viele Beiträge dazu.

Die Lösung besteht darin, zwei Werte aus der Windows Registry zu löschen. Es handelt sich dabei um

HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Common\Open Find\Microsoft SharePoint Designer\Settings\Website öffnen\ClientGUID

und

HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\Open Find\Microsoft SharePoint Designer\Settings\Website öffnen\ClientGUID

Einfach die beiden Werte löschen (Rechtsklick – Löschen), SharePoint Designer neu starten und alles funktioniert wieder 🙂

Dynamische Titel bei benutzerdefinierten Formularen

Wenn man mit SharePoint Designer benutzerdefinierte Formulare anlegt, wird für den Titel, also für den Namen der Spalten, immer der aktuelle Wert fest eingetragen. Für den eigentlichen Feldwert, also für den Teil mit dem die Benutzer später interagieren (z.B. eine Textbox oder Checkbox) wird ein FieldControl eingefügt. Ebenso für den Beschreibungstext einer Spalte. Wenn man dann später etwas ändert, indem man z.B. bei einem Auswahlfeld von DropDown auf Radiobuttons umschaltet, dann sind diese Änderungen auch sofort in den benutzerdefinierten Formularen zu sehen. Wenn man aber den Spaltennamen ändert, wird im benutzerdefinierten Formular immer noch der alte Name angezeigt. Außerdem wird dieser Name immer nur in einer Sprache angezeigt und paßt sich nicht an, wenn ein Benutzer eine andere Sprache auswählt.

Um dieses Problem zu lösen, kann man in der Codeansicht von SharePoint Designer den fest eingefügten Text durch ein in SharePoint immer vorhandenes Control ersetzen:

<SharePoint:FieldLabel ControlMode="Display" runat="server" FieldName="Title" />

Für das FieldName-Attribut gibt man wie immer den internen Namen der Spalte an. Das ControlMode-Attribut muß angegeben werden, aber es macht keinen Unterschied, ob man Display, Edit oder New einträgt.

Das war’s auch schon. Es ist mir (mal wieder) ein Rätsel, warum SharePoint Designer das nicht gleich so macht.