Customizing XForms with CSS

To date, one of the things that I can generally say about the XForms examples that I have been producing is that they are, well, kind of dull. Not necessarily topically - I've found in general that when I give XForms demonstrations to people, they tend to be fairly ho-hum about it until I actually begin to show the power of such schema driven development ... then their jaws drop. However, as with video games, while it may be the game play fundamentals (good story, good interface, good tech) that keeps people playing, its the artwork (usually of extraordinarily endowed CGI women, for some bizarre reason) that makes them buy the game in the first place. Neglecting presentation in XForms won't kill your app, but you can do so much more with it if you dress it up a bit.

You modify the presentation of Mozilla XForms in exactly the same way that you modify the presentation in XHTML - via the use of Cascading Stylesheets, or CSS. However, to do so, you need to know a little bit about namespace directives. Normally CSS applies to the elements in the default namespace - XHTML, typically, and there's no real reason to be concerned with such distinctions. However, in the case of XForms (and related standards, such as SVG), you will likely end up with more than one namespace in a document at any given time. For that reason, the CSS engine needs to have some means of distinguishing between namespaces. Enter the namespace directive:

<style type="text/css"><![CDATA[
            @namespace xf url('http://www.w3.org/2002/xforms');
            ]]><style>

This directive must be at the beginning of any style block if alternative namespaces are employed, and you should have one for each namespace that will be affected by CSS.

Because the colon character(":") already has an explicit meaning with CSS (it's the pseudoclass designator) you can't also use it to designate namespaces. For that reason, this character is replaced by the pipe ("|") character. For instance, suppose that you wished to set an xf:output element so that it is displayed within a box, you could do so as follows:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:d="http://www.mydata.com/xmlns/data">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
@namespace xf url('http://www.w3.org/2002/xforms');
xf|output  {font-size:24pt;color:blue;border:inset 2px buttonface}
]]></style>
    </head>
    <body>
        <xf:model>
            <xf:instance>
                <d:data value="0"/>
            </xf:instance>
        </xf:model>
        <xf:trigger>
            <xf:label>-</xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="/d:data/@value" value="/d:data/@value - 1"/>
            </xf:action>
        </xf:trigger>
        <xf:output ref="@value"/>
        <xf:trigger>
            <xf:label>+</xf:label>
            <xf:action ev:event="DOMActivate">
                <xf:setvalue ref="/d:data/@value" value="/d:data/@value + 1"/>
            </xf:action>
        </xf:trigger>
    </body>
</html>
        

(Note: I haven't yet covered triggers, but you can think of them as being buttons, though they are far more than that. I'll cover them some both in this column and in my next, on events).

It's worth paying special attention to the <style> block:

<style type="text/css"><![CDATA[
            @namespace xf url('http://www.w3.org/2002/xforms');
            xf|output  {font-size:24pt;color:blue;border:inset 2px buttonface}
            ]]></style>
        

The first line declares the xf namespace and associates it with the XForms namespace URI. The second then uses the notation xf|output to designate any <xf:output> tags in the document, assigning the CSS properties changing the size, color and border of the container as indicated. You can see the result of this in Figure 1.

Modifying the Output presentation
Figure 1. Modifying the Output presentation

You're not just limited to modifying the output. Any XForms element can be so modified. For instance, the buttons as rendered here are considerably smaller than the text. If you wanted to make them comparably sized, you COULD set the font-size of the xf:trigger element, but in this particular case (after a bit of experimentation) it turns out that you would set it onto the label, as illustrated in Figure 2:

<style type="text/css"><![CDATA[
            @namespace xf url('http://www.w3.org/2002/xforms');
            xf|output  {font-size:16pt;color:blue;border:inset 2px buttonface}
            xf|trigger xf|label {font-size:16pt;font-weight:bold;}            
            ]]></style>
Resized Triggers
Figure 2. Resized Triggers

For most XForms elements, there is actually an internal distinction between the overall form component, the value portion (such as the textbox of a input control) and the label portion. Each of these can thus be set independently. For instance, if you had an element set of the form:

<xf:input ref="@value">
            <xf:label>Roller: </xf:label>
            </xf:input>

you can then set each of the three pieces - overall object (red), value control (blue) and label (green), as follows (with the results shown in Figure 3:

<style type="text/css"><![CDATA[
            @namespace xf url('http://www.w3.org/2002/xforms');
            xf|input {border:solid 1px red;padding:5px}
            xf|input xf|label {border:solid 1px green;padding:3px}
            xf|input .xf-value {border:solid 1px blue;padding:3px;}
            ]]></style>
Illustrating the parts of a control
Figure 3. Illustrating the parts of a control.

Note the use of the odd class .xf-value. The W3C XForms specification defines the pseudoclass :value for modifying only the value portion of an image, but changing that within Mozilla apparently is fairly complicated. For this reason (at least at this point), they create as part of the component definitions the .xf-value class for working specifically with this sub-component.

This in turn can solve one of the more aggrevating visual problems with generic XForms - getting out of the ugly label syndrome. For instance, consider something like a tax form. This usually does not have labels located in front of the boxes, but instead has the labels within the fields. Now, with XForms (or for that matter, with ordinary HTML), this is a fairly complicated thing to duplicate, yet obviously there's some advantage to being able to maintain visual fidelity between a tax form on the screen and one printed out. Fortunately, you can use the combination of relative and absolute displays to make this happen quite easily within an XForms context:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:d="http://www.mydata.com/xmlns/data">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
    @namespace xf url('http://www.w3.org/2002/xforms');
    xf|input {position:relative;}
    xf|input .xf-value {padding-top:7px;} 
    xf|input xf|label {position:absolute;top:-7px;left:2px;font-size:6pt;font-family:Arial;}
    ]]></style>
    </head>
    <body>
        <xf:model>
            <xf:instance>
                <d:data firstname="Kurt" surname="Cagle" middlename="Alan"/>
            </xf:instance>
        </xf:model>
        <xf:input ref="@firstname">
            <xf:label>First Name</xf:label>
        </xf:input>
        <xf:input ref="@middlename">
            <xf:label>Middle Name</xf:label>
        </xf:input>
        <xf:input ref="@surname">
            <xf:label>Surname</xf:label>
        </xf:input>
    </body>
</html>
        

with the result being shown in Figure 4.

Embedded Labels
Figure 4. Embedded Labels.

What makes this even cooler to me is the fact that because it is CSS, it is completely transparent to the XForms implementation itself ... the presentation doesn't pollute the data-flow model at all. Moreover, CSS can also be defined to extremely high precision. You could create the same kind of output, specifying border widths and the like in mm (or tenths of mm); even if the browser doesn't necessarily display the content this way, when printed you should get that precision. Mind, I'm not necessarily advocating this as a way of creating precise print forms - Mozilla's print rendering engine is still a work in progress and I see XSL-FO being better for this - but it's not difficult to see this eventually being a perfectly valid print solution.

CSS and Data Binding

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:d="http://www.mydata.com/xmlns/data">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
    @namespace xf url('http://www.w3.org/2002/xforms');
    xf|input:invalid .xf-value {background-color:red;}            
    ]]></style>
    </head>
    <body>
        <xf:model>
            <xf:instance>
                <d:data value="1"/>
            </xf:instance>
            <xf:bind nodeset="@value" constraint="number(.) &lt; 10"/>
        </xf:model>
        <xf:input ref="@value" incremental="true">
            <xf:label>Value</xf:label>
        </xf:input>
    </body>
</html>
        
Invalid Field
Figure 5. Invalid Field

Similarly, you can use either the :optional or :required pseudo-classes in conjunction with the required binding in order to specify that a given element is either optional or required. In the following example, you have a form which specifies both the value and the coordinate system for a given length. If the value for that length is zero, then the units are optional (white), while otherwise that are light blue.

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:d="http://www.mydata.com/xmlns/data">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
    @namespace xf url('http://www.w3.org/2002/xforms');
    xf|input:required .xf-value {background-color:lightBlue;}
    xf|input:optional .xf-value {background-color:white;}                        
    ]]></style>
    </head>
    <body>
        <xf:model>
            <xf:instance>
                <d:data value="1" units="mm"/>
            </xf:instance>
            <xf:bind nodeset="@units" required="../@value != 0"/>
        </xf:model>
        <xf:input ref="@value" incremental="true">
            <xf:label>Value</xf:label>
        </xf:input>
        <xf:input ref="@units" incremental="true">
            <xf:label>Units</xf:label>
        </xf:input>
    </body>
</html>
        
Optional Field
Figure 6. Optional Field

Bindings can play a fairly significant role in presentation. For instance, one of the more frequently encountered "specialized widgets" in forms is a means of specifying a date. The Mozilla XForms implementation actually provides a very sophisticated pop-up calendar for giving dates, one that requires comparatively little effort to implement. You can set up an xf:bind element specifically associating a certain schema type binding, in this case xs:date, in order to let the system know that the element in question is in fact a date (and as such can use the date picker).

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:d="http://www.mydata.com/xmlns/data">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
    @namespace xf url('http://www.w3.org/2002/xforms');
    #hiddenInput .xf-value {display:none;}            
    ]]></style>
    </head>
    <body>
        <xf:model>
            <xf:instance>
                <d:data date="2006-06-01"/>
            </xf:instance>
            <xf:bind nodeset="@date" type="xs:date"/>
        </xf:model>
        <xf:input ref="@date" incremental="true">
            <xf:label>Date (Standard Format): </xf:label>
        </xf:input>
        <br/>
        <xf:input ref="@date" incremental="true" id="hiddenInput">
            <xf:label>Date (Specialized Format): </xf:label>
        </xf:input>
        <xf:output
            value="concat(substring(@date,6,2),'/',substring(@date,9,2),'/',substring(@date,1,4))"
        />
    </body>
</html>
        
Date Picker
Figure 7. Date Picker

The normal date picker function will automatically place a new date into a bound input field (shown in the first line). However, in some cases, you may want to be able to have a date displayed in a different format. In that particular case you can hide the input field (by setting its .xf-value's CSS display property to "display:none") and use an output field with a calculation to change the date configuration (second line). Note that you can also create considerably more complex date expressions by using XBL bindings, to be covered later in this article.

Customization via Bindings

It's worthwhile understanding the real meaning of abstraction in XForms components. In essence, the idea behind XForms is that while the XForms systems does in general define a default visual look and feel for a component, the real power of the component comes in its programmatic interfaces, which do not have any specific requirements on the visual rendering. What this means is that if you have a binding language (such as the Mozilla XBL language, the currently in-progress W3C XBL2 specification), or some form of AJAX binding, you can actually customize the component to handle the presentation that you want.

For instance, one of the more frequently required "presentations" is the need to display images. You may have noticed that there was no specific "image" component in XForms in the discussions, largely because, well, there aren't any in the language, but you can in fact "fake it" with a custom binding on the <xf:output> element.

To do so, you need first of all to associate with the element in question an XBL binding via CSS. I've discussed bindings in these pages before, so won't get into details about the specific binding process beyond what's needed to understand the specific bindings.

In this case, one or the more useful techniques (and the one that comes closest to being "approved" within the XForms specification) is to set the appearance attribute to your desired view. For instance, you can set the appearance to "image". Normally, this will have no effect, but if you set up a CSS selector to specifically be looking for such an attribute value, then you can associate a binding with it as follows:

<html xmlns="http://www.w3.org/1999/xhtml" 
            xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:d="http://www.metaphoricalweb.net/xmlns/data">
    <head>
        <title></title>
        <style type="text/css"><![CDATA[
@namespace xf url("http://www.w3.org/2002/xforms");
xf|output[appearance="image"] {
            -moz-binding:url( 'XFormsBindings1.xml#output-image');
            }            
    ]]></style>
        <xf:model id="mod1">
            <xf:instance>
                <d:data src="file:///c:/publishing/styles/images/jennie.jpg" />
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <xf:output appearance='image' ref="@src"/>
    </body>
</html>

In practice, what this does is take /d:data/@src from the model and updates the internal <xf:output> element data model. When this happens, internally this element calls the refresh() method on itself, something that can be picked up by creating an alternative refresh() method via a binding. This is essentially what happens within the binding (XFormsBindings1.xml#output-image) itself:

<xbl:bindings xmlns:xbl="http://www.mozilla.org/xbl"
    xmlns:h="http://www.w3.org/1999/xhtml">
    <xbl:binding id="output-image" 
        extends="chrome://xforms/content/xforms.xml#xformswidget-base">
        <xbl:content>
            <h:span>
                <h:img src="" anonid="img" xbl:inherits="style class"/>
                <xbl:children/>
            </h:span>            
        </xbl:content>
        <xbl:implementation implements="nsIXFormsUIWidget">
            <xbl:method name="refresh">
                <xbl:body><![CDATA[
            var img = document.getAnonymousElementByAttribute(this, "anonid", "img");
            img.setAttribute("src", this.stringValue);
            return true;
        ]]></xbl:body>
            </xbl:method>
        </xbl:implementation>
    </xbl:binding>
    </xbl:bindings>

There are several points to note within this particular binding. The first is the use of the extends attribute within the xbl:binding declaration. This notifies the XForms system that it should use the "chrome://xforms/content/xforms.xml@xformswidget-base" base binding (and if you're curious, you can type "chrome://xforms/content/xforms.xml" to see the FULL bindings used for XForms, which is definitely worth taking the time to do - these give the various virtual function bindings, showing which can and should be overridden.

Note: I'm specifically not showing this content, as it will almost certainly change as the XForms system is completed.

The xbl:content block in this particular case is simple enough - an HTML <img> element with an anonid instead of a regular id and an empty src attribute. The <xbl:children/> element exists to pass any children of the output element (such as label elements) back into the environment. This shouldn't affect the actual text content of the element, which is part of the XForms shadow content (and which isn't displayed now unless an effort is made to do so.

The refresh method here gets the image element defined in the shadow content, then uses the stringValue property, which is inherited from the nsIXFormsUIWidget. Any custom component that specifically exposes both that object and its implementation should expose both the refresh() method and have the stringValue property defined for it. The refresh() method is called every time that the XForms model is refreshed, which means that if the model (and specifically the @src) property changes, then the corresponding image will automatically get refreshed.

This is definitely overkill in the simple example given, but its not hard to put together an example where the utility of defining an interface in this manner makes sense. For instance, you can use the image-output object in conjunction with a select1 element in order to create a very simple slide-show viewer:

    <html 
        xmlns="http://www.w3.org/1999/xhtml" 
        xmlns:xf="http://www.w3.org/2002/xforms"
        xmlns:d="http://www.metaphoricalweb.net/xmlns/data" 
        xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
@namespace xf url("http://www.w3.org/2002/xforms");
xf|output[appearance="image"] {-moz-binding:url('XFormsBindings1.xml#output-image');}
xf|select1 xf|itemset {width:200px;}
    ]]></style>
        <xf:model id="main_model">
            <xf:instance id="image">
                <d:data src="Deep Space 2 - 1024x768.jpg">
                    <d:imageset title="Space Images"
                        primary="file:///c:/Publishing/styles/images/main/"
                        thumbnails="file:///c:/Publishing/styles/images/main/">
                        <d:image filename="Deep Space 2 - 1024x768.jpg"/>
                        <d:image filename="HORSEHEA.jpg"/>
                        <d:image filename="Hubble.jpg"/>
                        <d:image filename="M16wf2.jpg"/>
                        <d:image filename="prominence.jpg"/>
                        <d:image filename="pvorion.jpg"/>
                        <d:image filename="Solar Whirl - 1024x768.jpg"/>
                    </d:imageset>
                </d:data>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <xf:select1 ref="@src">
            <xf:itemset nodeset="/d:data/d:imageset/d:image" 
                model="main_model">
                <xf:label ref="@filename"/>
                <xf:value ref="@filename"/>
            </xf:itemset>
        </xf:select1><br/>
        <xf:output value="concat(/d:data/d:imageset/@primary,@src)" 
            appearance="image" align="top"/>
    </body>
</html>
        

An example of this is illustrated in Figure 8 (with images taken from the Hubble telescope):

Note that you can also construct more complex controls in this way. For instance, while the imageset element tends to limit you to a label/value pair, you can also use the XForms <repeat> capability in order to create a purely graphical setup engine, where you can select from thumbnails of one or more images to assign the value. It does take a little more ingenuity, but the benefits are worth it.

One of the first steps is putting together a good data model. You actually have two separate sets of data - the first being the currently selected file information for the displayed image, the second being the set of all files that could be displayed, as URLs. The example I'm giving here makes the assumption that each file has a thumbnail version located in a different directory, and that the location of both the primary and thumbnail directors are passed in with the resource data:

        
 <xf:model id="main_model">
    <xf:instance id="image">
        <d:data filename="" path="" icon=""/>
    </xf:instance>
    <xf:instance id="images">
        <d:imageset title="Space Images"
            primary="file:///c:/Publishing/styles/images/main/"
            thumbnails="file:///c:/Publishing/styles/images/thumbs/">
            <d:image filename="Deep Space 2 - 1024x768.jpg"/>
            <d:image filename="HORSEHEA.jpg"/>
            <d:image filename="Hubble.jpg"/>
            <d:image filename="M16wf2.jpg"/>
            <d:image filename="prominence.jpg"/>
            <d:image filename="pvorion.jpg"/>
            <d:image filename="Solar Whirl - 1024x768.jpg"/>
            <d:image filename="Deep Space 2 - 1024x768.jpg"/>
        </d:imageset>
    </xf:instance>
    <xf:bind nodeset="/d:data/@path" 
            calculate = "concat(instance('images')/@primary,/d:data/@filename)"/>
    <xf:bind nodeset="/d:data/@icon" 
            calculate = "concat(instance('images')/@thumbnails,/d:data/@filename)"/>
</xf:model>
        

In this case, the filename, path, and icon attributes will hold the single filename, the path to the primary files, and the path to the thumbnail respectively. The second instance, called "images", contains the path (terminating with a "/" character) to the primary files and thumbnail files respectively, then contains the individual filenames of the files in the relevant images. It is possible, using XForms expressions, to determine the full path from this information, using the two calculating bind statements at the end of the model.

The selection itself takes a slightly different approach than in the previous example:

<xf:select1 ref="/d:data/@filename" 
     id="thumbnailView" appearance="image">
    <xf:label>Image<br/></xf:label>
    <xf:description>
         <xf:output ref="/d:data/@icon" appearance="image"
              style="margin:0px;padding:0px;"/>
    </xf:description>
    <xf:repeat nodeset="instance('images')/d:image">
        <xf:item>
            <xf:label>
                <xf:output 
                    value="concat(instance('images')/@thumbnails,@filename)"
                    appearance="image"/>
            </xf:label>
            <xf:value ref="@filename"/>
        </xf:item>
    </xf:repeat>
</xf:select1>
<br/>
<xf:output value="/d:data/@path" appearance="image"/>            
        

You can get most of the way in putting together a graphical selection box by understanding that an <xf:item> is actually a container, with its contents being what's contained in it's <xf:label> element and the associated value of the item being contained within the <xf:value> element. This means that you can modify the label with more than just a tag, and the content therein will be reflected as images (in this case) rather than just menu-items.

However, one of the principle drawbacks to using the existing component is the fact that the input box that serves as the principle display area for the content can only reflect the label value, not the icon. This means that you'll always get a blank entry field, while an icon would probably be preferable.

The solution to this is a little complex. First you need to introduce a new output element for displaying the icon once selected (as shown above), in this case placing the output element within a description element:

<xf:select1 ref="/d:data/@filename" 
     id="thumbnailView" appearance="image">
    <xf:label>Image<br/></xf:label>
    <xf:description>
         <xf:output ref="/d:data/@icon" appearance="image"
              style="margin:0px;padding:0px;"/>
    </xf:description>            
        

The next step involves a little more work - opening up the chrome://xforms/content/select1.xml file, choosing the xbl:content from that resource, adding it into a new binding (in XFormsBindings2.xml#select1-image, and pasting the content of the old content into a new <xbl:content> element. From there, suppress the display of the input box (anonid="control") by setting it's style to "display:none;" and adding a new <children includes="description"/> element to insure that the contents of the description element (the icon) will be displayed before the control button. Finally, copy the event handlers for the selector button into a span surrounding the description fall through, so that the icon will act as another button:

<xbl:binding id="select1-image"
    extends="chrome://xforms/content/select1.xml#xformswidget-select1">
    <xbl:content>
        <xbl:children includes="label"/>
        <h:div class="-moz-xforms-select1-popup" anonid="popup"
            onmouseover="this.parentNode.shouldHandleBlur = false;this.parentNode.mouseOver(event);"
            onmouseup="this.parentNode.mouseUp(event);"
            onmouseout="this.parentNode.shouldHandleBlur = true;">
            <xbl:children/>
        </h:div>
        <h:span class="-moz-select1-container" anonid="container" style="border:none;">
            <h:input class="-moz-xforms-select1-input" anonid="control" xbl:inherits="accesskey"
                style="display:none;"
                onfocus="this.parentNode.parentNode.dispatchDOMUIEvent('DOMFocusIn')"
                onblur="this.parentNode.parentNode.handleBlur();this.parentNode.parentNode.dispatchDOMUIEvent('DOMFocusOut');"
                onclick="this.parentNode.parentNode.handleControlClick();"
                onkeypress="this.parentNode.parentNode.handleKeyPress(event);"
                onkeyup="this.parentNode.parentNode.handleKeyUp(event);"/>
            <h:span 
                onmousedown="this.parentNode.parentNode.shouldHandleBlur = false;"
                onmouseup="this.parentNode.parentNode.shouldHandleBlur = true;"
                onclick="this.parentNode.parentNode.togglePopup();this.previousSibling.focus();">
                <xbl:children includes="description"/>
            </h:span>
            <h:input type="button" class="-moz-xforms-select1-dropdown xf-value"
                anonid="dropmarker" tabindex="-1"
                onmousedown="this.parentNode.parentNode.shouldHandleBlur = false;"
                onmouseup="this.parentNode.parentNode.shouldHandleBlur = true;"
                onclick="this.parentNode.parentNode.togglePopup();this.previousSibling.focus();"
            />
        </h:span>
    </xbl:content>
    <xbl:implementation implements="nsIXFormsUIWidget">
        <xbl:method name="refresh">
            <xbl:body><![CDATA[
        return true;
    ]]></xbl:body>
        </xbl:method>
    </xbl:implementation>
</xbl:binding>
        

Once this process is completed, just associate the appropriate binding in CSS, and your XForms code should be ready to go, as is shown in XFormsBinding3.xhtml and Figure 9.

<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:d="http://www.metaphoricalweb.net/xmlns/data" 
    xmlns:ev="http://www.w3.org/2001/xml-events">
    <head>
        <title/>
        <style type="text/css"><![CDATA[
@namespace xf url("http://www.w3.org/2002/xforms");
xf|output[appearance="image"] {
     -moz-binding:url('XFormsBindings1.xml#output-image');}
xf|select1 xf|itemset {width:64px;}
xf|select1[appearance="image"]{
    -moz-binding:url('XFormsBindings2.xml#select1-image');}
#thumbnailView xf|item {width:64px;}
#thumbnailView {} 
        ]]></style>
        <xf:model id="main_model">
            <xf:instance id="image">
                <d:data filename="" path="" icon=""/>
            </xf:instance>
            <xf:instance id="images">
                <d:imageset title="Space Images"
                    primary="file:///c:/Publishing/styles/images/main/"
                    thumbnails="file:///c:/Publishing/styles/images/thumbs/">
                    <d:image filename="Deep Space 2 - 1024x768.jpg"/>
                    <d:image filename="HORSEHEA.jpg"/>
                    <d:image filename="Hubble.jpg"/>
                    <d:image filename="M16wf2.jpg"/>
                    <d:image filename="prominence.jpg"/>
                    <d:image filename="pvorion.jpg"/>
                    <d:image filename="Solar Whirl - 1024x768.jpg"/>
                    <d:image filename="Deep Space 2 - 1024x768.jpg"/>
                </d:imageset>
            </xf:instance>
            <xf:bind nodeset="/d:data/@path" 
                 calculate = "concat(instance('images')/@primary,/d:data/@filename)"/>
            <xf:bind nodeset="/d:data/@icon" 
                 calculate = "concat(instance('images')/@thumbnails,/d:data/@filename)"/>
        </xf:model>
    </head>
    <body>        
        <xf:select1 ref="/d:data/@filename" id="thumbnailView" 
             appearance="image">
            <xf:label>Image<br/></xf:label>
            <xf:description><xf:output ref="/d:data/@icon" appearance="image"
                style="margin:0px;padding:0px;"/></xf:description>
            <xf:repeat nodeset="instance('images')/d:image">
                <xf:item>
                    <xf:label>
                        <xf:output value="concat(instance('images')/@thumbnails,@filename)"
                            appearance="image"/>
                    </xf:label>
                    <xf:value ref="@filename"/>
                </xf:item>
            </xf:repeat>
        </xf:select1>
        <br/>
        <xf:output value="/d:data/@path" appearance="image"/>
    </body>
</html>
        

It's worth pointing out here that the <xbl:content> element in any inheriting binding supercedes the content of inherited bindings (just as the refresh() method supercedes the abstract refresh methods in earlier examples). This means that you can actually create fairly radically different implementations of selectors and other components, so long as you respect the implementations already defined.

People do not always set out to create components. Often it is easier to build up components using existing XForms building blocks, making minor modifications, but at some point its worth going from slight modification of existing components (such as those used in the output-image and select1-image) to creating entire components that are stand-alone (such as table and grid components which will be showcased later).

Summary

This is, even by my lights, an extraordinarily long post. The next post will cover the XForms event model, and will also explore the underlying XForms DOM and how to interact via JavaScript using this.

Links to previous articles in this series:

Why XForms Matter Revisited

Understanding XForms: The Model

Understanding XForms: Components

Kurt Cagle is an author, software architect and explorer of XML and web related technologies.