MSDN Magazin > Home > Ausgaben > 2008 > Juni >  Ausfüllen von Formularen: Erstellen von Workflo...
Ausfüllen von Formularen
Erstellen von Workflows für das Erfassen von Daten und Erstellen von Dokumenten
Rick Spiewak

Themen in diesem Artikel:
  • Erstellen benutzerdefinierter Aktivitäten
  • Zusammenarbeit mit Microsoft Office
  • Übergabe von Daten an Workflowaktivitäten
  • Extrahieren von Workflowdaten aus Office-Dokumenten
In diesem Artikel werden die folgenden Technologien verwendet:
Windows Workflow Foundation, 2007 Office System, Visual Basic
Ein Workflow beschreibt eine Methode für die Automatisierung von Geschäftsprozessen. Ein Workflow dient beispielsweise zum Verarbeiten von Kundenanfragen und Versicherungsansprüchen oder zum Einlösen von Anteilen an einem Investmentfonds. Ein Workflow beginnt möglicherweise, wenn ein Dokument über E-Mail oder eine Anfrage von einer Website empfangen wird oder ein Kunde in einem Supportcenter anruft. Vordefinierte Aufgaben lassen sich dann zuweisen und verarbeiten, entweder automatisch oder durch den Benutzer.
In der Vergangenheit stützten sich die Versuche, diese Art an Automatisierung bereitzustellen, im Allgemeinen auf monolithische Workflowsysteme, um die herum der Geschäftsprozess angelegt ist. Windows® Workflow Foundation (WF) ist ein komponentenbasierter Ansatz, mit dem sich ein Workflow erzeugen lässt, der nicht um einen Geschäftsprozess herum angelegt ist, sondern ihn unterstützt.
Wenn für einen Geschäftsprozess ein Workflow erforderlich ist, müssen häufig prozessbezogene Dokumente genutzt oder erstellt werden. Diese Anforderung tritt zum Beispiel dann auf, wenn ein Antrag (etwa für ein Darlehen oder für das Einlösen von Anteilen) während des Workflowprozesses genehmigt oder abgelehnt werden muss. Dies kann nach einer Prüfung durch ein Programm (automatisch) oder einen Antragsprüfer (manuell) stattfinden. Vielleicht muss ein Schreiben aufgesetzt oder eine Tabelle zum Kontostand erstellt werden.
In diesem Artikel werden Techniken für die Integration von Microsoft® Office-Anwendungen mit WF aufgezeigt. Es wird erläutert, wie sich InfoPath®-Formulare und andere Office-Dokumente für das Sammeln von Daten verwenden lassen, wie sich Daten an die entsprechenden Aktivitäten übergeben lassen und wie sich diese Datenfelder für Entscheidungen sowie für das Erstellen oder Ausfüllen neuer Office-Dokumente verwenden lassen.
Für das Testen mehrerer dieser WF- und Office-Integrationsszenarios wurde Visual Studio® 2008 verwendet, einschließlich der Auflistung benannter Felder, Extrahieren des Inhalts und Ausfüllen von Dokumenten anhand von Vorlagen. Dabei wurden benutzerdefinierte Workflowaktivitäten geschrieben, um die Dateneingabe, das Ausfüllen verschiedener Dokumenttypen und die Anzeige fertiger Dokumente zu demonstrieren. Weiterhin wurde für diese Aktivitäten eine Designunterstützung hinzugefügt, damit sich ihr Erscheinungsbild von anderen WF-Aktivitäten unterscheidet.

Gesamtentwurf
Jeder Entwurf für die Integration von Workflow und Office besteht aus drei grundlegenden Komponenten: Eingabe der Daten in den Workflow, Verwenden der Daten für das Erstellen oder Aktualisieren von Office-Dokumenten und Speichern oder Zurückgeben der Ausgabedokumente. Zur Unterstützung dieser drei Anforderungen wurde ein Satz an Schnittstellen erstellt, mit denen sich eine Reihe einzelner Workflowaufgaben über den zugrunde liegenden Office-Dokumenten und Anwendungen unterstützen lassen.
Die erste dieser Aufgaben besteht darin, die Namen der Datenfelder in den Office-Dokumenten abzurufen. Obwohl das generische Begriffsfeld für eine Beschreibung verwendet wurde, handelt es sich im Zusammenhang mit Microsoft Word um Lesezeichen. Microsoft Excel® verwendet benannte Bereiche, InfoPath-Felder werden als XML-Knoten angezeigt, und PowerPoint® verwendet Namen für Formen. (Vor dem 2007 Office System gab es innerhalb der Benutzeroberfläche keine Möglichkeit, Formen umzubenennen. In PowerPoint 2007 können Sie die Formen nach Öffnen des Auswahlfensterbereichs umbenennen.)
Als Nächstes folgt die Aufgabe, diese Felder mit Daten aufzufüllen. Ich habe mich entschieden, diese Aufgabe mithilfe eines Eingabewörterbuchs (Von Zeichenfolge, Zeichenfolge) durchzuführen und den Inhalt des Wörterbuchs mit den Feldern im Dokument zu vergleichen. Außerdem wurde die IDisposable-Schnittstelle implementiert, damit sich COM-Objekte bereinigen lassen.
Für die Implementierung der Aktivitäten zum Ausfüllen der Office-Dokumente wurde die Basisklasse OfficeFormFill-Klasse verwendet. Sie unterstützt das Dispose-Muster und enthält einen gemeinsamen Designer, der den Aktivitäten eine visuelle Einheitlichkeit verleiht. Von dieser Basisklasse wurden dann die benutzerdefinierten Aktivitätsklassen abgeleitet.
Der Workflow basiert auf den benutzerdefinierten Aktivitäten WordFormFill, ExcelFormFill, PowerPointFormFill und DataEntryActivity. DataEntryActivity ermöglicht die Einführung von Variablen für den Workflow, die sich nicht auf ein Dokument beschränken, in die generische Wörterbuchstruktur, die von den verschiedenen OfficeFormFill-Aktivitäten verwendet wird. Zu diesem Zweck wird ein Office-Dokument als Vorlage verwendet, in dem die benannten Felder aufgeführt sind, und das dem Benutzer zum Ausfüllen vorgelegt wird. Die Vorlage kann aus einer beliebigen unterstützten Dokumentklasse stammen, ohne Rücksicht auf das mögliche Zieldokument oder die Dokumente, die während des Workflows erstellt und ausgefüllt werden.

Eingabe der Daten in den Workflow
Eine der ersten Herausforderungen, die bei der Integration von Office-Dokumenten in Workflows auftraten, war das Auffinden von Daten, die sich in einem Eingabedokument innerhalb des Workflows befinden. Das Standardworkflowparadigma stützt sich auf das Vorauswissen über die Namen der mit den Aktivitäten verbundenen Eigenschaften. Abhängigkeitseigenschaften können hochgestuft und so für den Workflow und andere Aktivitäten sichtbar gemacht werden. Ich kam zu dem Entschluss, dass dieser Ansatz zu starr ist, da als Folge der Gesamtworkflowentwurf mit bestimmten Feldern im Eingabedokument verbunden werden müsste. Im Fall einer Office-Integration dient die Workflowaktivität als generischer Proxy für alle Office-Dokumente. Es ist unrealistisch, die Namen der Felder im Dokument im Voraus festzulegen, weil dies eine benutzerdefinierte Workflowaktivität für unterschiedliche Arten von Dokumenten erfordert.
Wenn Sie sich die die Art und Weise ansehen, wie Argumente an Workflows und durch Erweiterungsaktivitäten übergeben werden, können Sie erkennen, dass sie als generisches Wörterbuch (Von Zeichenfolge, Objekt) übergeben werden. Das WF-Laufzeitmodul bindet dann die Elemente des Wörterbuchs direkt an die Eigenschaften des Workflows. Zum Ausfüllen von Feldern in einem Office-Dokument sind zwei Informationen erforderlich: der Name des Felds und der einzufügende Wert. Die allgemeine Strategie, die ich in der Vergangenheit verwendet habe, bestand darin, die benannten Felder im Dokument aufzuzählen und sie dann über den Namen mit dem Wörterbuch der WF-Eingabeparameter abzugleichen. Wenn ein Dokumentfeld mit dem Schlüssel im Wörterbuch übereinstimmt, wird es an den Eingabeparameter mit dem gleichen Namen übergeben. In diesem Fall erschien dieser Ansatz jedoch nicht richtig, weil dann für jedes der möglichen im Workflow verwendeten Dokumentfelder Eigenschaften erstellt werden müssten.
Anstatt Eigenschaften in einer Aktivität zu benennen und heraufzustufen, damit sie mit Feldern im Dokument übereinstimmen, wurde ein generisches Wörterbuch (Von Zeichenfolge, Zeichenfolge) verwendet. Diese Parameter wurden „Felder“ genannt und in jeder der Workflowaktivitäten verwendet. Der Schlüssel wird für den Vergleich mit dem Feldnamen und der Wert für das Ausfüllen der Feldinhalte verwendet. Dieses Wörterbuch aus Feldern ist einer der Einträge in den Parametern, die anschließend an den Workflow übergeben werden. Deshalb sind die realen Parameter, zumindest für die Office-Integration, in einem Wörterbuch innerhalb eines Wörterbuchs enthalten (siehe Abbildung 1).
Private WithEvents workflowRuntime As WorkflowRuntime = Nothing
Private workflowInstance As WorkflowInstance = Nothing
Private WithEvents waitHandle As New AutoResetEvent(False)
Public Sub main()
   Dim WorkflowType As Type = GetType(Workflow4)
   Dim Params As New Dictionary(Of String, Object)
   Dim Fields As New Dictionary(Of String, String)
   Params.Add("Fields", Fields)
   ' Start the workflow
   workflowRuntime = New WorkflowRuntime
   workflowInstance = _
      workflowRuntime.CreateWorkflow(WorkflowType, Params)
   workflowInstance.Start()
   waitHandle.WaitOne()
Sämtliche Workflowaktivitäten sollten möglichst allgemein genutzt werden können, und es sollte mehr als eine Strategie möglich sein, um das Eingabedokument oder die Vorlage anzugeben. Damit dies funktioniert, verfügen alle Aktivitäten über den InputDocument-Parameter. Dies ist eine Abhängigkeitseigenschaft, die je nach den Anforderungen des Workflows heraufgestuft werden kann. Sie enthält den Pfad zu einem Eingabedokument oder zu einer Vorlage. Der Code enthält jedoch auch Unterstützung für die Verwendung eines Feld-Parameters, dessen Name mit dem Namen der Aktivität übereinstimmt, wenn er je nach verwendeter Office-Anwendung einen Pfad zu einem Dokument oder einer Vorlage enthält.
Jeder reale Workflow besitzt eine Eingabedatenquelle. Zu Demonstrationszwecken wurde eine DataEntry-Aktivität verwendet, die ihre Vorlage (die Felder und die Standardwerte) aus jedem unterstützten Office-Dokumenttyp beziehen kann. Sie zeigt ein Eingabeformular an, in dem der Benutzer eine Zielaktivität festlegen kann. Dabei kann es sich um eine bestimmte OfficeFormFill-Aktivität oder eine zusammengesetzte Aktivität handeln. Hier wird als Beispiel eine IfElseActivity verwendet. Der Code muss nur die allgemeine Feldeigenschaft kennen, die von allen OfficeFormFill-Aktivitäten verwendet wird.

Verarbeitung der Daten
Wie bereits erwähnt, besitzt jedes der Office-Dokumenttypen eine eigene Sammlung an benannten Feldern. Jede der von OfficeFormFill abgeleiteten Aktivitäten wurde so geschrieben, dass ein bestimmter Dokumenttyp unterstützt wird. Es wäre zwar möglich, die Funktionen zu kombinieren, damit sie auf mehrere Dokumenttypen verweisen (DataEntryActivity ist ein Beispiel hierfür), aber dabei gibt es ein Problem: Wenn im Code auf eine der primären Office-Interopassemblys (Primary Interop Assemblies, PIAs) verwiesen wird und diese im System, auf dem der Code am Ende bereitgestellt wird, nicht vorhanden ist, könnte eine Ausnahme ausgelöst werden, selbst wenn die konkrete PIA den Ausführungspfad nicht verwendet. Zum Beispiel führt eine Select Case- oder If-Anweisung, die die abwesende Komponente offensichtlich umgeht, immer noch zu einer Ausnahme. Darum macht es sich bezahlt, die Aufrufe an Office-Komponenten zu isolieren. Wenn Sie eine kombinierte Aktivität erstellen wollen, sollten Sie diese Isolierung unbedingt beibehalten. Natürlich sollten Sie die Anwendung auch immer in allen potenziellen Zielumgebungen testen.
Die Aktivitäten, die einen der Office-Dokumenttypen unterstützen, folgen alle dem gleichen Muster. Wenn die InputDocument-Eigenschaft angegeben wird, dient sie als Pfad zum Dokument oder zur Vorlage. Ist die InputDocument-Eigenschaft gleich Null, sucht die Aktivität in der Feld-Eigenschaft nach einem Schlüssel, der zum Namen der Aktivität passt. Ein gefundener Schlüssel wird nach einem Pfad mit einem Suffix untersucht, der zum von der Aktivität behandelten Dokumenttyp passt. Wenn diese Bedingungen erfüllt sind, wird die InputDocument-Eigenschaft auf diesen Wert festgelegt.
Jeder passende Eintrag aus der Fields-Sammlung wird verwendet, um das entsprechende Feld im Dokument auszufüllen. Das Ergebnis wird im Ausgabedokument aufgeführt. Der Wert wird entweder als die entsprechende Abhängigkeitseigenschaft (OutputDocument) übergeben oder befindet sich als Eintrag „Output KeyValuePair“ in der Fields-Sammlung. Wenn das Ausgabedokument kein Suffix aufweist, wird ein geeignetes Standardsuffix angefügt. Dadurch kann der gleiche Wert möglicherweise zum Erstellen verschiedener Dokumenttypen oder sogar mehrerer Dokumente verschiedener Typen verwendet werden.
Das Ausgabedokument wird unter dem angegebenen Pfad gespeichert. In den meisten Workflowumgebungen ist dies eine Netzwerkfreigabe oder eine SharePoint®-Dokumentbibliothek. Aus Gründen der Einfachheit wurde im Beispiel ein lokaler Pfad verwendet.
Jede der Aktivitäten besitzt auch ein Feld mit dem Namen „Visible“, mit dem sich das Dokument optional anzeigen lässt, während es ausgefüllt wird. Abbildung 2 zeigt den Code für das Ausfüllen eines Word-Dokuments. Wichtig ist vor allem diese Zeile:
WordFiller = New WordInterface(InputDocument, Fields)
<STAThread()> Protected Overrides Function Execute _
  (ByVal executionContext As ActivityExecutionContext) _
  As ActivityExecutionStatus

  Dim Status As ActivityExecutionStatus

  ' Open the target document or template
  If String.IsNullOrEmpty(Me.InputDocument) Then
    If Me.Fields _
      IsNot Nothing AndAlso Me.Fields.ContainsKey(Me.Name) Then  
      ' Use a field named for *this* activity if it is a document 
      Dim NameValue As String = Fields(Me.Name)
      Select Case System.IO.Path.GetExtension(NameValue).ToLowerInvariant
        Case ".doc", ".docx", ".dot", ".dotx"
          InputDocument = NameValue
        Case Else
          Throw New ArgumentException( _ 
   "Input Document Invalid or Missing")
      End Select
    End If
  End If

  ' Create or open the required document from an input 
  ' document or template
  WordFiller = New WordInterface(InputDocument, Fields)

  ' Production workflow may not want to show the document
  If Me.Fields.ContainsKey("Visible") Then
    Boolean.TryParse(Me.Fields("Visible"), WordFiller.Visible)
  End If

  ' Get success or failure from the Word Interface class
  Dim Success As Boolean = WordFiller.FillInDocument()
  WordApp = WordFiller.WordApp

  If Success Then
    ' Find the target output document
    If String.IsNullOrEmpty(Me.OutputDocument) Then
      If Me.Fields.ContainsKey("Output") Then
        Dim NameValue As String = Fields("Output").ToString

        If NameValue.EndsWith(".doc", _
          StringComparison.CurrentCultureIgnoreCase) _
          OrElse NameValue.EndsWith(".docx", _
          StringComparison.CurrentCultureIgnoreCase) Then

          ' Set the document property to the provided name
          Me.OutputDocument = NameValue
        Else 'Force .doc suffix
          Me.OutputDocument = NameValue & ".doc"
        End If
      End If
    End If

    ' Save the output 
    WordApp.ActiveDocument.SaveAs(Me.OutputDocument)

    Cleanup()

    Status = ActivityExecutionStatus.Closed
    executionContext.CloseActivity()
  Else
    Cleanup()

    Status = ActivityExecutionStatus.Closed
    executionContext.CancelActivity(Me)
  End If

  Return Status
End Function
An dieser Stelle wird die WordInterface-Klasseninstanz erzeugt und ihr der Pfad zum Dokument, das als Vorlage dienen soll, zusammen mit den Felddaten übergeben. Diese wurden ganz einfach in den entsprechenden Eigenschaften gespeichert, damit sie von den Methoden der Klasse verwendet werden können.
Die WordInterface-Klasse liefert die Funktionalität für das Ausfüllen des Zieldokuments (siehe Abbildung 3). Wenn es sich beim Eingabedokument um eine Vorlage handelt, wird eine neue Instanz des Dokuments erzeugt. Wenn es sich um ein Dokument handelt, wird es schreibgeschützt geöffnet, um zufälliges Überschreiben zu verhindern. Diese Klasse erbt auch die Visible-Eigenschaft von der Basisklasse. Beachten Sie, dass hier einige Fehlerabfragen aus Gründen der Kürze wegfallen.
Public Overrides Function FillInDocument() As Boolean
  Dim Status As Boolean = False
  _WordApp = New Word.Application
  WordApp.Visible = Visible

  ' Check for template
  Select Case Path.GetExtension(Document).ToLowerInvariant
    Case ".dot", ".dotx"
      _WordDocument = WordApp.Documents.Add(Document)
    Case ".doc", ".docx"
      _WordDocument = WordApp.Documents.Open(FileName:=Document, _
        ReadOnly:=True)
  End Select

  ' Determine dictionary variables to use 
  ' based on bookmarks in the document matching Fields entries
  If WordDocument IsNot Nothing _
    AndAlso WordDocument.Bookmarks.Count > 0 Then

    Dim BookMark As Word.Bookmark = Nothing
    For i As Integer = 1 To WordDocument.Bookmarks.Count
      BookMark = WordDocument.Bookmarks(i)
      Dim BookMarkName As String = BookMark.Name
      Dim rng As Word.Range = BookMark.Range
      If Me.Fields.ContainsKey(BookMarkName) Then
        rng.Text = Fields(BookMarkName).ToString   
        'This results in the bookmark being lost, it needs to be replaced
        WordApp.ActiveDocument.Bookmarks.Add(BookMarkName, rng)
      Else
        ' Handle special case(s)
        Select Case BookMark.Name
          Case "FullName"
            rng.Text = GetFullName(Fields)
        End Select
      End If
    Next
      Status = True
  Else
    Status = False
  End If

  Return Status
End Function
Ein besonderes case-Feld mit dem Namen „FullName“ wurde hier hinzugefügt. Enthält das Dokument ein Feld mit diesem Namen, werden die Eingabefelder Title, FirstName und LastName miteinander verbunden, um das Feld auszufüllen. Da alle Office-Dokumenttypen ähnliche Anforderungen haben, wurde diese Funktion zusammen mit einigen anderen allgemeinen Eigenschaften in die OfficeInterfaceBasisklasse verschoben. Die OfficeInterface-Klassen enthalten auch die erforderliche Bereinigungslogik, durch die das korrekte Entfernen der COM-Objekte sichergestellt wird, die zum Ändern der Office-Dokumente erstellt wurden.
Jede der OfficeFormFill-Klassen besitzt eine OutputDocument-Eigenschaft. Diese kann auf mehrere Arten direkt festgelegt werden. Innerhalb des Designers kann eine Eigenschaft an den Parameter einer Workflowebene (einschließlich der Eigenschaften, die von anderen Aktivitäten hochgestuft wurden) oder an einen konstanten Wert gebunden werden. Zur Laufzeit sucht jede der OfficeFormFill-Typen in der OutputDocument-Eigenschaft nach dem Pfad, unter dem das Dokument gespeichert werden soll. Wenn kein Pfad festgelegt wurde, wird innerhalb der Fields-Sammlung nach einem Schlüssel mit dem Namen „Output“ gesucht Endet ein Wert mit einem geeigneten Suffix, wird er übernommen. Existiert kein geeignetes Suffix, wird eines hinzugefügt. Die Aktivität speichert anschließend das Ausgabedokument. Dadurch wird eine maximale Flexibilität bei der Entscheidung über den Pfad des Ausgabedokuments ermöglicht. Weil das Suffix wegfällt, kann der gleiche Wert von jedem der OfficeFormFill-Typen verwendet und so das Dokument im jeweils korrekten Format gespeichert werden. (Für Word und PowerPoint wurde hier als Suffix die Version für den Kompatibilitätsmodus verwendet. Excel liefert die Excel8CompatibilityMode-Eigenschaft, mit der sich der Dokumenttyp unterscheiden lässt, deshalb können Sie eine direkte Entscheidung treffen.)

Ein Beispielworkflow
Abbildung 4 zeigt den hier verwendeten Beispielworkflow, der demonstriert, wie die Integration funktioniert. Im Folgenden werden die Funktionen aller Aktivitäten im Einzelnen erörtert.
Abbildung 4 Workflow für das Erstellen eines Dokuments (Klicken Sie auf das Bild, um es zu vergrößern)
EnterCustomerData ganz oben ist eine Instanz der DataEntryActivity-Klasse. Dabei wird die DataEntryDocument-Eigenschaft verwendet, die in diesem Fall einfach zur Entwurfszeit festgelegt wird (siehe Abbildung 5). Diese Eigenschaft hätte auch durch das Programm festgelegt werden können, das den Workflow gestartet hat. DataEntryActivity zeigt ein Windows-Formular mit einem DataGridView-Steuerelement an, das durch Extrahieren des benannten Felds aus DataEntryDocument ausgefüllt wird. Der Pfad zu diesem Dokument wird ebenfalls angezeigt. Der Benutzer kann Werte in die Felder eintragen oder sie ändern.
Abbildung 5 Bindungen für die EnterCustomerData-Aktivität (Klicken Sie auf das Bild, um es zu vergrößern)
Bei DataEntryDocument kann es sich um ein beliebiges der unterstützten Office-Dokumenttypen handeln. In diesem Beispiel wird eine InfoPath-Dokumentvorlage verwendet. Die Felder des Dokuments werden extrahiert, indem die geeignete OfficeInterface-Klasse aufgerufen wird. Sie lädt das Zieldokument in die entsprechende Anwendung und listet die Felder (und falls vorhanden ihre Inhalte) auf. Hier wurde ein standardmäßig aktiviertes Kontrollkästchen verwendet, um die Standardnamen für Formen in PowerPoint auszuschließen. Sie besitzen Namen wie TextBox 1. Der Code für DataEntryActivityForm enthält eine Routine, um Feldnamen auszuschließen, die auf einem regulären Ausdruck basieren, und die dazu verwendet wird, um diese Werte und Namen in der DataGridView auszulassen.
Eines der durch DataEntryActivity bereitgestellten Felder ist TargetActivity. Das ist nur der Name der Aktivität, deren Feld-Eigenschaft mit den aus dem Eingabedokument gesammelten Feldern aufgefüllt werden soll. DataEntryActivity könnte dann auf jede der von OfficeFormFill abgeleiteten Aktivitäten oder auf eine beliebige CompositeActivity abzielen. Für diesen Artikel wird das TargetActivity-Feld angezeigt, das mit CustomerScenario im Voraus ausgefüllt wurde.
Der Zielaktivität werden die Daten von DataEntryActivity übergeben. Wenn das Ziel eine zusammengesetzte Aktivität ist, ist für diesen Prozess u. U. eine Navigation innerhalb der Aktivitätsstruktur erforderlich, um die geeignete Felder-Eigenschaft zu finden. Hier wurde eine WorkflowUtilities-Klasse erstellt, die Routinen für das Auffinden von Aktivitäten anhand des Namens oder der gewünschten Eigenschaft enthält.

Festlegen der Richtung
Die CustomerScenario-Aktivität ist eine IfElseActivity (die sich von CompositeActivity ableitet). Der IfElseActivity-Typ führt jede Verzweigung auf der Suche nach einem Rückgabewert von „True“ der Reihe nach aus. Gibt eine Verzweigung „True“ zurück, werden andere Verzweigungen nicht ausgeführt.
Die Struktur von CompositeActivity befindet sich in der XAML-Datei (eXtensible Application Markup Language) mit dem Namen „Workflow4.xoml“ und die entsprechende Logik befindet sich in der Codedatei mit dem Namen „Workflow4.xoml.vb“. Der erste Teil der CustomerScenario-Aktivität wird in Abbildung 6 gezeigt.
<IfElseActivity x:Name="CustomerScenario">
  <IfElseBranchActivity x:Name="ProcessApproval">
    <IfElseBranchActivity.Condition>
    <CodeCondition Condition="ifElseBranchActivity1Condition" />
    </IfElseBranchActivity.Condition>
    <CodeActivity x:Name="codeActivity1" ExecuteCode=
      "{ActivityBind Workflow4,Path=codeActivity1_ExecuteCode1}" />
    <ns1:WordFormFill Description="Fill in fields in a Word document"
      x:Name="WriteCustomerLetter" Fields="{x:Null}" 
      OutputDocument="{x:Null}" 
      InputDocument=" C:\MSDN Workflow\Templates\Customer Letter.doc" />
</IfElseBranchActivity>
Damit der Code hinter der Verzweigung die Entscheidung verarbeitet, wird ein Rückgabewert in der Result-Eigenschaft des Ereignisarguments festgelegt, das an jede Verzweigung übergeben wird. Die Entscheidung, mit einer bestimmten Verzweigung fortzufahren, wird im Code getroffen und basiert auf dem Wert des Status-Felds, das an die Aktivität übergeben wird. Die ProcessApproval-Verzweigung sucht nach einem Wert „Genehmigt“. Die ProcessDisapproval-Verzweigung sucht nach „Abgelehnt“. Alle anderen Werte werden von der ProcessUndecided-Verzweigung behandelt. Abbildung 7 zeigt den Code, der in der ProcessApproval-Verzweigung ausgeführt wird.
Public Sub ifElseBranchActivity1Condition(ByVal sender As Object, _
  ByVal e As ConditionalEventArgs)
  ' Look for status matching desired value for *this* branch
  If Me.Fields.ContainsKey("Status") AndAlso _
    Me.Fields("Status") = "Approved" Then

    ' Look for a WordFormFill activity as a child to *this* branch
    Dim target As Activity = _
      WorkflowUtilities.FindActivityByType( _
      GetType(WordFormActivity.WordFormFill), _
      (DirectCast(sender, Activity)))

    If target IsNot Nothing Then
      Dim TargetActivity As WordFormActivity.WordFormFill = _
        DirectCast(target, WordFormActivity.WordFormFill)
      TargetActivity.Fields = Me.Fields
      ' Set input document by using field named for target activity
      TargetActivity.Fields.Add(TargetActivity.Name, Me.InputDocument)
      e.Result = True

    Else
      e.Result = False
    End If

  Else
    e.Result = False
  End If

End Sub
Die anderen Verzweigungen sind ähnlich, übergeben aber ihre Daten an eine Aktivität vom Typ Power­PointFormFill oder ExcelFormFill. In einem realen Geschäftsprozess lassen sich einfach unterschiedliche Word-Vorlagen verwenden, durch die beispielsweise ein Kundenanschreiben erstellt wird, sodass nicht mit zusätzlichen Office-Dokumenttypen interagiert werden muss.
Bei der WordFormFill-Instanz, die in dieser Verzweigung ausgeführt wird, wurde das Eingabedokument (Vorlage), wie in Abbildung 8 gezeigt, zur Entwurfszeit festgelegt. Beachten Sie, dass die Fields-Eigenschaft anfangs Null ist. Dies ist programmgesteuert festgelegt.
Abbildung 8 WriteCustomerLetter-Eigenschaften (Klicken Sie auf das Bild, um es zu vergrößern)

Workflowbeziehungen
Der im Beispielworkflow verwendete Ansatz verlässt sich teilweise auf die Fähigkeit einer Aktivität, eine andere zu finden. Dazu müssen die Aktivitäten untergeordnete Aktivitäten einer CompositeActivity sein, die die Hauptworkflowtypen umfasst: Sequenziell, Statuscomputer und regelgesteuert. Dies Art von Beziehungen ist folglich Bestandteil der Aktivitäten jedes nicht trivialen Workflows.
Die WorkFlowUtilities-Klasse stellt Methoden bereit, um über den Namen oder den Typ nach einer untergeordneten Aktivität in einer Aktivitätsstruktur zu suchen. Sie stellt auch Funktionen bereit, um in der Struktur aufwärts nach übergeordneten Aktivitäten mit einer bestimmten Eigenschaft zu suchen.
Diese Dienstprogrammmethoden unterstützen das Auffinden von Zielaktivitäten, um an sie Parameter zu übergeben, sowie das Auffinden einer Quelle für die Eingabeparameter. Wenn zum Beispiel DataEntryActivity ein TargetActivity-Parameter über die Fields-Sammlung übergeben wird, kann dieser, angefangen beim eigenen Container, überall in der Aktivitätsstruktur gefunden werden:
Dim EntryActivity As Activity = Nothing
EntryActivity = WorkflowUtilities.FindActivityByName( _
  TargetActivityName, Me.Parent)

Abschluss des Workflows
Die Aktivitäten innerhalb des Beispielworkflows sind fast beendet. Die über das Status-Feld ausgewählte Aktivität erstellt das Ausgabedokument am angegebenen Speicherort. Von hier aus kann es von anderen Aktivitäten zur weiteren Verarbeitung abgerufen werden.
In diesem Artikel wurde ein allgemeiner Entwurfsansatz für die Kopplung eines Workflows mit Office-Clientanwendungen skizziert. Durch Einhaltung der Prinzipien des objektorientierten Entwurfs können wiederverwendbare Workflowaktivitäten und unterstützende Klassen erstellt werden, die sich für viele ähnliche Aufgaben einsetzen lassen.

Rick Spiewak ist als Lead Software Systems Engineer bei der MITRE Corporation tätig Er arbeitet am U.S. Air Force Electronic Systems Center an der Planung von Einsätzen. Rick Spiewak arbeitet seit 1993 mit Visual Basic und seit 2002 mit Microsoft .NET Framework. Er war einer der Betatester für Visual Studio .NET 2003.

Page view tracker