Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Hosting InfoPath 2007 in a WinForms Application

0.00/5 (No votes)
16 Jan 2009 2  
Advanced aspects of hosting InfoPath 2007 in a WinForms Application

Introduction

InfoPath is a powerful template editor, which can be used to create and edit templates. Since version 2007, it can be hosted inside a Windows Forms application as well. If this feature is new to you, have a look here: Hosting the InfoPath 2007 Form Editing Environment in a Custom Windows Form Application. The purpose of this article is on the one hand side to share some experiences of a quite small documented feature of InfoPath 2007 and on the other hand side to introduce an alternative way of integrating InfoPath 2007 in WinForms application.

Comment: If you get problems running the executable, the reason could be that you 1. haven't installed InfoPath 2007, 2. didn't install the .NET features of Office (in the Office 2007 setup) or 3. need to save the InfoPath templates again (right click TravelRequest.xsn --> Design --> Save as and overwrite existing files).

Background

As I will try to point out later in more detail, the main disadvantage integrating and hosting InfoPath 2007 in a custom WinForms application depends on the fact that publishing as an "installable form template" requires to deploy the custom deployed application every time any of the form templates change. Usually creating and/or changing form templates happens independently from the development of a custom made application - may be even business users should get enabled to design and upload those form templates. Unfortunately, for this kind of integration I didn't find good documentation during my research.

Therefore I'd like to show another way integrating WinForms applications and InfoPath 2007, which enables developers to load InfoPath 2007 form templates from any kind of destination. Furthermore, I hope to show some related pitfalls and give a rough (not complete) feature overview which could help in "proof of concept" phases.

Of course, there are are a lot of different ways achieving the same flexibility (e.g. InfoPath and Sharepoint). However, I think smart clients offer in certain scenarios still nice features we cannot or do not want to implement with web technologies only.

Overview

This example will load, display, edit and change the views of an InfoPath form template. Furthermore we require a solution which allows us to load different kind of templates without requiring the reinstallation of the whole application. Therefore it should enable us to load form templates, which are published on a web server, etc., and present this template inside a WinForms application. This means that this solution will not require to publish a template as an "installable form template." Furthermore I will try to give some feedback of my experiences in terms of deployment, read only/edit modes, partial trusted forms and changing form content programmatically.

Creating a InfoPath Forms Template

First of all we need to create a sample InfoPath 2007 document. I included an appropriate file in the attached demo (Project: HostingInfopath2007). In order to create it ourselves it is required to open the "Travel request" document (sample from Microsoft shipped with InfoPath 2007). Your document should look like this:

Afterwards some small changes need to be done:

1. Security Level of InfoPath documents

The security level of the document needs to be changed. If you configure InfoPath to detect automatically the appropriate security levels and you are working inside a domain and the form does not access external resources, it will be set to 'Domain.' The security levels can be adjusted in the dialog Form Options (menu Tools --> Form Options --> Security and Trust). Following my experiences, I would recommend to change these settings to Restricted, if possible. The reason for that relates 1. to the way the software is supposed to be distributed and 2. to the security settings of the project. It is important to realize that projects which are deployed with ClickOnce require the project to activate CAS (code access security). This can be done e.g. on the Security Page on the Property pages of a Windows Forms Project. Furthermore we need to consider that the FormControl class in the InfoPath hosting environment require CAS set to Full Trust (FormControl Class). However, after some trials I came to the following conclusions:

You can run your forms in a partial trusted environment (CAS is activated) as long as you do not need them to access any external content (files, web services, etc.) or use VSTO functionality (code behind of the forms). If your forms are able to run in a partial trusted environment, you can deploy your application via ClickOnce.

If you use VSTO or need to access any external content in your InfoPath Forms, you can expect that the deployment of your application in a partial trusted environment will be very difficult or impossible. You should expect a lot of difficulties and think about, deploying your application in an unrestricted environment (e.g. via an installer, 'unrestricted' means that any CAS code segment gets ignored, i.e. different from 'Full Trust').

Comment: Please notice that the form in this example is deployed not as a form template. Therefore some different experiences could happen due to a different internal handling of the hosting environment.

InfoPathFormOptions.jpg

2. Offline InfoPath 2007 forms

If forms are deployed in a web environment, InfoPath is able to let users fill out those forms even if they are not connected (offline). This kind of functionality cannot be used in a hosted environment. A possible workaround could be, to save the form manually on the local hard disk and open it from this location in the hosted environment. Probably it is required to set in this case a different form template ID for the local version.

I made the experience that versioning InfoPath forms works much smoother, having the InfoPath offline options deactivated. Therefore we are able to load the form template from a web server (where the template needs to be published before) in our windows forms application. If we overwrite this published template with a newer version, the hosted InfoPath control realizes this automatically and will merge any old data (if possible) in the new template. If we activate the InfoPath offline functionality of the template, we will get an error message in our forms environment, which says, that InfoPath found another version on your local computer. Therefore it asks the user in a dialog which version should be used. Unfortunately this dialog does not appear in a forms hosted environment, but only if you load your form in InfoPath standalone (e.g. type in the URL of the template in your internet browser).

InfoPathFormOptions2.jpg

3. Read only /edit modes

In centralized systems where more than one user is able to work on the same form data, it is normal to support some kind of concurrency modes (mostly pessimistic with forms). Having said that, it seems to be straight forward to support read only and edit modes in an application. Unfortunately there is no "out of the box" support to mark a form in our hosted environment as read only. Therefore a small workaround is needed. It is possible to switch between different views (even in the code). A new feature of InfoPath 2007 allows to mark a view as read only. Therefore a new view can be created (task pane Views), the content of the original page can be copied (copy - paste) and in the View Properties dialog the view can be marked as read-only.

Comment: Notice that the naming of the read-only view should be uniform.

ViewsTaskPane.jpg
Task pane Views
ViewsPropertiesDialog.jpg
Dialog View Properties

4. Publishing

As already mentioned there are from my view two different ways how to load a InfoPath form template in a Forms hosted environment. First of all we can publish it as an "installable form template." This solution has the impact that after every change of the template, the application needs to get redistributed. Secondly, InfoPath offers the opportunity to publish it in various kind of network places (Sharepoint, Network folder, web server) or it can be saved on the local hard disk (done in this example). Anywhere the template was published, it can be read by the hosting environment.

Developing the InfoPath hosting environment

In the following part I'd like to explain some code segments of the attached example.

1. Create a new form template with or without existing form data

The following code example opens another form template with or without merging some existing form data. Therefore it can be used to create a new form or load existing form data. The first experience I'd like to share is about closing the form first of all before creating a new one. This was done because the FormControl doesn't mind if it is closed a couple of times without having anything loaded before. The other way around - creating/loading a new form while the FormControl has already loaded another one before, will throw an Exception, saying that is should be closed before.

Another interesting aspect is the usage of an IInitEventHandler instance. We will cover this one a little bit deeper while covering the details of changing views within a document.

Last but not least it should be mentioned that any form template can be loaded with the method NewFromFormTemplate of the FormControl class. The URL parameter can point to any kind of network/local source (including web references --> e.g. http:xxx). Furthermore it is possible to put any kind of stream as a second parameter, which contains the form data, the loaded template should be merged with. This functionality can be used to present/edit any kind of existing form data of one specific template. If you load existing form data with a newer version of the template - assuming that you overwrite your old template every time you do an update - it is important to test the resulting behaviour in the first place.

this.formControl.Close();

// Open / create a form
if (dataStream != null)
    formControl.NewFromFormTemplate(
        formUrlName.ToString(),
        dataStream,
        XmlFormOpenMode.Default);
else
    formControl.NewFromFormTemplate(
        formUrlName.ToString());

// Default view of InfoPath document could be the 
// readonly or another one
this.infoPathInitEventHandler.InitReadOnlyFlag();

RefreshView(UIStatesForm.DocumentReadMode);

2. Set field values programmatically

In order to set a field value programmatically, it is required to figure out the namespace of the form data in the XML document. This namespace is created by InfoPath behind the scenes and identifies one unique form template. You can find this information in InfoPath as well (Menu File --> Properties). The way I figured out the namespace in this examples is by a given prefix. Because in all of my form templates (seems to be standardized for any InfoPath form templates) the prefix is "my," I use the following method in order to get the namespace.

private static void FindInfoPathNamespace(
    System.Xml.XPath.XPathNavigator navigator2,
    out string namespaceName2,
    out bool foundNamespace2)
{
    namespaceName2 = string.Empty;
    foundNamespace2 = false;
    if (navigator2.MoveToFirstChild())
    {
        namespaceName2 = navigator2.LookupNamespace(@"xmlns:my");
        while (namespaceName2 == null || namespaceName2.Length <= 0)
        {
            if (!navigator2.MoveToNext())
                break;
            namespaceName2 = navigator2.LookupNamespace(@"my");
        };
    }
    if (namespaceName2 != null && namespaceName2.Length > 0)
        foundNamespace2 = true;
}

Therefore the value can be easily changed by calling the standard XPathNavigator methods. Changing the value in the document will immediately update the InfoPath views.

var navigator =
    formControl1.XmlForm.MainDataSource.CreateNavigator();

string namespaceName;
bool foundNamespace;
FindInfopathNamespace(
    navigator,
    out namespaceName,
    out foundNamespace);

var nav2 = navigator.Clone();
var found = nav2.MoveToFollowing(
    fieldName,
    namespaceName);
if (found)
    nav2.SetValue(fieldValue);

3. Switch between InfoPath view

Switching views is quite straight forward in the implementation of the FormControl class. It's possible to read information about all provided views through the ViewInfos property and change a view via its SwitchView function. It is very important to catch a COMException which is quite often thrown by the SwitchView function of InfoPath. This exception can be reproduced while switching the views a few times in a short period. In the following implementation I assume that a view was switched properly if no exception was thrown.

public void SwitchView()
{
    TestInitialization();

    try
    {
        ViewInfo readOnlyView = null;
        ViewInfo editView = null;
        foreach (ViewInfo view in this.formControl.XmlForm.ViewInfos)
        {
            if (string.Compare(view.Name.ToLower(
                    CultureInfo.CurrentCulture),
                    READONLY_VIEW_NAME,
                    StringComparison.CurrentCulture) == 0)
                readOnlyView = view;
            else editView = view;
        }

        var new_readonly = !this.readOnly;

        if (new_readonly)
            this.formControl.XmlForm.ViewInfos.SwitchView(readOnlyView);
        else this.formControl.XmlForm.ViewInfos.SwitchView(editView);

        // Switching view was successful
        this.readOnly = new_readonly;
    }
    catch (COMException)
    {
        // switching view was not successfull, do nothing
    }
}

As an alternative the interface IInitEventHandler could be implemented. This interface allows us getting notified if the view was switched or the form context was changed. In order to get this working I created a separate class (in the attached example an embedded class) which implements this interface. In the InitEventHandler method, the event InternalStartup can be handled where we are able to handle the ContextChanged and/or the ViewSwitched event. Do not try to handle the ContextChanged an/or ViewSwitched event directly in the constructor. This will throw an exception with the (very informative) message "InfoPath cannot create a new, blank form."...

The implementation of this interface should look similar like the following:

internal class InfoPathInitEventHandler : IInitEventHandler
{
    private InfoPathInitEventHandler() { }

    FormControl formControl;
    bool internalStartupDeclared = false;

    private InfoPathInitEventHandler(
        FormControl formControl)
    {
        this.formControl = formControl;
    }

    internal static InfoPathInitEventHandler CreateInstance(
        FormControl formControl)
    {
        return new InfoPathInitEventHandler(
                    formControl);
    }


    #region IInitEventHandler Members

    public void InitEventHandler(
        object sender,
        XmlForm xmlForm,
        out Microsoft.Office.Interop.InfoPath.XdReadOnlyViewMode
            viewsReadOnlyMode)
    {
        viewsReadOnlyMode =
            Microsoft.Office.Interop.InfoPath.XdReadOnlyViewMode.xdDefault;

        if (!internalStartupDeclared)
        {
            this.formControl.InternalStartup +=
                new FormControl.EventHandler<EventArgs>(formControl_InternalStartup);
            internalStartupDeclared = true;
        }
    }

    void formControl_InternalStartup(object sender, EventArgs e)
    {
        this.formControl.EventManager.FormEvents.ContextChanged +=
            new ContextChangedEventHandler(FormEvents_ContextChanged);
        this.formControl.EventManager.FormEvents.ViewSwitched +=
            new ViewSwitchedEventHandler(FormEvents_ViewSwitched);
    }

    #endregion

    void FormEvents_ViewSwitched(object sender, ViewSwitchedEventArgs e)
    {
    }

    void FormEvents_ContextChanged(
        object sender,
        ContextChangedEventArgs e)
    {
    }
}

Finally we need to notify our FormControl instance about this implementation. This can be done directly after the FormControl instance is initialized.

private void InitInfoPathInitEventhandler()
{
    this.infoPathInitEventHandler =
        InfoPathInitEventHandler.CreateInstance(
            this.formControl);

    this.formControl.SetInitEventHandler(
        this.infoPathInitEventHandler);
}

I'd like to mention another small detail about changing views. It's possible to figure out which view is the default view, i.e. the view which is presented as soon as we open a form template. The following code snippet checks if the default view is named "readonly."

private bool IsInitialReadOnly
{
    get
    {
        return
            (string.Compare(this.formControl.
                XmlForm.ViewInfos.Default.Name,
                READONLY_VIEW_NAME,
                true,
                CultureInfo.CurrentCulture) == 0);
    }
}

4. Validation of an InfoPath form template

InfoPath ships with very powerful validation controls. It is possible to return the amount and details of the validation errors to the hosting application.

public int ValidateInfoPath()
{
    TestInitialization();
    
    return this.formControl.XmlForm.Errors.Count;
}

5. Return InfoPath form data

The usual way of storing InfoPath data is by submitting it to different destinations (web service, e-mail, hosting environment). None of these options gives developers the flexibility to store them directly in a database or store the form data without having fulfilled all validation requirements. Lucky wise, the form data can be read from the form template manually. This form data (string) can be stored manually and streamed back to the form template while creating it (see 1).

public string FormData
{
    [EnvironmentPermissionAttribute(
        SecurityAction.LinkDemand)]
    get
    {
        TestInitialization();

        XmlForm xf = formControl.XmlForm;
        if (xf != null) return xf.MainDataSource.
            CreateNavigator().OuterXml;
        else return string.Empty;
    }
}

6. Using the InfoPath user control

Finally we are able to use the user control in the application. This is very straightforward - therefore I forbear from further explanations.

static private void RefreshSwitchViewName(
    InfoPathUserControl InfoPathUserCtrl,
    Button buttonSwitchView)
{
    buttonSwitchView.Text =
        "Switch view (Readonly is " +
        InfoPathUserCtrl.ReadOnly +
        ")";
}

static private void CreateDocument(
    InfoPathUserControl InfoPathUserCtrl,
    Button buttonSetFieldValue,
    Button buttonSwitchView,
    string InfoPathTemplate)
{
    InfoPathUserCtrl.CreateFormTemplate(
        new Uri(
            Path.Combine(
                Environment.CurrentDirectory,
                InfoPathTemplate)));

    buttonSetFieldValue.Enabled = true;
    buttonSwitchView.Enabled = true;
    RefreshSwitchViewName(
        InfoPathUserCtrl,
        buttonSwitchView);
}

static private void SetFieldValue(
    InfoPathUserControl InfoPathUserControl)
{
    InfoPathUserControl.SetFieldValue(
        "name", Environment.UserName);
}

static private void SwitchView(
    InfoPathUserControl InfoPathUserCtrl,
    Button buttonSwitchView)
{
    InfoPathUserCtrl.SwitchView(
        !InfoPathUserCtrl.ReadOnly);
    RefreshSwitchViewName(
        InfoPathUserCtrl,
        buttonSwitchView);
}

Conclusion

The ability to host InfoPath 2007 in a custom Windows Forms Application give developers a tool to integrate their own powerful template based form editor. This can be useful in various scenarios. The best documented scenario which publishes the form template as an installable version lacks the ability of a separation of concerns between the creation / versioning of form templates and the versioning of the customized application. In most cases it will be required to create or edit form templates more often than the whole host application. This requirement can be implemented successfully.

Drawback: Some additional custom code needs to be written in terms of creating, publishing and validating forms, etc.

In total in worked quite fine in an n-tier production WPF application, I was working on in the past.

History

Just published version 1.0

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here