Accessibility

Table of Contents

Building an XML viewer on AIR with Flex

Stepping through the code

For this section, you want to have the code loaded into Flex Builder, and perhaps have these pages printed or displayed on a second monitor. The links to download the source files are provided in the Requirements section at the beginning of this article.

To import the source files into Flex Builder, use the File > Import menu item, select Flex Project, and then browse to the archive containing the project, XML_viewer.zip. Once imported, you should see the XML-viewer project in the Flex Navigator pane. Expand the folders to locate the source files in the src folder, and the source XML and graphics in the assets folder.

main.mxml

Main.mxml is the backbone of the application. As with all MXML components, it begins with an XML declaration, followed by the top-level element, which defines the application as a WindowedApplication (that is, an AIR application).

<?xml version="1.0" encoding="utf-8"?>
  <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
                 layout="vertical"
                 width="800" height="660"
                 xmlns:comp="*" xmlns:fmtComp="fmtComps.*"
                 creationComplete="srcData.send()"
                 paddingLeft="0" paddingRight="0"
paddingTop="0" paddingBottom="0">

The attributes contain some key information, including:

  • Overall layout of the components in the viewer (layout="vertical").
  • Dimensions of the window (width="800" height="660").
  • The location of the other components of the application (xmlns:comp="*" xmlns:fmtComp="fmtComps.*").
  • The function to call when the basic application finishes loading (creationComplete="srcData.send()"). This part is crucial. It causes the XML file to be loaded, which in turn triggers the formatting of the XML in the presentation window.

The following snippet shows one of the few instances of ActionScript in this application. It creates a container/variable to hold the XML data (userManualObj). Then it defines a function to load the XML data into the container. The XML data comes attached to the event object, as event.result.

<mx:Script>
  <![CDATA[
  import mx.utils.ObjectProxy;
  import mx.rpc.events.ResultEvent;
  
  [Bindable]
  private var userManualObj:ObjectProxy;
  
  private function loadModelData(event:ResultEvent):void
  {
     userManualObj=event.result.UserManual;
  }
  ]]>
  </mx:Script>

Dataproviders to MXML components, defined later in this application, bind to this container, so when the data loads into this container, those components are updated accordingly, and that drives the entire presentation of the XML content.

The following code defines an HTTP service that will be the source of the XML data. This is the service that is called when the application completes loading, as defined in the root element of the WindowedApplication. The service retrieves the XML data from the file, the location of which in this case is hard-coded ("assets/UserGuide.xml"). The result causes the loadModelData function to be invoked (as defined in the earlier ActionScript), passing the XML data retrieved as a result property to the event.

<!-- Set up the HTTP service from where we get the source XML data -->
  <mx:HTTPService id="srcData" 
                 url="assets/UserGuide.xml"
                 result="loadModelData(event)"/>

The following code defines the main container for the rest of the presentation objects. The attributes handle some of the formatting. For the sake of brevity, the obvious formatting codes will not be included in further descriptions.

<mx:VBox width="100%" horizontalAlign="left" 
     height="100%" paddingBottom="0" paddingLeft="0" 
     paddingRight="0" paddingTop="0">

The following line defines the top-level tabnavigator, which is used in this example to provide a way to navigate between top-level elements of the XML (the troubleshooting section and the model descriptions):

<mx:TabNavigator width="100%" height="100%"

This application makes frequent use of the Repeater component, as a way to match and enumerate through all the children of an element:

<mx:Repeater id="modelsRep" dataProvider="{userManualObj.Product.Model}">

The dataProvider binds to all the children of userManualObj.Product of element type Model, creating an array containing pointers to Model elements. With each pass through the Repeater, the variable modelsRep points to one of those Model elements.

With each pass through the Repeater, a VBox is created. In effect, this creates a tab for each Model element (remember the Repeater is inside a TabNavigator). The label (title) of the tab (VBox) is mapped to the Name child element of the currentItem of the Repeater. This populates the title of the tab with the model name.

<mx:VBox label="{modelsRep.currentItem.Name}" width="100%" height="100%">
     <comp:ModelDesc model="{modelsRep.currentItem}"/>
</mx:VBox>

While still inside the VBox, you call on another component of the application, ModelDesc, passing to it the currentItem:

<comp:ModelDesc model="{modelsRep.currentItem}"/>

This component will populate the VBox with the rest of the content of the currentItem. After the Repeater handling the Model elements, but still inside the TabNavigator, you add another tab (VBox) for the Troubleshooting section:

<mx:VBox label="Troubleshooting" width="100%" ...>
     <comp:Troubleshooting issues="{userManualObj.Product.Troubleshooting.Issue}" />

The issues argument is passed to the Troubleshooting component. It contains an array of pointers to Issue elements that are children of the Troubleshooting element.

          </mx:TabNavigator>
     </mx:VBox>
</mx:WindowedApplication>

And that's all there is to the Main.mxml. Having populated the tabs with content, the application then gives the user the tabbed UI, scroll bars, and so on for navigating around the content.

ModelDesc.mxml

ModelDesc.mxml is a component designed to control the layout and format of the contents of a Model element. It is called inside the main Repeater in Main.mxml.

Here is the only other instance in which ActionScript is used in this application—to create a way to pass arguments to components. In this case, the argument will hold a pointer to a model element.

<mx:Script>
     <![CDATA[
          [Bindable]
          public var model:Object;
      ]]>
</mx:Script>

Three VBox containers inside a TabNavigator create three tabs to hold the content for each model (Overview, Instructions, and Details):

<mx:TabNavigator width="100%">
         <mx:VBox label="Overview" width="100%" height="550">
                 <fmtComp:ModelHeader title="{model.Name}" picture="{model.Picture.Graphic.file}" />
                 <fmtComp:Section title="Description" body="{model.Description}" />
                 <fmtComp:Features features="{model.Features}" />
                 <fmtComp:ComplexSection title="Usage" body="{model.Usage}"/>
         </mx:VBox>
         <mx:VBox label="Instructions" width="100%" height="550">
                 <fmtComp:Instructions title="Operation" body="{model.Operation}" />
                 <fmtComp:Instructions title="Cleaning" body="{model.Cleaning}" />
         </mx:VBox>
         <mx:VBox label="Details" width="100%" height="550">
                 <fmtComp:ComplexSection title="Storage" body="{model.Storage}"/>
                 <fmtComp:Section title="Availability" body="{model.Availability}" />
                 <fmtComp:TableSect tableSect="{model.Parts}" />
                 <fmtComp:Section title="Warranty" body="{model.Warranty}" />
                 <fmtComp:DistiSect distis="{model.Distribution.Distributor}" />
                 <fmtComp:Section title="Compliance" body="{model.Compliance}" />
                 <fmtComp:ComplexSection title="Notes" body="{model.Notes}"/>
         </mx:VBox>
</mx:TabNavigator>

Because the order of the immediate children of the Model element are known and appear once, it is easy to call components to render each one. In some cases, there are specific components to handle specific element types (for example, ModelHeader) but in other places, components can be reused for different element types (for example, Section and ComplexSection).

Other application components

The remaining components of the viewer application contain similar code and concepts to main.mxml and ModelDesc.mxml. The following list details the components:

  • ModelHeader.mxml: ModelHeader is invoked by ModelDesc to display the model name and picture (references to both of these are passed as arguments). The built-in MXML components for Label and Image are used to render the elements.

    Note: Because the source document uses graphics files in the SWF format, the graphics could be more engaging than static pictures, containing animation, video, or interactivity, for example. Such rich assets could be added to the source document without any changes to the viewer application.

  • Section.mxml: Uses the MXML components Label and Text to display headings and paragraphs, with a Repeater to enumerate all the paragraphs in a section.
  • Features.mxml: Displays a bulleted list of features.

    fmtComp:zeroOrMoreParas is invoked to display paragraphs that optionally may appear before the bulleted list. This component is a wrapper for a simple Repeater. It is used here because using Repeater directly inside Features.mxml tends to always result in at least one iteration. Unlike with ActionScript, there is no conditional in MXML (that is, if..then), so this workaround is needed to produce the intended effect.

    An HBox is used inside the Repeater to render a bulleted list. The HBox contains two items: the bullet and a paragraph.

  • ComplexSection.mxml: If a Section element contains a Note, Caution, or Warning (or a combination of the three) then ComplexSection.mxml is called. It uses the zeroOrMore wrapper components as a workaround to using Repeater components directly.
  • Instructions.mxml: Using similar techniques seen in Features.mxml, ComplexSection.mxml and ModelHeader.mxml, Instructions.mxml renders a series of numbered instructions. Note the use of text="{instrStepsRep.currentIndex + 1}" to generate and display the step number.
  • TableSect.mxml: TableSect.mxml is used to render Parts tables. This component uses the DataGrid component to render the table. This component is simple because Parts tables always have a three-column layout.
  • DistiSect.mxml: This component renders out a list of Distributors, using a Repeater.
  • Troubleshooting.mxml: This component is invoked by Main.mxml, and renders a list of topics, concerns, and actions using a Repeater.