Sunday, August 20, 2006

Picture Buttons in InfoPath 2007

In InfoPath 2007 it is impossible to create buttons with images as background. But, you can adapt some InfoPath files by yourself to create those picture buttons. Follow my instructions:

1. Create a new InfoPath form template

2. Add a new button. (
The button has ‘button’ as standard label.The border is black and the background is gray.)

Remar
k
In InfoPath 2003 it was still possible to replace the label value with a space character to have a button without text. In InfoPath 2007 it is impossible to add a space character as label value. We replace the standard value with ‘myvalue’.


3. Go to Tools > Resource Files and Add an .gif file as a Resource file. Rename the file to ‘picture.gif’.

4. Save your form template to ‘test.xsn

5. Now, go to the location where you stored the form template and rename ‘test.xsn’ to ‘test.cab’.

6. Extract all files and open View1.xsl with notepad. This stylesheet file renders the xml data. The content is written in HTML en CSS. The XSL has the same relation like HTML & CSS has. HTML files contain content and CSS files contain style definition which can be applied to the content files (HTML files).

7. Navigate to the Style endtag.

8. Add a new CSS definition for the picture button

.imageButton
{
BACKGROUND-IMAGE: url(picture.gif);
BACKGROUND-REPEAT: no-repeat;
}


9. Navigate to the input tag in the body which defines our button and add the newly created CSS definition the class attribute.

... class="imageButton langFont" ...



10. InfoPath 2007 does not support an empty buttonlabel. But you can change the label manually in this file. So replace the ‘myValue’ in the value attribute with empty quotes.
... class="imageButton langFont" value="" ...



11. Save and close View1.xsl.

12. You can now reopen your form by right-clicking on manifest.xsf and select ‘Design’ in the menu.

13. The picture will be integrated in your view right now.

Greg Collins also wrote an article about Picture Buttons for InfoPath 2003.
My article is a supplement on his article.

Sunday, August 13, 2006

Switch view when drop down list has changed


It’s impossible to use the ViewInfos.SwitchView(string) method while the changed event is running. You can only switch a view by using the button clickhandler. This post will create a workaround to change view without clicking on buttons.

1. Using ViewInfos.SwitchView() method in the Changed event

While firing events it is impossible to switch to another view. Only the Click event on the button will permit a ‘SwitchView’ action. In all other cases you will receive a COMException at runtime.


public void InternalStartup()
{
EventManager.XmlEvents["/my:myFields/my:list"].Changed +=
new XmlChangedEventHandler(list_Changed);
}

public void field1_Changed(object sender, XmlEventArgs e)
{
ViewInfos.SwitchView("view2");
// The code is successfully compiled, but will not run !
// It results in a run-time error (COMException)
}

Redirecting to another view is not quite simple. We have to use other tricks


2. OnContextChange event is asynchronous

The OnContextChange event is fired when the form or elements on the forms has been changed. There is just a little problem the event is asynchronous. So, it does not fire on every change in the context node.

Instead, it fires the event when all other events has been terminated.We will use the OnContextChange event (which is only enabled if you created a form without browser-compatible features) to redirect to another view.Test the Asynchronous OnContextChange event:



public void InternalStartup()
{
EventManager.FormEvents.ContextChanged +=
new ContextChangedEventHandler(FormEvents_ContextChanged);
}

public void FormEvents_ContextChanged(object sender, ContextChangedEventArgs e)
{
System.Windows.Forms.MessageBox.Show("context changed");
}


While selecting several values in the drop down list, you will see that the OnContextChange event will not fire on every change in the context node.


More information:
Microsoft - OnContextChange event



3. Change the focus to force the OnContextChange event

We cannot trust on the OnContextChange event because it’s asynchronous. So we have to force the OnContextChange event by changing the focus from the drop down list to another field through code.

To change the focus we will use a textbox which must be visible and not read-only. You cannot set the focus to every control. More information :
http://blogs.msdn.com/infopath/archive/2004/04/07/109189.aspx

Following code will be executed while the ‘changed event’ of the drop down list has been fired.



public void list_Changed(object sender, XmlEventArgs e)
{
SetFocus("/my:myFields/my:unusedfield");
}

private void SetFocus(string xpath)
{
XPathNavigator fieldToFocus = MainDataSource.CreateNavigator().
SelectSingleNode(xpath,NamespaceManager);
this.CurrentView.SelectText(fieldToFocus);
}


Download the new InfoPath 2007 Object Model by this link:
http://download.microsoft.com/download/0/9/c/09cda3f2-6d3d-4082-aec5-9a62b7679ecf/Office2007InfoPathOMPoster.exe




4. Switch to another view

Now we can implement the OnContextChange method which allows to use the SwitchView() method.


public void FormEvents_ContextChanged(object sender, ContextChangedEventArgs e)
{
ViewInfos.SwitchView("view2");
}


The code is insufficient. The OnContextChange event will fire many times while filling in a form. It is not every time allowed to change the view. We need an extra variable which indicates if the SwitchView() method me be called.


private bool canRedirect = false;
public void FormEvents_ContextChanged(object sender, ContextChangedEventArgs e)
{
if (canRedirect)
{
canRedirect = false;
ViewInfos.SwitchView("view2");
}
}



5. Complete code


public partial class FormCode
{
public void InternalStartup()
{
EventManager.XmlEvents["/my:myFields/my:list"].Changed +=
new XmlChangedEventHandler(list_Changed);
EventManager.FormEvents.ContextChanged +=
new ContextChangedEventHandler(FormEvents_ContextChanged);
}


private bool canRedirect = false;
public void list_Changed(object sender, XmlEventArgs e)
{
canRedirect = true;
SetFocus("/my:myFields/my:unusedfield");
}

private void SetFocus(string xpath)
{
try
{
XPathNavigator fieldToFocus = MainDataSource.CreateNavigator().
SelectSingleNode(xpath,NamespaceManager);
this.CurrentView.SelectText(fieldToFocus);
}
catch (ArgumentException ex)
{
MessageBox.Show("The focus cannot be set to " + xpath);
}
}

public void FormEvents_ContextChanged(object sender, ContextChangedEventArgs e)
{
if (canRedirect)
{
canRedirect = false;
ViewInfos.SwitchView("view2");
}
}
}




6. Tips
You can hide the ‘canRedirect’ member by writing a wrapper class around this member


Tuesday, August 08, 2006

Hiding check boxes and DateTime controls

InfoPath still does not allowing hided checkboxes and DateTime controls.
In certain situations it’s recommended to hide those controls for the users.
The conditional formatting options are restricted and we can only disable them.

Use following trick to hide controls:

1. Create an empty group (for example: CheckTest) in the main datasource.
This group will act like a wrapper section for the Checkbox (which can not be invisible)


my:myFields
-> CheckTest





2. Add a Boolean field into 'CheckTest'
If you don't want to hide FieldA, you can add this field directly below my:myFields.


my:myFields
-> CheckTest
-> FieldA


3. Use ‘CheckTest’ as Section on the screen.

4. Use conditional formatting to hide this section when the user may not see the check boxes.
Perhaps you need another field to base your conditional formatting on.
For example: if FieldB is empty then 'Hide'.


Tip: You can base your conditional rule on another field which is calculated by customized code or directly based on existing fields or expressions in your form.

For a detailed example please read Greg Collins' article on InfoPathdev.com