Adatok másolása InfoPath-adatforrások között
FRISSÍTÉS: Bizonyos körülmények között baj van az AppendChild() függvénnyel. Készítettem egy megkerülő megoldást, amely az InsertAfter() függvényt használja, és némiképpen lerövidítettem a kódot. Mostantól ez olvasható a cikkben.
Érdekes kérdést kaptam az egyik ügyfelünktől. Egy InfoPath-űrlap egy webszolgáltatást hív, és ismétlődő adatokat kap vissza. Szeretné további mezőkkel kiegészíteni a sémát, majd űrlaptárba menteni a kitöltött űrlapot. Egyszerűnek hangzik, de nem az.
A feladat
Tegyük fel, hogy a külső adatforrás egy projektlista, amely minden projekthez megadja annak megnevezését és a felelős nevét. Ha másodlagos adatforrásként hozzáadjuk az InfoPath-űrlapsablonhoz, kb. a következőt kapjuk:
A kis lakatok azt jelzik, hogy sem az adatforrás szerkezetét, sem az egyes mezők tulajdonságait nem változtathatjuk meg. Pedig nekünk valami olyasmi kellene, mint a következő ábrán:
Az ismétlődő ProjectReport rekordok valójában a külső adatforrás Project rekordjaira épülnek, de tartalmaznak egy plusz mezőt (ProjectStatus) és egy teljes ismétlődő csomópontot (Task) is annak extra mezőivel egyetemben. Mivel a másodlagos adatforrást nem tudjuk módosítani, az a megoldás, hogy adatait beolvassuk, majd átmásoljuk az elsődleges adatforrás megfelelő mezőibe.
Ezt a feladatot már nem tudjuk az InfoPath negyedik generációs eszközeivel (pl. szabályok) elvégezni, programoznunk is kell. Ebben segít, hogy a 2007-es InfoPath a VBScript/JScript mellett tartalmazza a Visual Studio Tools for Applications (VSTA) környezetet, ami a Visual Basic for Applications (VBA) utódja, és teljes körű programozási lehetőséget biztosít Visual Basic .NET és C# nyelveken egyaránt.
A VSTA a .NET Framework 2.0-ra és a Core XML Services 6.0-ra épül, és helyből ki van kapcsolva az Office-telepítőben, ezeket tehát célszerű előre ellenőrizni.
A megoldás
Hozzuk létre a fent látható első (sárga hátterű) szerkezettel rendelkező külső adatforrást. A webszolgáltatás helyett az egyszerűség kedvéért használhatunk egy XML adatfájlt is, kb. a következő tartalommal:
<?xml version="1.0" encoding="utf-8" ?>
<Projects>
<Project>
<ProjectName>P001</ProjectName>
<ProjectOwner>Kőnig Tibor</ProjectOwner>
</Project>
<Project>
<ProjectName>P002</ProjectName>
<ProjectOwner>Kőnig Tibor</ProjectOwner>
</Project>
<Project>
<ProjectName>P003</ProjectName>
<ProjectOwner>Csermák Zsolt</ProjectOwner>
</Project>
</Projects>Nyissunk meg egy új InfoPath-űrlapsablont, és hozzuk benne létre a fent látható második (fehér hátterű) adatszerkezetet.
Adjuk a sablonhoz az XML fájlt mint másodlagos adatkapcsolatot. (Legyünk negatívak: ne erőforrásfájlként vegyük fel, ne engedélyezzük a kapcsolat nélküli használatot, és ne kérjük a beolvasását az űrlap megnyitásakor.)
Alakítsuk ki az űrlap felhasználói felületét. Itt látható egy lehetőség a megvalósításra (ismétlődő szekciókkal és elrendezéstáblázatokkal):
Mentsük el az űrlapsablont a biztonság kedvéért, pl. ProjectReport.xsn néven.
Nyissuk meg a Betöltés gomb tulajdonságait, majd kattintsunk az Űrlapkód módosítása... gombra. Ha a VSTA telepítve van a gépünkön, megjelenik a Visual Studionál már megszokott fejlesztőkörnyezet, benne egy InfoPath-kódprojekt az InfoPathban beállított nyelven (VB vagy C#; nálam VB). A kurzor a Betöltés gomb (én btnLoad-nak neveztem el) eseménykezelőjében villog, ami kb. így néz ki:
Public Sub btnLoad_Clicked(ByVal sender As Object, _
ByVal e As ClickedEventArgs)
' Ide írhatja a kódot.
End SubEzt az eseménykezelőt fogjuk most elkészíteni. A fő és a másodlagos adatforrás XML adatain dolgozunk majd, az XPath technológia használatával. Először olvassuk be a (nálam Projects névre keresztelt) forrásadatokat a következő kóddal:
' A forrásadatok beolvasása
Me.DataConnections("Projects").Execute()Tegyük elérhetővé a projektek listáját:
' Az adatforrás és a projektek gyűjteményének megcímzése
Dim projectRoot As XPathNavigator = Me.DataSources _
("Projects").CreateNavigator()
Dim projectList As XPathNodeIterator = _
projectRoot.Select("//Project", NamespaceManager)Egy kis értelmezés: az XPathNavigator (amit itt a CreateNavigator állít elő) az űrlap adatszerkezetének egy csomópontját (Node), míg az XPathNodeIterator csomópontok egész gyűjteményét címezi meg, a NamespaceManager objektum pedig nyilvántartja a különböző adatkapcsolatokban használt névtereket.
Játsszuk el ezt a projektjelentések listájával is:
' Az űrlapadatok megcímzése
Dim reportRoot As XPathNavigator = _
Me.MainDataSource.CreateNavigator()Az űrlapsablonban helyből van egy (üres) ProjectReport csomópont vagy rekord. Ezt sokszorosítjuk, hogy pont annyi legyen belőle, mint amennyi Project csomópont van a projektek listájában:
' A szükséges számú üres jelentés létrehozása
Dim firstReport As XPathNavigator = _
reportRoot.SelectSingleNode _
("//my:ProjectReport[1]", NamespaceManager)
Dim projectIndex As Integer
For projectIndex = 2 To projectList.Count
Dim clonedReport As XmlReader = _
firstReport.ReadSubtree
clonedReport.ReadInnerXml()
firstReport.InsertAfter(clonedReport)
NextMost pedig ciklusban átmásoljuk a projektlistából a projektjelentés-listába a projektek megnevezését és felelősét:
' A projektek és felelősök nevének átmásolása
For projectIndex = 1 To projectList.Count
reportRoot.SelectSingleNode _
("//my:ProjectReport[" & projectIndex.ToString _
& "]/my:ProjectName", _
NamespaceManager).SetValue _
(projectRoot.SelectSingleNode _
("//Project[" & projectIndex.ToString & _
"]/ProjectName", NamespaceManager).Value)
reportRoot.SelectSingleNode _
("//my:ProjectReport[" & projectIndex.ToString _
& "]/my:ProjectOwner", _
NamespaceManager).SetValue _
(projectRoot.SelectSingleNode _
("//Project[" & projectIndex.ToString & _
"]/ProjectOwner", NamespaceManager).Value)
Next
Kész az űrlapsablon, tegyük közzé, és kezdjük el használni! A forrásadatok és a kézzel bevitt új adatok egy új, közös sémában egyesülnek.