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.
--andrew
Great Post Andrew.
ReplyDeleteGreat Help too !
Thanks for the pointer Andrew, I was playing around with the validating event with no joy & thought that due to not being able to force a postback on the Contact Selector it wasn't worth bothering trying the changed event until I came across your post.
ReplyDeleteDear Andrew
ReplyDeleteI hope you are well. You mentioned the following
Domain trust form
No digital certificate
The form throws an error if i do not put full trust and digital certificate. What´s the workround to solve this issue. I look forward to hearing from you. My email is rsvacchi@googlemail.com
Cheers
Rod
Hi Andrew,
ReplyDeleteYou might find this no code solution useful for next time....
Alana
http://www.myriadtech.com.au/blog/Alana/Lists/Posts/Post.aspx?ID=13
Thanks Alana, you're solution is a very elegant no-code option!
ReplyDeleteI modified this to clean it up a bit. I think as written it could add multiple errors to the stack, say by adding 2 contacts, then going back and adding a third. I also update the logic for detecting errors to avoid the empty catch.
ReplyDeletevoid ApprovingManager_Changed(object sender, XmlEventArgs e)
{
try
{
// get the count of managers
int managerCount = e.Site.SelectChildren(XPathNodeType.Element).Count;
// If there is an existing error, clear it out to prepare for an error condition
// and ensure that there is only one error on the stack
if (this.Errors.GetErrors(_approvingManagerErrorName).Length > 0)
{
this.Errors.Delete(_approvingManagerErrorName);
}
// Check for and react to too few or too many errors
if (managerCount > 1)
{
this.Errors.Add(e.Site, _approvingManagerErrorName, Properties.Resources.TooManyApprovingManagerError);
}
else if (managerCount < 1)
{
this.Errors.Add(e.Site, _approvingManagerErrorName, Properties.Resources.MissingApprovingManagerError);
}
}
catch (Exception ex)
{
ProcessException(ex);
}
}
Hi Andrew, the contact selector in Infopath 2010 is not working on the browser as in 2007 version. do you know if this is a bug? I tried everything you mentioned.
ReplyDeletethanks
atavi,
ReplyDeleteUnfortunately, I am unfamiliar with InfoPath 2010. I have recently changed jobs, and technology focus so I no longer am dealing with SharePoint on a day-to-day basis. The last version of SharePoint/InfoPath that I used regularly was 2007.
You might check this forum post on MSDN for more information regarding the Contact Selector in InfoPath 2010: http://social.msdn.microsoft.com/Forums/en-US/sharepointinfopath/thread/fb18b122-402a-457e-9779-c4c32432acaf/