• home
    • news & events
    • blog
  • über uns
    • projekte und referenzen
    • partner
    • produkte & technologien
    • offene jobs / stellen
    • veröffentlichungen
  • dienstleistungen & services
    • software design & architektur
    • software entwicklung
    • beratung / consulting
    • training, kurse und workshops
  • angebote
    • quick-starts
    • trainings und kurse
    • modulare sharepoint 2010 workshops
  • kontakt
Wir bieten SharePoint und .NET
Kompetenz, Erfahrung und Know-How:
"1stQuad guaranteed."
Diesen Blog abonnieren
Subscribe in NewsGator Online Add to My AOL
Add to Google Reader or Homepage Add to netvibes

Aktuelle Posts

Quick-Tipp: Publishing Site Settings
Update: Dynamisches Wiki Inhaltsverzeichnis
Chart Part für SharePoint 2010
SharePoint Content DB Migration -> Access denied
Konfigurieren von „Gefällt mir“ und Kategorien und Notizen

Archiv

Januar 2012 (4)
Dezember 2011 (2)
November 2011 (10)
September 2011 (3)
August 2011 (7)
Juli 2011 (1)
Juni 2011 (3)
Mai 2011 (6)
April 2011 (5)
März 2011 (8)
Februar 2011 (8)
Januar 2011 (4)
Dezember 2010 (5)
November 2010 (7)
September 2010 (6)
August 2010 (2)
Juli 2010 (11)
Juni 2010 (13)
Mai 2010 (11)
April 2010 (4)
März 2010 (6)
Februar 2010 (2)
Januar 2010 (6)
Dezember 2009 (4)
November 2009 (13)
Oktober 2009 (17)
September 2009 (2)
Juli 2009 (2)
März 2009 (2)
Januar 2009 (1)

1stQuad ist Microsoft Certified Gold Partner und bietet SharePoint und .NET Produkt- und Projekt-Kompetenz, -Erfahrung und -Know-How für Entwicklung, Architektur, Beratung, Schulung, Training und Kurse in Schweiz sowie Deutschland und Östereich.
1stQuad ist MatchPoint Partner und bietet MatchPoint Produkt- und Projekt-Kompetenz, -Erfahrung und -Know-How für Entwicklung, Architektur, Beratung, Schulung, Training und Kurse in Schweiz sowie Deutschland und Östereich.
1stQuad ist Nintex Partner und bietet Nintext SharePoint Workflows Produkt- und Projekt-Kompetenz, -Erfahrung und -Know-How für Entwicklung, Architektur, Beratung, Schulung, Training und Kurse in Schweiz sowie Deutschland und Östereich.
1stQuad ist Balesio Gold Partner und bietet SharePoint FILEMinimizer Produkt- und Projekt-Kompetenz, -Erfahrung und -Know-How für Entwicklung, Architektur, Beratung, Schulung, Training und Kurse in Schweiz sowie Deutschland und Östereich.
1stQuad Solutions ist Kentico Certified Solution Partner und bietet Produkt- und Projekt-Kompetenz, -Erfahrung und -Know-How für Entwicklung, Architektur, Beratung, Schulung, Training und Kurse in Schweiz sowie Deutschland und Östereich.
© 2011 1stQuad Solutions
Alle Rechte vorbehalten
> Impressum
Wir bieten Microsoft SharePoint und .NET Projekt- und Produkt-Know-how, Kompetenz und Erfahrung für Entwicklung, Architektur, Beratung, Schulung, Training und Kurse in Zürich, Bern, Basel, Schweiz sowie Deutschland und Östereich.

Blog > Juli 2010

State Machine Workflow mit InfoPath Formularen für SharePoint 2010 – Teil 7

Im siebten Teil der Workflow Serie schreiben wir den Code für unseren Workflow.

Veröffentlicht am 31.07.2010 22:41:04 von Reiner Ganser mit 3 Kommentar(en)

Teil 7: Workflow mit Code erweitern


 Diesen Blog-Post als PDF herunterladen

   Sourcecode herunterladen
 
Nachdem wir im letzten Teil den Design des Workflows fertig gestellt haben, können wir jetzt die Activities mit Code hinterlegen und auch die Verzweigungen in unseren Zuständen mit Logik füllen.
Wir müssen an den folgenden Stellen etwas tun:
  • Nach dem Start des Workflows müssen wir die Start-Parameter auslesen für den Genehmiger und die Anweisungen für diesen
  • Beim Anlegen einer Aufgabe für den Genehmiger, muss die TaskID gesetzt und die Aufgabe samt Beschreibung an den Genehmiger zugeweisen werden.
  • Wir müssen auf die Bearbeitung der Aufgabe reagieren und anhand dessen was der Benutzer gemacht hat, entsprechend reagieren.
  • Das gleiche brauchen wir für die Aufgabenbehandlung des Zustandes stateReviewer

Benötigte Variablen anlegen und Startparameter auslesen

Beginnen wir mit dem Code nach dem Start des Workflows: Doppelklick auf eventDrivenActivity1 und wir landen in deren Innereien:

 
Wir klicken auf die Activity onWorkflowActivated1, um diese auszuwählen. In den Eigenschaften dieser Activity existiert die Eigenschaft Invoked, welche momentan noch leer ist. Man kann hier nun eine Methode eintragen und beim Drücken der Eingabetaste, wird der Code angelegt und man landet auch direkt in diesem. Wir verwenden die Methode OnWorkflowActivated:

 
Im Code angelangt, brauchen wir noch ein paar variablen, die wir im Workflow benutzen wollen und definieren diese zuerst:

#region Variables

private string _reviewerID = "";      // Login Name des Reviewers
private string _approverID = "";      // Login Name des Genehmigers   
private string _instructions = "";    // Anweisungen für den Genehmiger
private string _status = "";          // Enthält den Status der jeweiligen Aufgabe
private string _comment = "";         // Kommentare, die im Aufgaben Formular angegeben werden können

#endregion

Innerhalb der Methode OnWorkflowActivated() können wir jetzt die Daten, die der Benutzer im Startformular eingegeben hat abholen, auspacken und den Variablen zuweisen:

private void OnWorkflowActivated(object sender, ExternalDataEventArgs e)
{
    try
    {
        XmlDocument retData = new XmlDocument();
        retData.LoadXml(workflowProperties.InitiationData);
        XmlNode approverNode = retData.SelectSingleNode("//approver");
        if (null != approverNode)
        {
            _approverID = approverNode.InnerText;
        }
        XmlNode instructionNode = retData.SelectSingleNode("//instructions");
        if (null != instructionNode)
        {
            _instructions = instructionNode.InnerText;
        }
    }
    catch (Exception ex) { }
}

Aufgabe inialisieren mkit TaskId, Titel, Zuweisung an Person und Anweisung eintragen

Als nächstes gehen wir zurück in den Workflow Designer und dort zurück auf die Workflow Übersicht. Im Zustand stateApprover können wir einen Doppelklick auf die Activity stateInit_Approver machen. Für die enthaltene Activity createTask_Approver brauchen wir nun ebenfalls wieder einen Event Handler, der es uns ermöglicht, die Aufgabe für den Genehmiger zu initialisieren. Die Methode, die aufgerufen werden soll nenne ich OnTaskCreate_Approver:

 
Innerhalb der angelegten Methode initialisieren wird die Aufgabe für den Genehmiger mit einer neuen TaskId (GUID), sowie den sonstigen Parametern für die Aufgabe (an wen zuweisen, Titel, Beschreibung). Zusätzlich geben wir hier an, welches Formular verwendet werden soll:
 
private void onTaskCreate_Approver(object sender, EventArgs e)
{
    try
    {
        TaskId_Approver = Guid.NewGuid();
        TaskProperties_Approver.Title = "Bitte Dokument genehmigen.";
        TaskProperties_Approver.AssignedTo = _approverID;
        TaskProperties_Approver.Description = _instructions;
        TaskProperties_Approver.TaskType = 0; // Nr. Des Aufgaben Formulars
        TaskProperties_Approver.ExtendedProperties["instructions"] = _instructions;
    }
    catch (Exception ex)
    {
    }
}

In obigem Code findet man die Anweisung

TaskProperties_Approver.TaskType = 0;

Diese weist das Formular mit der ID 0 unserer Aufgabe für den Genehmiger zu. Im vorigen Post haben wir ja in der Datei elements.xml die ID der InfoPath Formulare in den Elementen <Task0_FormURN> und <Task1_FormURN> eingetragen. Die obige Codezeile bedeutet in unserem Falkl nichts anderes als dass das Formular im Element <Task0_FormURN> verwendet wird. In gleicher Weise werden wir später für den Reviewer die ID 1 verwenden und somit auf die InfoPath Formular ID verweisen, die im Element <Task1_FormURN> hinterlegt ist.

In obigem Code ist zusätzlich die Anweisung
 
TaskProperties_Approver.ExtendedProperties["instructions"] = _instructions;

zu finden. Diese übergibt die Anweisungen (_instructions) an das Feld instructions im InfoPath Formular. Wir haben ja dafür anhand der Datei ItemMetadata.XML eine 2. Datenquelle erstellt, die die Daten lesen kann und ins entsprechende Feld des Formulars schreibt.

Auf Änderungen in der Aufgabe reagieren

Damit haben ist die Aufgabe für den Gewnehmiger angelegt. Jetzt müssen wir den Code implementieren, der die Aktionen im Aufgabenformular auswertet, also ob der Genehmiger genehmigt oder abgelehnt hat.

Dazu müssen wir diese erst einmal abholen. Wir gehen zuerst wieder im Designer auf die Übersicht und machen einen Doppelklick auf die Activity eventDrivenActivity_Approver im Zustand stateApprover. Die Activity TaskChanged_Approver braucht zunächst wieder eine Methode, die bei Veränderungen an der Aufgabe aufgerufen werden. In unserem Fall ist dies die Methode OnTaskChanged_Approver:

 
Innerhalb dieser Methode holen wir uns die Daten, welche der Benutzer im Aufgabenformular eingegeben hat ab und füllen unsere lokalen Variablen damit, damit die Daten nicht verloren gehen:

private void onTaskChanged_Approver(object sender, ExternalDataEventArgs e)
{
    try
    {
        _status = AfterProperties_Approver.ExtendedProperties["status"].ToString();
        _comment = AfterProperties_Approver.ExtendedProperties["comment"].ToString();
    }
    catch (Exception ex) { }
}

Die Daten aus dem Aufgabenformular werden anhand der AfterProperties zurückgeliefert und sind über den Indexer ExtendedProperties zugänglich. Der Zugriff erfolgt sehr einfach über den Namen des Feldes im Aufgabenformular.
Damit haben wir die Daten aus dem Aufgabenformular des Genehmigers abgeholt. Der nächste Schritt besteht nun darin, anhand der vom Benutzer eingegebenen Informationen im Workflow weiter zu verzweigen. Im Workflow Designer sehen wir, dass bei der if-else Verzweigung immer noch immer das rote Ausrufezeichen angezeigt wird. Beim Herunterklappen der Meldung sieht man, dass die Bedingung noch nicht definiert ist:

 
Wenn wir auf die Fehlermeldung klicken landen wir in der Eigenschaft Condition der if-else Verzweigung (ifElseBranchActivity1). Beim Aufklappen dieser Eigenschaft sehen wir, dass wir entweder Regel hinterlegen können oder dass wir eine Methode implementieren können, die für die Verzweigung benutzt wird. In unserem Fall wählen wir Code Condition aus. Nun müssen wir noch die Methode angeben, die verwendet werden soll. In unserem Fall geben wir den Methodennamen IsApproved an. Beim Drücken der Eingabetaste landet man wieder im Code und im Designer sieht unsere Condition Eigenschaft also jetzt wie folgt aus: 

 
Innerhalb der Methode IsApproved fragen wir den Status ab, welcher vom InfoPath Aufgabenformular geliefert wurde. Dabei bedeutet „ok“, dass der Genehmiger genehmigt hat und „nok“, dass er zurückgewiesen hat. Unser Code bedarf deshalb keiner Raketentechnik und sieht wie folgt aus:

private void IsApproved(object sender, ConditionalEventArgs e)
{
    e.Result = false;
    if ("ok" == _status)
    {
        e.Result = true;
    }
}

Die Eigenschaft Result aus den Ereignis Argumenten (ConditionalEventArgs) wird dabei von der if-else Verzweigung genutzt, um zu entscheiden, ob die Abfrage wahr oder falsch ist. Deshalb setzen wir e.Result auf true, wenn der Status „ok“ ist (Genehmiger hat genehmigt oder auf false, wenn der Genehmiger zurückgewiesen hat.

Damit haben wir alles für die Aufgabe des Genehmigers erledigt. Das gleiche Spielchen wie oben beschrieben, müssen wir nun noch für den Zustand stateReviewer durchführen. Der Code für den gesamten Reviewer Zustand ist dabei wie folgt:

#region Reviewer Task

public Guid TaskId_Reviewer = default(System.Guid);
public SPWorkflowTaskProperties TaskProperties_Reviewer = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
public SPWorkflowTaskProperties AfterProperties_Reviewer = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
public SPWorkflowTaskProperties BeforeProperties_Reviewer = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();

private void OnTaskCreate_Reviewer(object sender, EventArgs e)
{
    TaskId_Reviewer = Guid.NewGuid();
    TaskProperties_Reviewer.Title = "Bitte passen Sie das Dokument nochmal an.";
    TaskProperties_Reviewer.AssignedTo = workflowProperties.Originator;
    TaskProperties_Reviewer.Description = _instructions;
    TaskProperties_Reviewer.TaskType = 1; //Aufgaben Formular 2
    TaskProperties_Reviewer.ExtendedProperties["comment"] = _comment;
}

private void OnTaskChanged_Reviewer(object sender, ExternalDataEventArgs e)
{
    try
    {
        _status = AfterProperties_Reviewer.ExtendedProperties["status"].ToString();
        _instructions = AfterProperties_Reviewer.ExtendedProperties["instructions"].ToString();
    }
    catch (Exception ex) { }
}

private void SendToApprover(object sender, ConditionalEventArgs e)
{
    e.Result = false;
    if ("approve" == _status)
    {
        e.Result = true;
    }
}

#endregion

Wie man in obigem Code sehen kann, wird eine neue Aufgabe an den Originator zugewiesen, also demjenigen, der den Workflow gestartet hat. Die bewirkt die folgende Zeile:

TaskProperties_Reviewer.AssignedTo = workflowProperties.Originator;

An diesem Beispiel kann man sehr schön erkennen, dass man auf etliche Parameter des Workflows Zugriff hat.
Zum Abschluss dieses Teils noch ein kleiner Tipp: Der Workflow Designer in Visual Studio erzeugt einiges an Code. Dummerweise jedoch mehr oder weniger wahllos, je nachdem wie man die Eigenschaften der Activites füllt. Ich ordne diesen Code manuell um und verwende Regionen (#region), um einen besseren Überblick zu behalten. Ich bündele beispielsweise in einem State Machine Workflows alles, was zu einem Zustand gehört in einer #region. Mein Code für den Workflow sieht also nach der Umorganisation momentan wie folgt aus:

 
Im herunter ladbaren Code Beispiel kann man das noch besser nachvollziehen.
Damit haben alles beisammen, um den Workflow ablaufen zu lassen. Der Test des Workflows ist Bestandteil des nächsten Teils der Serie.
 

Kommentar
Lukas
Vielen Danke für diese Weltklasse-Beiträge. Sehr gut gemacht - super.

Hab das Beispiel durchgeklickt und meine, dass die primäre Datenquelle in den Infopath-Formularen unnötig ist. Die Kommunikation zu Sharepoint läuft doch nur über diese sekundäre Datenquelle mit diesen ows_xxx Feldern. D.h. man könnte diese ows_xxx Felder grade direkt an die Eingabefelder knüpfen. Das status-Feld, das beim Betätigen der Buttons gesetzt wird, sollte eigentlich das ows_status Feld der sekundären Quelle sein (fehlt im Moment noch in der Datasource).
Aber kann ja natürlich sein, dass du hier einen anderen Weg zeigen möchtest.

Danke dir.
Gruss, Lukas
02.08.2010 13:44:01

Reiner Ganser
Hi Lukas,
freut mich, dass Dir die Serie gefallen hat und Du sie gewinnbringend nutzen konntest. Es fehlen ja 1-2 Teile zum Test und Troubleshooting, je nachdem wie ich das aufteile.
Zu Deiner Frage: Prinzipiell eine absolut coole Idee, nur funktioniert sie aus meiner Sicht nicht, da die sekundäre Datenquelle nur lesend ist. Du kannst also keine Werte an den Workflow über diesen Weg zurückliefern. D.h. die primäre Datenquelle ist notwendig, damit die ExtendedProperties bzw. Before- und AfterProperties auf die Werte in den Feldern zugreifen können. So funktioniert auch der Zugriff auf den Status.

Gruss
Reiner
03.08.2010 00:25:09

Lukas Jung
Hi Reiner
Ich hab das ausprobiert - ich konnte den Status-Update in die sekundäre Datenquelle schreiben und hatte den Status so automatisch in den ExtendedProperties zur Verfügung.
Ich hab dein Beispiel so abgeändert, dass keine Datenfelder mehr in der primären Datasource mehr vorhanden sind - der WOrkflow funktioniert so bei mir.
Wie auch immer, ich freu mich auf deine abschliessenden Beiträge zum Thema.
Gruss, Lukas
03.08.2010 10:13:59

Kommentar hinterlassen



 Security code
Zurück, Seite drucken