Integrating InfoPath Forms using XmlFormView control with ASPX pages and SharePoint 2007

Hi Folks,

Scenario

You have a web form where you need to do the following:

  • Retrieve XML data from a SQL Data Store
  • Provide an interface for Viewing and editing the data depending on the status of the data being viewed.
  • The form view for the XML data should be easy to generate when the XSD changes
  • The template used to publish the elements and attributes of the XML will be designed using InfoPath
  • InfoPath will use Web Service data connections, which are published as Data Connection Libraries on a
    SharePoint 2007 Data Connection Library
  • InfoPath will use a Edit and View mode in one template, so data can be controlled and secured for viewers and authors of data.
  • InfoPath templates will be published on SharePoint 2007 InfoPath Forms library
  • A custom ASPX page will leverage the XmlFormView control to embed the web based Info Path form which is hosted on SharePoint.

Design the Web Services

The first stage is to have the web services ready to Submit and Receive Data. All Info Path forms are designed with data connections to
Submit and Receive Data.

So the first step is to have the Data Layer built.

Design the InfoPath Form

This is divided into two stages

Stage 1

This is where you will create a Data Connection Library and publish it to the MOSS 2007 Server. The Data Connection library will have
all the properties to Submit and Receive data from the web service. When InfoPath forms are loaded it will retrieve the data connections|
from the SharePoint 2007 Data connection library. Below is a sample Library:

Notice above I have two libraries:

DCL for the data connections and WorkflowForms for the InfoPath Template.

Also notice I have no files saved in the WorkflowForms, the template is there, just no need to have a XML based file. The XmlFormView
can still use the template J

Notice the two types:

Main Query (Receive) and Submit.

If we click on of these published DCL’s we have this information:

MainQuery:

  <?xml version="1.0" encoding="UTF-8" ?>

  <?MicrosoftWindowsSharePointServices ContentTypeID="0x010100B4CBD48E029A4ad8B62CB0E41868F2B0"?>

<udc:DataSource MajorVersion="2" MinorVersion="0" xmlns:udc="http://schemas.microsoft.com/office/infopath/2006/udc">

  <udc:Name>Main query</udc:Name>

  <udc:Description>Format: UDC V2; Connection Type: WebService; Purpose: ReadOnly; Generated by Microsoft Office InfoPath 2007 on 2009-05-08 at 13:25:16 by romiko.</udc:Description>

<udc:Type MajorVersion="2" MinorVersion="0" Type="WebService">

  <udc:SubType MajorVersion="0" MinorVersion="0" Type="" />

  </udc:Type>

<udc:ConnectionInfo Purpose="ReadOnly" AltDataSource="">

  <udc:WsdlUrl>http://Romiko.com/Workflow/WorkflowService.asmx?WSDL</udc:WsdlUrl>

<udc:SelectCommand>

  <udc:ListId />

  <udc:WebUrl />

  <udc:ConnectionString />

  <udc:ServiceUrl UseFormsServiceProxy="false">http://Romiko.com/Workflow/WorkflowService.asmx</udc:ServiceUrl>

  <udc:SoapAction>http://Romiko.WebServices/GetWorkflowRecordByTransactionId</udc:SoapAction>

  <udc:Query />

  </udc:SelectCommand>

<udc:UpdateCommand>

  <udc:ServiceUrl UseFormsServiceProxy="false" />

  <udc:SoapAction />

  <udc:Submit />

  <udc:FileName>Specify a filename or formula</udc:FileName>

  <udc:FolderName AllowOverwrite="" />

  </udc:UpdateCommand>

<!–

udc:Authentication><udc:SSO AppId=” CredentialType=” /></udc:Authentication

  –>

  </udc:ConnectionInfo>

  </udc:DataSource>

 

Submit

  <?xml version="1.0" encoding="UTF-8" ?>

  <?MicrosoftWindowsSharePointServices ContentTypeID="0x010100B4CBD48E029A4ad8B62CB0E41868F2B0"?>

<udc:DataSource MajorVersion="2" MinorVersion="0" xmlns:udc="http://schemas.microsoft.com/office/infopath/2006/udc">

  <udc:Name>Main submit</udc:Name>

  <udc:Description>Format: UDC V2; Connection Type: WebService; Purpose: WriteOnly; Generated by Microsoft Office InfoPath 2007 on 2009-05-08 at 13:24:00 by romiko.</udc:Description>

<udc:Type MajorVersion="2" MinorVersion="0" Type="WebService">

  <udc:SubType MajorVersion="0" MinorVersion="0" Type="" />

  </udc:Type>

<udc:ConnectionInfo Purpose="WriteOnly" AltDataSource="">

  <udc:WsdlUrl>http://Romiko.com/Workflow/WorkflowService.asmx?WSDL</udc:WsdlUrl>

<udc:SelectCommand>

  <udc:ListId />

  <udc:WebUrl />

  <udc:ConnectionString />

  <udc:ServiceUrl UseFormsServiceProxy="false" />

  <udc:SoapAction />

  <udc:Query />

  </udc:SelectCommand>

<udc:UpdateCommand>

  <udc:ServiceUrl UseFormsServiceProxy="false">http://Romiko.com/Workflow/WorkflowService.asmx</udc:ServiceUrl>

  <udc:SoapAction>http://Romiko.WebServices/SetWorkflowRecord</udc:SoapAction>

  <udc:Submit />

  <udc:FileName>Specify a filename or formula</udc:FileName>

  <udc:FolderName AllowOverwrite="" />

  </udc:UpdateCommand>

<!–

udc:Authentication><udc:SSO AppId=” CredentialType=” /></udc:Authentication

  –>

  </udc:ConnectionInfo>

  </udc:DataSource>

 

This is all done in the InfoPath form designer and published via the designer as well.
These are basically udcx files published on SharePoint.

Stage 2

Now that you have defined the Data Connection Libraries, you can now design the look and feel.

A nice way to control access to the form, is to use views in the SAME template. Why?

SharePoint ALLOWS only ONE type of template per InfoPath Form Library. So if you need different views on the same data,
use ONE template with multiple views:

We can then access these views programmatically, based on business rules in the ASPX page we build J

In my case we have three views:

Default View

Read Only

Write

Programmatically Access Fields in View

You can also programtically access these fileds in your view, for example. When you retrieve the data, you
may need the primary key (TransactionID), so when you call the web service to update, you can then
send the XML data back in the ViewState and also have the TransactionID sent to the webservice, so it
knows what to update.

Another nice thing is you can make the textboxes readonly and programmatically insert the key in the box,
where the data is retrieved, so users cannot hack in and try edit records they do not have permission to!

Web Browser Compatible

Ensure that the form is Web Browser Compatible, in the Form Options set the options:

Notice to that the submit options are for a web service:

WSDL

InfoPath will automatically show all fields in the DataSource pane for the WSDL data, so it is easy to drag
the fields onto the form and then design!

Rules for switching views

You can switch views by linking buttons in the form to a custom rule, so need for custom code for the views.

If you are not familiar with InfoPath forms, there is more than enough material on the internet to get affiliated
with this.

Here are some links I liked:

http://msdn.microsoft.com/en-us/library/aa701078.aspx

 

ASPX Development

Before we get cracking you can read about the XmlFormView control here:

http://msdn.microsoft.com/en-us/library/microsoft.office.infopath.server.controls.xmlformview.aspx

Now, all you need to do is create a basic aspx page that will host the XmlFormView. Notice the xsnlocation is the
InfoPath forms document library on SharePoint!

 

Link Custom Page to XmlFormView Page

All you need to do now, is link your page where users can select data from a view (gridview, repeater etc) and then
the corresponding data is shown in a user friendly manner where they can edit/view the xml J

You can then add the XmlFormview you to your page in a panel or so:

<asp:Panel ID="PanelWorkFlowRecord" runat="server" Height="100%" Width="100%" CssClass="hidden">

<asp:DetailsView ID="dtvWorkflowRecord" runat="server" AutoGenerateRows="False">

<Fields>

<asp:BoundField DataField="TransactionId" HeaderText="TransactionId" SortExpression="TransactionId"/>

<asp:BoundField DataField="CsId" HeaderText="CS ID"/>

<asp:BoundField DataField="Stage" HeaderText="Stage"/>

<asp:BoundField DataField="Status" HeaderText="Status"/>

<asp:BoundField DataField="TransactionDate" HeaderText="Transaction Date" DataFormatString="{0:dd-MM-yyyy HH:mm:ss}"/>

<asp:BoundField DataField="ErrorType" HeaderText="ErrorType"/>

<asp:BoundField DataField="ErrorMessage" HeaderText="ErrorMessage"/>

<asp:BoundField DataField="XMLData" HeaderText="XMLData" Visible="False" />

</Fields>

</asp:DetailsView>

<asp:Button ID="ButtonUnLock" runat="server" OnClick="ButtonUnLock_Click" Text="Unlock" Enabled ="false" ToolTip="When someone is editing a record, this record is locked until it is unlocked or reset by the same person. Only the person that has locked the record can unlock it. After a certain amount of time the record is automatically unlocked."/>&nbsp;

<asp:Label ID="LabelUnLockMessage" runat="server" Font-Italic="True" Text="This record is locked by user" Visible="False"></asp:Label>&nbsp;

<asp:Button ID="ButtonReset" runat="server" Text="DEBUG reset xmlFormview" OnClick="ButtonReset_Click" />

<cc1:xmlformview id="XmlFormViewWorkFlowRecord" runat="server" height="100%" width="100%" xsnlocation="/WorkflowForms/Forms/template.xsn"></cc1:xmlformview>

</asp:Panel>

 

You will need a way to reset the XmlFormView, this can be called from GridView_RowCommand event, when
you want to select data based on a key (transactionid in my case) from a row in a gridview.

 

Here is an overview of the form integrated into a aspx page.

 

So basically you can select a record, which triggers the RowCommand and then do the magic!

private void WriteToInfoPathForm(string value, string xpath)

{

XmlFormViewWorkFlowRecord.Visible = true;

XmlFormViewWorkFlowRecord.Enabled = true;

XmlFormViewWorkFlowRecord.DataBind();

XPathNavigator xNavMain = XmlFormViewWorkFlowRecord.XmlForm.MainDataSource.CreateNavigator();

XmlNamespaceManager xNameSpace = new XmlNamespaceManager(new NameTable());

xNameSpace.AddNamespace("my", XmlFormViewWorkFlowRecord.XmlForm.NamespaceManager.LookupNamespace("my").ToString());

xNameSpace.AddNamespace("tns", XmlFormViewWorkFlowRecord.XmlForm.NamespaceManager.LookupNamespace("tns").ToString());

xNameSpace.AddNamespace("dfs", XmlFormViewWorkFlowRecord.XmlForm.NamespaceManager.LookupNamespace("dfs").ToString());

xNameSpace.AddNamespace("s1", XmlFormViewWorkFlowRecord.XmlForm.NamespaceManager.LookupNamespace("s1").ToString());

xNameSpace.AddNamespace("s2", XmlFormViewWorkFlowRecord.XmlForm.NamespaceManager.LookupNamespace("s2").ToString());

xNavMain.SelectSingleNode(xpath, xNameSpace).SetValue(value);

}

 

private void ResetXmlFormView()

{

WriteToInfoPathForm("", "/dfs:myFields/dfs:queryFields/tns:GetWorkflowRecordByTransactionId/tns:id");

}

 

Here is a sample gridview row command that’s writes the transactionid to the form, so the user can then
click the button to get the data:

 

protected void gdvWorkflowRecordHistory_RowCommand(object sender, GridViewCommandEventArgs e)

{

if (e.CommandName == "Select")

{

ResetXmlFormView();

int index = Convert.ToInt32(e.CommandArgument);

HighlightGridViewRow(gdvWorkflowRecordHistory, index);

int id = Convert.ToInt32(gdvWorkflowRecordHistory.Rows[index].Cells[0].Text);

dtvWorkflowRecord.DataSource = WorkflowServiceAgent.GetWorkflowRecordDetails(id);

bool tmpEnabled = XmlFormViewWorkFlowRecord.Enabled;

XmlFormViewWorkFlowRecord.Enabled = true;

dtvWorkflowRecord.DataBind();

WriteToInfoPathForm( id.ToString(), "/dfs:myFields/dfs:queryFields/tns:GetWorkflowRecordByTransactionId/tns:id");

XmlFormViewWorkFlowRecord.Enabled = tmpEnabled;

SetLockButtonStatus();//set edit/lock buttons and messages dependent on the selected row.

}

}

 

When users click the button to view the form, you can do a response.redirect:

protected void ButtonView_Click(object sender, EventArgs e)

{

if (gdvWorkflowRecordHistory != null)

{

int transactionIdFieldIndex = GetColumnIndexByHeaderText(gdvWorkflowRecordHistory, "TransactionId");

int csIdFieldIndex = GetColumnIndexByHeaderText(gdvWorkflowRecordHistory, "CS ID");

int statusIndex = GetColumnIndexByHeaderText(gdvWorkflowRecordHistory, "Status");

int RowIndex = getIndexOfSelectedWorkflowRecordHistoryRecord();

 

string transActionId = gdvWorkflowRecordHistory.Rows[RowIndex].Cells[transactionIdFieldIndex].Text;

string csid = gdvWorkflowRecordHistory.Rows[RowIndex].Cells[csIdFieldIndex].Text;

string status = gdvWorkflowRecordHistory.Rows[RowIndex].Cells[statusIndex].Text;

if (csid != "" && status != "" && transActionId != "")

{

Session.Add("InfoPathCsId", Convert.ToInt32(csid));

Session.Add("TransActionId", Convert.ToInt32(transActionId));

Session.Add("InfoPathReadOnly", true);

Response.Redirect("XMLFormView.aspx");

}

else

{

lblInfoMessage.Text = "There is no CS id, STATUS or TRANSACTION id, cannot open infopath";

lblInfoMessage.Visible = true;

}

}

}

 

You can also choose which view to write to, based on the status of the message, users can get a different
view:

WriteToInfoPathForm("false", "/dfs:myFields/my:FormReadOnly");

So we can now click the view button, and voila, the InfoPath on SharePoint gets the data:

SharePoint Server

The SharePoint server must have Info Path forms services enabled, and the feature must be activated.

Also you MUST enable the FormView is the SharePoint Central Administration pages of the MOSS server:

You need to enable Enterprise Features or have InfoPath Form Services Installed.

Also, you must ENABLE form view, this is needed to store the data from the form back to the server,
on post backs.

Summary

So basically your InfoPath form which is embedded in a Panel, will be seamlessly integrated into your
custom web application.

The infrastructure is:

  • SharePoint Server with Info Path Forms Server
  • IIS Server with custom web application
  • XmlFormView.aspx page
  • A Panel is a custom web form where you link the XmlFormView
  • Some events to loads the Form and run rules
  • Views are used to control access to the different forms options
  • Very little logic in the code

So, this article should give you some ideas on how to integrate InfoPath forms with existing web applications.

So if XSD/WSDL changes, you can just upload/publish a new template to SharePoint and your users will get the
latest form with new fields etc J

Thanks to a colleague, Leon Droog who did allot of the prototyping and development of this solution. It
can sometimes be a real pain to get things going, but once they going it is allot easier than anticipiated!

Advertisements
Uncategorized

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s