in

InfoPath Dev

Tom Lawrence

  • Connecting an InfoPath Forms Services form template to DBXL v2.2

    Connecting an InfoPath Forms Services form template to DBXL v2.2

     

    For users of the InfoPath application in Office 2007, DBXL has been providing a great way to easily connect forms with SQL databases. Now, with the increasing popularity of InfoPath Forms Services (IPFS) running on Microsoft Office SharePoint Services (MOSS), people are wondering how to connect an IPFS form template to a database using DBXL. It can be done, but for now it requires some form code to access the DBXL web services. This gets around the problem of IPFS being unable to open an xml document from any source other than a SharePoint document collection. Eventually, this should be easy to do from within DBXL without any code in the form template. To provide a solution now, this blog post intends to provide a tutorial of how-to steps for connecting an existing form template with DBXL for use on IPFS. This blog post builds on the initial idea outlined by David Airapetyan in a blog entry at http://www.infopathdev.com/blogs/david/archive/2008/05/05/browser-forms-and-dbxl.aspx.

    At the end of this tutorial you should have an IPFS-ready form template that can submit documents to DBXL and load them through the use of a querystring added the IPFS launch URL for your template.

     

    I. Preparations

    I will make some assumptions during this tutorial about how the form template is set up. The instructions provided here can be adapted to any form template. However, if you are not an advanced form template developer you may wish to follow the tutorial through once exactly as described before attempting this on a different form template.

    1. I will be customizing the ExpenseReport sample that comes with InfoPath. This form template does not contain any existing load or submit handlers. If your form template does, you’ll need to modify them as fits your scenario to connect with the sample code I provide in this tutorial.
    2. I will assume you have a working SharePoint and MOSS 2007 server, have verified that IPFS is working correctly on simple forms, and that DBXL v2.2 installed and working on the main SharePoint web site at port 80.

    II. Modifying the InfoPath Form Template in the Designer

    Creating the Form

    1. Start InfoPath. In the dialog box that opens, click Design a Form Template..., then click Customize a Sample..., then choose the template called Sample - Expense Report.
    2. Save the form template to a convenient location. We’ll need to save the form in order to add code to it. Don’t worry about publishing yet.

    Creating the GetDocument and SubmitDocument data adapters

    The form code will use two web service data adapters, one called GetDocument which will retrieve document data from DBXL, the other called SubmitDocument which will post a modified document back to DBXL. First, we’ll configure the GetDocument data adapter.

    1. From the Tools menu, choose Data Connections..., then click the Add... button.
    2. Select the Create a new connection to radio button. Then choose Receive data. Click Next in the wizard.
    3.  Under From where do you want to receive your data?, choose Web service. Click Next.
    4. Enter the location of the Qdabra DBXL Document web service. It likely looks like http://<your-server-name>/QdabraWebService/DbxlDocumentService.asmx . Click Next.
    5. Select the operation called GetDocument, near the bottom of the list. Click Next.
    6.  For tns:docId, click Set Sample Value..., enter 0 (that is, zero), click OK, then click Next.
    7. No value is needed for tns:docId on the next panel. Just click Next.
    8. There is no need to store a copy of the data in the form template. Just click Next.
    9. Uncheck the box Automatically retrieve data when the form is opened. Our code will do that instead. Then click Finish.

    Now we’ll configure the SubmitDocument data adapter.

    1. We should be back at the data connections dialog box. Click Add....
    2. Select the Create a new connection to radio button. Then choose Receive data. Click Next in the wizard. Note: you are choosing Receive data even though think of the operation as submitting the document. This is a result of how the web service method was defined.
    3. Under From where do you want to receive your data?, choose Web service. Click Next.
    4.  Enter the location of the Qdabra DBXL Document web service, same as above in step 6. Click Next.
    5. Select the operation called SubmitDocument, near the bottom of the list. Click Next.
    6. No value is needed for any of the parameters on this panel. Just click Next.
    7. There is no need to store a copy of the data in the form template. Just click Next.
    8. Uncheck the box Automatically retrieve data when the form is opened. Our code will do that instead. Then click Finish.
    9. Back at the data connections dialog box, click Close.

    Adding the On Load and Submit handlers

    In this series of steps we will hook up the On Load and Submit handlers to form code using Visual Studio Tools for Applications (VSTA). However, we won’t fill in the event handlers yet.

    1. Click the Tools menu, locate the submenu Programming, and choose the item Loading Event.... VSTA will launch and show code for an empty event handler, called FormEvents_Loading.
    2. Back in InfoPath Designer, go to the Tools menu and choose Submit Options....
    3. In Expense Report, Allow users to submit this form will already be checked. If it is not for your form template, check it now.
    4. Choose Perform custom action using Code.
    5. Click Edit Code.... VSTA will flash on the task bar. If you switch to it you’ll notice another empty submit handler, called FormEvents_Submit, has appeared.
    6. Back in InfoPath Designer, click OK to dismiss the submit handler dialog box.

    Writing the custom code to query and submit the DBXL document

    In this series of steps we will hook up the On Load and Submit handlers to form code using Visual Studio Tools for Applications (VSTA). However, we won’t fill in the event handlers yet.

    1. We’ll need to add some subroutines to the form code which will handle querying the web services we added earlier.

    a.  If you are programming in Visual Basic, insert the following code just before the End Class statement at the bottom of the file:

    Private Function GetDocTypeName() As String

     

        ' Return the document type name that will be used in DBXL to identify the documents.

        ' For tutorial simplicity this is hard-coded, but can be obtained via more flexible methods for production templates.

        Return "MyDocType"

     

    End Function

     

    Private Function LoadFromDbxl(ByVal docType As String, ByVal docId As String) As Boolean

     

        Try

            Dim domGetDocument As XPathNavigator = DataSources("GetDocument").CreateNavigator()

     

            ' Set docId argument for the DBXL web service call.

            domGetDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:GetDocument/tns:docId", NamespaceManager).SetValue(docId)

     

            ' Invoke the web service method to query DBXL for the document.

            Dim connGetDocument As DataConnection = DataConnections("GetDocument")

            connGetDocument.Execute()

     

            ' Check for error.

            If Not domGetDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:GetDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean Then

                Throw New Exception("GetDocument failed")

            End If

     

            ' Replace main DOM with obtained document.

            Dim root As XPathNavigator = MainDataSource.CreateNavigator().SelectSingleNode("/child::*", NamespaceManager)

            Dim newDoc As XPathNavigator = domGetDocument.CreateNavigator().SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:docInfo/tns:Content/node()", NamespaceManager)

            root.InnerXml = newDoc.InnerXml

     

            ' Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.

            InsertQdabraDbxlPi(docType, docId)

        Catch

            Return False ' Failure.

        End Try

        Return True ' Success.

     

    End Function

     

    Private Function SubmitToDbxl(ByVal docType As String, ByVal name As String, ByVal author As String, ByVal description As String) As Boolean

     

        Dim domMainDocument As XPathNavigator = MainDataSource.CreateNavigator()

        Dim domSubmitDocument As XPathNavigator = DataSources("SubmitDocument").CreateNavigator()

     

        Try

            ' Set the arguments for the SubmitDocument web service call.

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:docTypeName", NamespaceManager).SetValue(docType)

            ' Notice that tns:xml will contain the entire main document being submitted to DBXL.

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:xml", NamespaceManager).SetValue(MainDataSource.CreateNavigator().OuterXml)

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:name", NamespaceManager).SetValue(name)

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:author", NamespaceManager).SetValue(author)

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:description", NamespaceManager).SetValue(description)

     

            ' Invoke the web service method to submit the document data to DBXL.

            Dim connSubmit As DataConnection = DataConnections("SubmitDocument")

            connSubmit.Execute()

     

            ' Check for error.

            If Not domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:SubmitDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean Then

                Throw New Exception("SubmitDocument failed")

            End If

     

            ' Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.

            Dim docId As String = domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:docId", NamespaceManager).Value

            InsertQdabraDbxlPi(docType, docId)

        Catch

            Return False ' Failure.

        End Try

        Return True ' Success.

     

    End Function

     

    Private Sub InsertQdabraDbxlPi(ByVal docType As String, ByVal docId As String)

     

        Dim domMainDocument As XPathNavigator = MainDataSource.CreateNavigator()

     

        ' Remove any existing QdabraDBXL PI.

        Dim oldPi As XPathNavigator = domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'QdabraDBXL']", NamespaceManager)

        If Not oldPi Is Nothing Then

            oldPi.DeleteSelf()

        End If

     

        If docId <> "" Then

            ' Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.

            Dim newPi As String = String.Format("<?QdabraDBXL docid='{0}' doctype='{1}' ?>", docId, docType)

            domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'mso-infoPathSolution']", NamespaceManager).InsertBefore(newPi)

        End If

     

    End Sub

    b.  If you are programming  in Visual C#, use this code instead, inserted just after the closing brace for the empty FormEvents_Submit function:

    public string GetDocTypeName()

    {

        // Return the document type name that will be used in DBXL to identify the documents.

        // For tutorial simplicity this is hard-coded, but can be obtained via more flexible methods for production templates.

        return "MyDocType";

    }

     

    private bool LoadFromDbxl(string docType, string docId)

    {

        try

        {

            XPathNavigator domGetDocument = DataSources["GetDocument"].CreateNavigator();

     

            // Set docId argument for the DBXL web service call.

            domGetDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:GetDocument/tns:docId", NamespaceManager).SetValue(docId);

     

            // Invoke the web service method to query DBXL for the document.

            DataConnection connGetDocument = DataConnections["GetDocument"];

            connGetDocument.Execute();

     

            // Check for error.

            if (!domGetDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:GetDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean)

                throw new Exception("GetDocument failed");

     

            // Replace main DOM with obtained document.

            XPathNavigator root = MainDataSource.CreateNavigator().SelectSingleNode("/child::*", NamespaceManager);

            XPathNavigator newDoc = domGetDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:docInfo/tns:Content/node()", NamespaceManager);

            root.InnerXml = newDoc.InnerXml;

     

            // Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.

            InsertQdabraDbxlPi(docType, docId);

        }

        catch

        {

            return false; // Failure.

        }

        return true; // Success.

    }

     

    private bool SubmitToDbxl(string docType, string name, string author, string description)

    {

        XPathNavigator domMainDocument = MainDataSource.CreateNavigator();

        XPathNavigator domSubmitDocument = DataSources["SubmitDocument"].CreateNavigator();

     

        try

        {

            // Set the arguments for the SubmitDocument web service call.

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:docTypeName", NamespaceManager).SetValue(docType);

            // Notice that tns:xml will contain the entire main document being submitted to DBXL.

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:xml", NamespaceManager).SetValue(MainDataSource.CreateNavigator().OuterXml);

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:name", NamespaceManager).SetValue(name);

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:author", NamespaceManager).SetValue(author);

            domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:description", NamespaceManager).SetValue(description);

     

            // Invoke the web service method to submit the document data to DBXL.

            DataConnection connSubmit = DataConnections["SubmitDocument"];

            connSubmit.Execute();

     

            // Check for error.

            if (!domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:SubmitDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean)

                throw new Exception("Submit failed");

     

            // Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.

            string docId = domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:docId", NamespaceManager).Value;

            InsertQdabraDbxlPi(docType, docId);

        }

        catch

        {

            return false; // Failure.

        }

     

        return true; // Success.

    }

     

    private void InsertQdabraDbxlPi(string docType, string docId)

    {

        XPathNavigator domMainDocument = MainDataSource.CreateNavigator();

     

        // Remove any existing QdabraDBXL PI.

        XPathNavigator oldPi = domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'QdabraDBXL']", NamespaceManager);

        if (oldPi != null)

        {

            oldPi.DeleteSelf();

        }

     

        if (docId != "")