[Update November 2009] A follow up post with source code, demos and instructions for this approach is available here: http://fmuntean.wordpress.com/2009/11/27/infopath-task-forms-made-easy-part-2/
If you are using an InfoPath library there is a very easy and safe way to work with the xml InfoPath generated data.
You get and set data on the item is by using xml de/serialization as shown in the next snippet:
SPItem item = ....
YourInfoPathFormClass yourForm;
XmlSerializer serializer = new XmlSerializer(typeof(YourInfoPathFormClass));
using (Stream stream = item.File.OpenBinaryStream())
{
yourForm = serializer.Deserialize(stream) as YourInfoPathFormClass;
}
Now YourInfoPathFormClass is generated using the xsd utility from the InfoPath Form xsd files: xsd.exe YourForm.xsd /c /namespace:YourNameSpace
When it comes to the Workflow and InfoPath Task Forms, this easy and type safe way does not work. Microsoft decided to use a rather complicated and convoluted mechanism to pass data between Workflow and the InfoPath Task Form. So you find yourself building helper classes with lots of lines just to encapsulate the strings used inside the Task Form. For simple Task Forms this is not a big problem but it will be if your Workflow has many complicated Task Forms. Another problem that you will face is the inability to use complicated types inside Task Forms because the Extended Properties contains a flat list of key value pairs.
I would like to be able to use the first approach for Task Forms too. All I needed is for Microsoft to have passed the InfoPath xml data as an Extended Property and I would be able to de/serialize my Task Form data in xml format inside that Extended Property.
So again I put my "Reverse Engineer" Hat on and start looking at how Microsoft built the interface between InfoPath and SharePoint Workflow Task Forms.
Following are my findings:
A content type is used to differentiate between the OOB Task Forms and the InfoPath ones. A ItemMetadata xml file is used to transfer data between the InfoPath Form and SharePoint. The WrkTaskIP.aspx inside _Layouts is the page that contains the InfoPath XMLFormControl that displays the InfoPath Form and controls the data flow between the Task and SharePoint. For more info on InfoPath Tasks Forms read the following MSDN article: http://msdn.microsoft.com/en-us/library/ms497641.aspx?ppud=4
Having all this information now we can modify the way that we send and receive data from the InfoPath Task Forms:
1. Create a new Content Type that derives from the InfoPath Content Type and uses a different WrkTaskIP.aspx page.
2. Copy the WrkTaskIP.aspx page under the same name that you have used in the custom Content Type and modify the copy to implement the page from a custom class.
3. Create a Custom Class that derives from WrkTaskIPPage and add the following references: Microsoft.office.InfoPath.Server, Microsoft.Office.Workflow.Pages (you can found this in the _app_bin folder), Microsoft.SharePoint,System.Web, System.Xml
4. Now in the custom class we will need to override the following events using the OnInit method as shown in the following snippet:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
base.XmlFormControl.Initialize -= new EventHandler<InitializeEventArgs>(base.XmlFormControl_Initialize);
base.XmlFormControl.Initialize += new EventHandler<InitializeEventArgs>(this.XmlFormControl_InitializeNew);
base.XmlFormControl.Close -= new EventHandler(this.XmlFormControl_OnClose);
base.XmlFormControl.Close += new EventHandler(this.XmlFormControl_OnCloseNew);
}
5. You will be sending data to the InfoPath form in the InitializeNew and get data from the InfoPath form in the OnCloseNew methods.
6. Personally I chose to use "IPFormDataXML" as a key in the Extended Properties to pass data back and forth.
7. Make sure that you build everything as a SharePoint solution.
Now to work with the InfoPath Task Form all we need is to again generate the classes from the Task From xsd schema and de/serialize data in the Extended Properties (make sure that you now specify your content type inside the workflow.xml file for the TaskListContentTypeId attribute.
For more info drop me a line.
