Wednesday, September 1, 2010

Update InfoPath field value from SharePoint Workflow

The Problem

While developing a SharePoint workflow for an InfoPath form library I ran in to a couple of issues with writing values back to the submitted InfoPath form.  The form data that is stored in the form library is pure xml so it seemed reasonable to me that it was simply a matter of finding the node and updating the value.  Turns out, if you don’t take a couple of very specific steps while doing this you will complete hose up the InfoPath form schema and never be able to open the InfoPath form in Web enabled mode again.  The odd thing is you if you do mess up the schema you can still open it with InfoPath filler but we wanted to keep everything web based for this particular effort. 

The Solution

I wrote a few methods to update the InfoPath form correctly.  The primary method that we call from our code is UpdateInfoPathField. 

UpdateInfoPathField Method

public void UpdateInfoPathField(string nodeXPath, string nodeValue)
{

    SPFile file = workflowProperties.Item.File;
    MemoryStream inputStream = new MemoryStream(file.OpenBinary());
    XmlDocument ipDoc = new XmlDocument();
    ipDoc.Load(inputStream);

    // This was the first trick.  When you are reading the InfoPath form into an XMLDocument, it is very important to specify PreserveWhiteSpace = true
    ipDoc.PreserveWhitespace = true;

    inputStream.Close();
    inputStream.Dispose();
    XPathNavigator ipNav = ipDoc.CreateNavigator();
    
    // The BuildNamespaceManager dynamically builds a namespacemanager to use in XPathNavigator select statements.  
    XmlNamespaceManager nsManager = BuildNamespaceManager(ipNav);

    XPathNavigator nodeNav = ipNav.SelectSingleNode(nodeXPath, nsManager);
    
    // This is the second trick.  In my case the field I was populating is initially null when the form is created.  You have to remove the nil attribute or you will receive terrible errors.
    DeleteNil(nodeNav);

    nodeNav.SetValue(nodeValue);

    MemoryStream outputStream = new MemoryStream();
    ipDoc.Save(outputStream);
    file.SaveBinary(outputStream.ToArray());
    outputStream.Close();
    outputStream.Dispose();
}

BuildnamespaceManager Method

private XmlNamespaceManager BuildNamespaceManager(XPathNavigator ipFormNav)
{
    XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable());
    ipFormNav.MoveToFollowing(XPathNodeType.Element);
    foreach (KeyValuePair<string, string> ns in ipFormNav.GetNamespacesInScope(XmlNamespaceScope.All))
    {
        if (ns.Key == string.Empty)
            nsManager.AddNamespace("def", ns.Value);
        else
            nsManager.AddNamespace(ns.Key, ns.Value);
    }
    return nsManager;
}

DeleteNil Method

public void DeleteNil(XPathNavigator node)
{
    if (node.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance"))
        node.DeleteSelf();
}

No comments:

Post a Comment