I encountered a situation at a client that had a requirement for cloning an InfoPath form by the click of a button. They wanted the ability to kick off a new work item and retain part of the forms information. The reason being that they might fill out anywhere from 1 – 200 form with some of the information always remaining the same for that particular instance. One thing to note, the InfoPath form was browser-enabled. I was able to design a solution using code-behind on the button click event on the form.
Blank Slate
As I mentioned earlier, one of the requirements was to retain a “portion” of the form. In order to do so, I needed to get a blank copy of the form’s XML structure before it’s loaded. I obtained this by adding code to the FormEvents_Loading method of the InfoPath form. Additionally, I was working with a large portion of views, so I needed to make sure I retained the XML throughout the process and also that it was not overwritten. To achieve this, I added the following nodes to the InfoPath form’s data source:
| emptyFormXml is used for storing the form’s empty xml structure. |
| formHasLoaded is used to check if the form has loaded for the first time or not. |
After creating these nodes, I added the following code to the FormEvents_Loading method:
1 | <span style="color:#606060" id="lnum1"> 1:</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> FormEvents_Loading(<span style="color:#0000ff">object</span> sender, LoadingEventArgs e) |
1 | <span style="color:#606060" id="lnum2"> 2:</span> { |
1 | <span style="color:#606060" id="lnum3"> 3:</span> XPathNavigator formNav = MainDataSource.CreateNavigator(); |
1 | <span style="color:#606060" id="lnum4"> 4:</span> <span style="color:#0000ff">if</span> (formNav.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:Support/my:formHasLoaded"</span>, NamespaceManager).Value == <span style="color:#006080">"no"</span>) |
1 | <span style="color:#606060" id="lnum5"> 5:</span> { |
1 | <span style="color:#606060" id="lnum6"> 6:</span> XPathNavigator navBlankXML = MainDataSource.CreateNavigator(); |
1 | <span style="color:#606060" id="lnum7"> 7:</span> XmlDocument blankXML = <span style="color:#0000ff">new</span> XmlDocument(); |
1 | <span style="color:#606060" id="lnum8"> 8:</span> navBlankXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:Support/my:emptyFormXml"</span>, NamespaceManager).SetValue(navBlankXML.OuterXml.ToString()); |
1 | <span style="color:#606060" id="lnum9"> 9:</span> navBlankXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:Support/my:formHasLoaded"</span>, NamespaceManager).SetValue(<span style="color:#006080">"yes"</span>); |
1 | <span style="color:#606060" id="lnum10"> 10:</span> } |
1 | <span style="color:#606060" id="lnum11"> 11:</span> } |
The purpose of this code is to check to see if this is the first time the form has loaded (formHasLoaded node). If this is the first time, then you need to grab the OuterXml by creating a XPathNavigator from the form’s MainDataSource. Then you store the OuterXml into the emptyFormXml field for later use and set the value of the formHasLoaded field to “yes”.
Now that you have captured the empty XML, you are ready to move to the next step in the process.
InfoPathService Web Service
In order to be able to submit the form into a new process, you will need to add a web reference to your project. The web URL you will need to add is: http://SERVERNAME:81/RuntimeServices/InfoPathService.asmx. The reason your adding this web reference is to be able to access the SubmitInfoPathData method. This method will submit the data of the InfoPath form to the workflow server to either start a new workflow or action a worklist item (task). For my example, I called it InfoPathWebService_Dev.
Submit Clone
Now for the “meat” of the solution! I added a button to the form called btnSubmitClone, went to the button’s properties and selected Edit Form Code. In the first part of the code, I grabbed the fields I wanted to keep from the form and stored them in a variable accordingly.
The next step is to obtain the empty form xml that we captured earlier (emptyFormXml field). Once we retrieve that value, load it into a XmlDocument and create an InfoPath navigator off of the XML document. With the new Navigator, set the values you want to display on the new form into the new XML. One thing to take into consideration is that if the field you are trying to pass in is not a string data type, you will receive an error if you don’t delete the “nil” attribute. In order to do so, call the following method (DeleteNil) to remove the attribute:
1 | <span style="color:#606060" id="lnum1"> 1:</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> DeleteNil(XPathNavigator node) |
1 | <span style="color:#606060" id="lnum2"> 2:</span> { |
1 | <span style="color:#606060" id="lnum3"> 3:</span> <span style="color:#0000ff">if</span> (node.MoveToAttribute(<span style="color:#006080">"nil"</span>, <span style="color:#006080">"http://www.w3.org/2001/XMLSchema-instance"</span>)) |
1 | <span style="color:#606060" id="lnum4"> 4:</span> node.DeleteSelf(); |
1 | <span style="color:#606060" id="lnum5"> 5:</span> } |
Now for the IMPORTANT step in the process. Set the value of the K2 serial number (/my:myFields/my:_K2/my:SN) to string.Empty. If you don’t clear the SN value, it will try to use the existing workitem’s number and will cause an error when referencing the cloned workitem.
For my K2 workflow process, I check at the beginning of the process for a cloned form or not. In order to do this, I also created an additional field on the InfoPath form called cloneForm with a default value of “no”. Therefore, if the value is no, the process is handled normally, else, the user has to complete the form with the additional info needed after it has been cloned and then it resumes the normal process.
I set the value of this cloneForm field at this point in the process to mark the document as cloned. This only is applied with the new blank xml form I’m building, and will not change the existing form’s field value.
The final step in the cloning process is to load the navigator’s OuterXml into a new XML document and instantiate the web service(InfoPathService) that I had referenced earlier. I then call the SubmitInfoPath method to pass the new XML document I had cloned. In order for this to submit successfully, it NEEDS to be in the following format:
1 | <span style="color:#606060" id="lnum1"> 1:</span> ipService.SubmitInfoPathData(<span style="color:#0000ff">new</span> XmlText[] { xmlDoc2.CreateTextNode(xmlDoc2.InnerXml) }); |
At this point, you are done with the code. Obviously, you don’t have to set any values and just submit the XML empty to have a new blank form, but it comes in handy if you want to manipulate values. Also, you can add logic to let the user choose how many clones and then loops through the process of submitting the form the number of times the user has chosen.
For a complete example of the btnSubmitClone_Clicked event, see below:
1 | <span style="color:#606060" id="lnum1"> 1:</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> btnSubmitClone_Clicked(<span style="color:#0000ff">object</span> sender, ClickedEventArgs e) |
1 | <span style="color:#606060" id="lnum2"> 2:</span> { |
1 | <span style="color:#606060" id="lnum3"> 3:</span> <span style="color:#0000ff">try</span> |
1 | <span style="color:#606060" id="lnum4"> 4:</span> { |
1 | <span style="color:#606060" id="lnum5"> 5:</span> <span style="color:#008000">//get info to retain from form</span> |
1 | <span style="color:#606060" id="lnum6"> 6:</span> <span style="color:#0000ff">string</span> s_Name = e.Source.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:name"</span>, NamespaceManager).Value; |
1 | <span style="color:#606060" id="lnum7"> 7:</span> <span style="color:#0000ff">string</span> s_Address = e.Source.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:address"</span>, NamespaceManager).Value; |
1 | <span style="color:#606060" id="lnum8"> 8:</span> <span style="color:#0000ff">string</span> i_Zip = e.Source.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:zip"</span>, NamespaceManager).Value; |
1 | <span style="color:#606060" id="lnum9"> 9:</span> |
1 | <span style="color:#606060" id="lnum10"> 10:</span> <span style="color:#008000">//get empty form xml & load the xml</span> |
1 | <span style="color:#606060" id="lnum11"> 11:</span> <span style="color:#0000ff">string</span> cachedFullXml = e.Source.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:Support/my:emptyFormXml"</span>, NamespaceManager).Value; |
1 | <span style="color:#606060" id="lnum12"> 12:</span> XmlDocument formXDoc = <span style="color:#0000ff">new</span> XmlDocument(); |
1 | <span style="color:#606060" id="lnum13"> 13:</span> formXDoc.LoadXml(cachedFullXml); |
1 | <span style="color:#606060" id="lnum14"> 14:</span> XPathNavigator navXML = formXDoc.CreateNavigator(); |
1 | <span style="color:#606060" id="lnum15"> 15:</span> |
1 | <span style="color:#606060" id="lnum16"> 16:</span> <span style="color:#008000">//set info to retain into empty xml</span> |
1 | <span style="color:#606060" id="lnum17"> 17:</span> navXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:name"</span>, NamespaceManager).SetValue(s_Name); |
1 | <span style="color:#606060" id="lnum18"> 18:</span> navXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:address"</span>, NamespaceManager).SetValue(s_Address); |
1 | <span style="color:#606060" id="lnum19"> 19:</span> |
1 | <span style="color:#606060" id="lnum20"> 20:</span> <span style="color:#008000">//integer field</span> |
1 | <span style="color:#606060" id="lnum21"> 21:</span> XPathNavigator xndateField = navXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:zip"</span>, <span style="color:#0000ff">this</span>.NamespaceManager); |
1 | <span style="color:#606060" id="lnum22"> 22:</span> DeleteNil(xndateField); |
1 | <span style="color:#606060" id="lnum23"> 23:</span> navXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:zip"</span>, NamespaceManager).SetValue(i_Zip); |
1 | <span style="color:#606060" id="lnum24"> 24:</span> |
1 | <span style="color:#606060" id="lnum25"> 25:</span> <span style="color:#008000">//set form submission values</span> |
1 | <span style="color:#606060" id="lnum26"> 26:</span> navXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:_K2/my:SN"</span>, NamespaceManager).SetValue(<span style="color:#0000ff">string</span>.Empty); <span style="color:#008000">//clear K2 Serial Number</span> |
1 | <span style="color:#606060" id="lnum27"> 27:</span> navXML.SelectSingleNode(<span style="color:#006080">"/my:myFields/my:Support/my:cloneForm"</span>, NamespaceManager).SetValue(<span style="color:#006080">"yes"</span>); <span style="color:#008000">//field to mark item as cloned form</span> |
1 | <span style="color:#606060" id="lnum28"> 28:</span> |
1 | <span style="color:#606060" id="lnum29"> 29:</span> <span style="color:#008000">//submit xml as new workflow item</span> |
1 | <span style="color:#606060" id="lnum30"> 30:</span> XmlDocument xmlDoc2 = <span style="color:#0000ff">new</span> XmlDocument(); |
1 | <span style="color:#606060" id="lnum31"> 31:</span> xmlDoc2.LoadXml(navXML.OuterXml.ToString()); |
1 | <span style="color:#606060" id="lnum32"> 32:</span> InfoPathWebService_Dev.InfoPathService ipService = <span style="color:#0000ff">new</span> rtoq2.InfoPathWebService_Dev.InfoPathService(); |
1 | <span style="color:#606060" id="lnum33"> 33:</span> ipService.Credentials = System.Net.CredentialCache.DefaultCredentials; |
1 | <span style="color:#606060" id="lnum34"> 34:</span> ipService.SubmitInfoPathData(<span style="color:#0000ff">new</span> XmlText[] { xmlDoc2.CreateTextNode(xmlDoc2.InnerXml) }); |
1 | <span style="color:#606060" id="lnum35"> 35:</span> ipService.Dispose(); |
1 | <span style="color:#606060" id="lnum36"> 36:</span> |
1 | <span style="color:#606060" id="lnum37"> 37:</span> } |
1 | <span style="color:#606060" id="lnum38"> 38:</span> <span style="color:#0000ff">catch</span> (Exception ex) |
1 | <span style="color:#606060" id="lnum39"> 39:</span> { |
1 | <span style="color:#606060" id="lnum40"> 40:</span> <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> System.Exception(ex.Message); |
1 | <span style="color:#606060" id="lnum41"> 41:</span> } |
1 | <span style="color:#606060" id="lnum42"> 42:</span> } |
Happy Cloning!!