Ein Kunde, der InfoPath 2003 einsetzt, hat einige Anforderungen, die sich nicht so einfach umsetzen lassen. Da das große weite Internet leider fast nichts dazu hergibt, hier meine Probleme inkl. Lösungen.
Anforderung 1: Eine “multi-select list box”, also eine ListBox zum anhaken. In InfoPath 2007 bereits enthalten, muss diese bei 2003 leider selbst gebaut werden. Dazu gab es einen recht guten Artikel bei InfoPathDev, den ich nun (nach deren umgestaltung) leider nicht mehr wieder finde… Deshalb hier nochmal in Kürze:
Die DataSource Struktur (der InternetPortals Knoten ist repeatable und “internetPortalSelected” ist vom Typ boolean):
Nun erstellt man eine optionale Section auf dem Formular, und bindet sie an den “PortalsGroup” Knoten. Innerhalb der optionalen Section erstellt man eine Scrolling Region. Dort zieht man dann den “InternetPortals” Knoten hinein und wählt “Repeating Table”. In den Eigenschaften hakt man “Allow users to insert and delete rows” und “Include Header” ab. Außerdem sollte die Tabelle natürlich keine Rahmen haben.
Jetzt nur noch das ganze entsprechend ansortieren, die Textbox durch ExpressionBox ersetzen und als default Wert für den Namen etwas schreiben wie “loading…”. Die ID muss dabei dem Benutzer gar nicht gezeigt werden und kann gelöscht werden. So sieht das ganze dann aus:
Anforderung 2: Und hier wird es komplizierter. Die ListBox soll nun mit Daten von einem WebService befüllt werden, und zwar erst beim öffnen der Section.
Problem 1 daran ist, dass es im IP 2003 Objektmodell keinerlei Events gibt, die ein aufklappen einer section anzeigen. Das einzige Event, was an der Stelle hilft ist das OnContextChange Event. Dieses wird allerdings ziemlich häufig gefeuert, z.b. bei jedem klick irgendwo auf das Formular, etc.
Problem 2 ist, dass man an die selbstgebaute ListBox keine DataSource anbinden kann (zumindest nicht über Wizards). Hier die Lösung (siehe Kommentare im Code):
[InfoPathEventHandler(EventType = InfoPathEventType.OnContextChange)]
public void OnContextChange(DocContextChangeEvent e)
{
if (e.Type == "ContextNode")
{
LoadPortals();
}
}
private void LoadPortals()
{
// Zunächst den Knoten finden der den Repeatable Knoten enthält
IXMLDOMNode ipGroup = thisXDocument.DOM.selectSingleNode("my:Fields/my:PortalsGroup/my:Portals1");
// Ist dieser null dann ist die section nicht aufgeklappt, es muss nichts getan werden
if (ipGroup == null)
return;
// Die DataSource abrufen (hier: "GetInternetPortals"). Diese muss zunächst über
// Tools->Data Connections angelegt werden und das häkchen bei "Automatically retrieve data
// when form is opened" muss nicht selektiert werden, denn die Daten sollen ja nur bei Bedarf
// geladen werden, um so das öffnen des Formulars nicht unnötig zu verlangsamen.
DataObject dob = thisXDocument.DataObjects["GetInternetPortals"];
dob.Query();
// Hier müssen die Namespaces gesetzt werden um auf das DOM des WebServices zugreifen zu können.
// Der erste Namespace ist IP 2003 standard, der zweite muss durch den Namespace
// des WebServices ersetzt werden.
IXMLDOMDocument2 d = (IXMLDOMDocument2)dob.DOM;
d.setProperty("SelectionNamespaces",
"xmlns:dfs=[http://schemas.microsoft.com/office/infopath/2003/dataFormSolution\](http://schemas.microsoft.com/office/infopath/2003/dataFormSolution\)
xmlns:tns="http://MY-WEB-SERVICE-URL.COM/SERVICES\"");
// Den Knoten des WebService Response holen (wie dieser Knoten heißt lässt sich am einfachsten
// raus finden, in dem man im debug modus einfach mal den Inhalt von d.xml anschaut).
IXMLDOMNode rootNode = d.selectSingleNode("dfs:myFields/dfs:dataFields/
tns:GetInternetPortalsResponse/tns:GetInternetPortalsResult");
// Erst jetzt kann man feststellen, ob die Daten bereits in der Listbox enthalten sind
if (ipGroup.childNodes.length > rootNode.childNodes.length)
return;
bool removeFirst = true;
foreach (IXMLDOMNode node in rootNode.childNodes)
{
// Hier die beiden Knoten die vom WebService zurückgeliefert werden.
string id = node.selectSingleNode("tns:Id").text;
string name = node.selectSingleNode("tns:Name").text;
IXMLDOMNode group = thisXDocument.DOM.
selectSingleNode("my:Fields/my:PortalsGroup/my:Portals1");
IXMLDOMNode field = thisXDocument.DOM.
selectSingleNode("my:Fields/my:PortalsGroup/my:Portals1/my:InternetPortals");
IXMLDOMNode newNode = field.cloneNode(true);
if (removeFirst)
{
group.removeChild(field);
removeFirst = false;
}
// Werte vom neuen Knoten setzen
newNode.selectSingleNode("my:internetPortalId").text = id;
newNode.selectSingleNode("my:internetPortal").text = name;
group.appendChild(newNode);
}
}
Demnächst mehr aus der InfoPath Hell...