Friday, September 21, 2007

Requiring the Contact Selector in InfoPath 2007

The contact selector control in InfoPath makes implementing a user/group lookup field quite easy. However, there are a few downsides. Ben Walters has done a very nice job of listing the upsides and downsides to using this control in his post, Contact Selector the Good and the Bad.

The one aspect of InfoPath (2003) that prevented company wide deployment at my employer was the need to have the InfoPath client in order to fill out a form. Now that Microsoft has implemented Forms Server and allowed the filling of forms via a web browser, many of my colleagues have been banging at my door for custom InfoPath solutions.

The requirement that I have been presented with was to use the Contact Selector control, to make it required, and to only allow one value. After reading the above post by Ben, which references some validation code posted in a comment on the InfoPath Team blog, it became apparent there was no easy solution for browser-based forms.

Below I offer my solution:
  • Domain trust form
  • No digital certificate

public void InternalStartup()
{
 EventManager.FormEvents.Loading +=
  new LoadingEventHandler(FormEvents_Loading);
 EventManager.XmlEvents["/my:myFields/my:contactSelector"].Changed +=
  new XmlChangedEventHandler(contactSelector_Changed);
}

public void FormEvents_Loading(object sender, LoadingEventArgs e)
{
 XPathNavigator mainDS = this.MainDataSource.CreateNavigator();
 if (this.New)
 {
  XPathNavigator contSel =
   mainDS.SelectSingleNode("/my:myFields/my:contactSelector",
    NamespaceManager);
  this.Errors.Add(contSel, "ContactSelectorError",
   "You must select a contact.");
  return;
 }
}

public void contactSelector_Changed(object sender, XmlEventArgs e)
{
 if (e.Site.SelectChildren(XPathNodeType.Element).Count == 1)
 {
  try
  {
   this.Errors.Delete("ContactSelectorError");
  }
  catch { }
 }

 if (e.Site.SelectChildren(XPathNodeType.Element).Count > 1)
  this.Errors.Add(e.Site, "ContactSelectorError",
   "Only one contact can be selected.");

 if (e.Site.SelectChildren(XPathNodeType.Element).Count < 1)
  this.Errors.Add(e.Site, "ContactSelectorError",
   "You must select a contact.");
}

Some notes:
  • "/my:myFields/my:contactSelector" is the XPath to the Contact Selector control. This is the main group, i.e. in my example, it would be:
    <contactSelector>
      <Person>
        <DisplayName />
        <AccountId />
        <AccountType />
      </Person>
    </contactSelector>
  • "ContactSelectorError" is the name of the error being added/deleted. It is not displayed to the user, but rather the internal error name for code references.
  • I only add the error in the loading event for new forms, since an existing form would already have these fields required. An alternative to this.New would be to test the Contact Selector control for a value, and add the error if it's value was equal to the empty string ("").
  • In the function "contactSelector_Changed", you must have a try/catch around the delete statement, because if you attempt to delete an error that does not exist, an error will be thrown.
  • If you have multiple contact selector controls on your form, you will need a seperate "onChanged" event for each control, and I would suggest simply changing the name of the error from "ContactSelectorError" to something like "ContactSelectorErrorEmployee", and ensure each contact selector control has a unique name for its error. You will also need as many "this.Errors.Add" lines in the loading event as you have contact selector controls you want validated.
Enjoy!
--andrew

Welcome!

Welcome to my professional blog. I am a software developer spending much of my time currently within Microsoft SharePoint. I have been doing SharePoint administration and development for a little over a year now, having started on SPS2003 (albeit, very little work in 2003) and now working within MOSS2007. I have created this blog in an effort to record various problems/issues/bugs/"features" that I come across while working.

I am not claiming that these are best practices or the only way to do things. If you have a better way to solve any of my problems, I would be more than happy to hear them.

Some things I anticipate to cover:
  • SharePoint (administration and development)
  • InfoPath 2007
  • Web Services
Several general disclaimers:
  • For all posts, whenever I refer to SharePoint, I am talking about MOSS2007. If I ever need to talk about SPS2003, which I doubt, I will explicitly call it by that name.
  • Please do not attempt to correct my posts based solely on grammar/spelling.
  • When appropriate, I will attempt to provide useful screen shots of the errors I receive and the steps I took to fix them.
  • [Reserved for future disclaimers]
Thanks, and enjoy!
--andrew