Requirements
Prerequisite knowledge

Basic knowledge of Flex and MXML is
suggested, although you should be able to
follow along in the code walkthrough
provided in this article even if you are new
to Flex and MXML. To be able to edit and
extend the application, some familiarity
with Flex Builder is required. If you have
experience with structured authoring,
representing structured documents in XML,
or transforming XML into HTML using XSLT,
you will recognize many aspects of this
application's design.
Required products

Sample files

User level

Beginning
 
Adobe AIR, Flex, and XML combine to create a compelling platform for delivering applications. A great example of this is to take a structured document in XML and present the information in a new and effective way. Interest in building intelligent viewers for XML documents is growing rapidly, especially among technical communicators. Technical writers are quickly adopting structured authoring and therefore are able to deliver their content in XML, using standards such as DITA (for topic-based information such as Help), S1000D (in aerospace or defense environments), SPL (for pharmaceutical products), and so on.
 
This article demonstrates how easy it is to develop an XML document viewer in Flex, and then use Adobe AIR to package and deliver the application. Figure 1 shows the product information for a line of sunglasses in the XML Viewer sample application. Although the focus in this article is to deliver a standalone application, the same Flex code can be reused to deliver a hosted rich Internet application (RIA) via a browser.
 
Note: This article is a good starting point for someone new to Flex or Adobe AIR, because it provides a walkthrough for how a complete application can be built with surprisingly few lines of code. You don't need much familiarity with ActionScript to understand this project, because nearly all the code is MXML. Alternatively, if you are well-versed in Flex, you may find the design of this application to be rather novel, because it is written in a declarative style of programming and shows an efficient way to build an application using just a few MXML components.
 

 
Understanding structured documents

Before I get into the design of the viewer, I'd like to explore the domain of structured documents. If you are already familiar with structured documents and how they are represented in XML, you can skip to the section titled Producing the Flex application
 
 
What are structured documents?
Every document you read has an implicit structure, that is conveyed via formatting and layout (see Figure 2). We are so used to these formatting and layout conventions that we hardly have to think about this. For example, we expect chapters to begin on a new page, and if there's a line of text at the top of the page in a bold font and larger size, we automatically deduce that's a chapter title. We recognize section titles in a similar way, and we know that if text in a paragraph is in italics, it probably has a special meaning.
 
Every document has an implicit structure
Figure 2. Every document has an implicit structure.
 
The term structured documents is used when the structure of the document is made explicit. Instead of using formatting and other visual cues, the content in a structured document is tagged to indicate the purpose of that element (for example, "this is a series of instructions", or "this is the title of this section"). The content of the document (text, graphics, and so on) is organized into a hierarchy of elements and attributes, and the rules dictating how the content is tagged and organized are described in a schema or Document Type Definition (DTD) as shown in Figure 3.
 
Every document has an implicit structure.
Figure 3. An example of a structured document.
 
Years ago, it was common in certain industries (such as aerospace) to represent structured documents in SGML. But it was only with the introduction of XML (and all the associated tools to support processing XML, such as XSLT) that it became cost-effective for structured authoring to gain broad adoption.
 

 
Advantages of structured documents

There are many advantages to structuring your documents, including:
 
  • Formatting is separated from content. Because elements are tagged according their meaning, a template can be produced that maps the elements to formatting. This helps provide consistent formatting across a document, and relieves the author of the task of assigning formatting manually. It also means that any changes to the formatting can be accomplished by just changing the template, without any reworking of the document content. This allows many different templates to be used to produce different types of output from the same source (for example, one template for PDF, one for HTML, one for mobile devices, and so on). This technique is also known as single-source, multichannel publishing. Conversely, the content can be edited and then reflowed into the template. This is extremely useful in translation workflows (because it eliminates the manual task of reformatting localized content), and with documents that are often being updated (for example, user manuals).
  • Manipulation of the content can be automated. Because the content is tagged and organized into a predictable hierarchy of elements, you can write scripts to intelligently process the document, to filter, reorganize, or combine new content as required.
  • Metadata is explicitly represented in a standard way. By using attributes on elements to represent metadata, you can be smart on filtering content, such as identifying all elements tagged with a specific model number or skill level.

 
Creating a structured document

To create a structured document, you'll need:
 
  • A DTD or Schema that determines the information architecture of your document (that is, the rules for which elements and attributes can be used and how they can be organized)
  • A template that maps formatting to elements and attributes
  • An authoring environment that:
    • Lets you author your content in an element hierarchy
    • Lets you add attributes to elements
    • Understands and validates against the DTD

 
Creating the UserGuide.xml example structured document

In this example, I used Adobe FrameMaker to author the structured document. However the origin of the XML file is immaterial to the viewer application. This is an advantage of using structured documents and XML: Your authoring environment and your publishing environment can be isolated from each other, allowing you to change your authoring environment or support multiple authoring tools without affecting the downstream publishing environment.
 
All that matters to the viewer application is that the XML is valid, meaning that it conforms to the rules in the DTD or Schema. The DTD is the specification of the data format to which both the authoring environment and the publishing environment conform. To keep the viewer code short and simple, you can assume the XML data is valid according to the DTD. If the XML wasn't valid, then the viewer would not recognize the elements or attributes, or those elements or attributes would not be in the hierarchy the viewer would expect. In a production environment, you would either have to introduce a process to ensure only valid XML was provided to the viewer, or you would need to build a validation function (and corresponding exception handling) into the viewer.
 
For UserGuide.xml, I will use a DTD of my own design (UserManual.dtd, which is in the src/assets folder of the ZIP file that accompanies this article).
 
Note: I could have gone with an industry-standard DTD or Schema (for example, DocBook or DITA), but the DTD is use for this article strikes a good balance by being complex enough to represent a working scenario without being as complex as one of the industry-standard DTDs.
 
UserGuide.xml, which is also part in the src/assets folder of the ZIP file that accompanies this article, contains text that is tagged and organized into a hierarchy of elements, and it represents product information on some sunglasses. Attributes are also provided on some elements providing additional information. The data references a number of external graphics. In this example, those graphics are in the SWF format, created using Adobe Illustrator and Adobe Flash.
 

 
Producing the Flex application

You are going to build a standalone viewer for structured documents that conform to the UserManual DTD. This viewer needs to provide a user interface for navigating the content of the UserGuide.xml, and it needs to format the content appropriately on screen. Graphics, numbered lists, and tables in the document need to be rendered correctly.
 
 
Design considerations
The design of the viewer is influenced by the following:
 
  • Produce the viewer in Flex Builder and deliver it as an AIR application.
  • Use MXML and built-in Flex components as much as possible, keeping the use of ActionScript to a minimum.
  • Design the code along lines similar to how XSLT applications are commonly written, using pattern matching and a declarative style of programming.
 
Design overview
At the highest level, you will build the XML viewer sample application from a selection of built-in Flex layout and control components. These components have default UI behaviors that enable you to create an application with just a few lines of code. You will group and nest the components to match the desired presentation of the content and its structure.
 
You will hold the content of the XML in a single variable. By binding various elements of the variable to the values of the components, you create a relationship between the content and the components displayed on screen.
 
The creationComplete event (which is dispatched when the application has finished loading) is the trigger to populate the variable with the XML content, which in turn updates all the UI components with the data from the XML document.
 
Repeater components enable us to iterate through multiple children inside an element. The combination of dataProvider components and Repeater components give us similar capabilities to pattern matching in XSLT.
 
To handle the display of elements, we will use standard MXML components, such as Text and Image. We will control layout with standard components such as VBox and HBox. This is similar to how, with XSLT, you can use standard HTML tags to control formatting and layout.
 
To provide a simple method of organizing and navigating the various sections of content, you'll use TabNavigator in two ways. The first organizes the model descriptions and troubleshooting section into tabs, and the second puts various sections of model descriptions into sub-tabs.
 
Finally, you'll organize the code into multiple components. This should make the code easier to maintain, and allow you to reuse components in other projects.
 

 
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.

 
Publishing and testing the AIR application

Testing the application while in Flex Builder is as simple as selecting Run > Run main from the menu bar.
 
Creating the Adobe AIR installer for deployment is just as easy, using the Export Release Build feature, and clicking through the wizard. By default, the name of the AIR file exported is main.air (because the wizard takes its cue from the main.mxml component); you may want to edit this to something more descriptive, such as XML-viewer.air (see Figure 4).
 
Provide a digital certificate.
Figure 4. Use the export wizard.
 
Click Next, and you are asked for a digital signature to be used to sign your application (see Figure 5).
 
Note: You must digitally sign an AIR application before you can distribute it. Digitally signing your AIR apps instill trust in your users that the application has not been tempered with by a third party. For more details, see Todd Prekaski's Digitally signing Adobe AIR applications.
 
Provide a digital certificate.
Figure 5. Provide a digital certificate.
 
If this is your first AIR project, then you will want to create a digital certificate so you can sign your application (unless you already have one, in which case, use that).
 
Note: For testing purposes you can use a self-signed certificate (see Figure 6).
 
Include the dependent files of your AIR application.
Figure 6. Provide a digital certificate.
 
After you have assigned a digital certificate, the final step is to specify what files to include in the AIR file. Just accept the default setting, which includes all the files (see Figure 7).
 
Include the dependent files of your AIR application.
Figure 7. Include the dependent files of your AIR application.
 
To install the application on your computer, first locate and double-click the AIR installer file in your project folder. The installer first provides some information on the potential security risks, but since you built this application, you are pretty sure it is OK, and click Install (see Figure 8).
 
Note: If your AIR app is digintally signed by a certificate authority and not self-signed (as in this example), the Publisher in Figure 8 would be identified by name.
 
Installing the application.
Figure 8. Install the application.
 
The final step is to choose a location for the application (see Figure 9). You can accept the default location, or choose another one. When the Start application after installation check box is selected, you'll see the application pop up almost immediately after you click Continue (see Figure 10).
 
Provide a digital certificate
Figure 9. Provide a digital certificate.
 
The XML Viewer sample application —at our command
Figure 10. The XML Viewer sample application—at our command.
 

 
Where to go from here

With less than 300 lines of code, you have a functional and fairly rich application. You also have a design that could likely be adapted for other document viewer applications.
 
From here, you could take this code in various directions. For example, you could improve the look and feel of the application, standardize and centralize the formatting of components, editing skins, using CSS styles, and so on. Check the documentation and samples for the MX:Style component, and explore how Flex transitions can add animated effects that make applications more engaging.
 
The XML is loaded locally in this application, and there are interesting ways in which it could be enhanced to gather or synchronize its content over the Internet. For example, you could provide a user interface or other control to allow users to select the location of the XML data file, rather than have it hard-coded. The server architecture could be as simple as files hosted on a web server, or you could have a more sophisticated system using PHP or Cold Fusion on the back end. AIR provides features for local file and data storage and synchronization, so you could develop a sophisticated system that operates both online and offline.
 
The DTD used in this example results in XML files that are self-contained. In contrast, DITA and other document models enable reuse of, and reference to XML elements split across many different files. You could extend the application to work with such a distributed document model. Logic to handle the assembly of distributed information could be handled at either the server side (via PHP, ColdFusion, or a similar technology) or at the client side using ActionScript and the functions and classes available for processing and handling XML.
 
As is, the content is rendered in a relatively static way, and there is little interaction between the text content and the graphic assets. You could explore ways to dynamically deliver the content (for example, present the instruction steps one-by-one, or to allow the user to dynamically personalize or filter the content). You could also explore ways in which the text and graphics could interact (for example, click a hot spot in a graphic to jump to a section of text, or click a hyperlink in the text to cause an animation in a graphic). This would require some creative UI design, to define buttons and other user interface elements for navigation and interaction. Flex and ActionScript provide a rich model for handling the events generated from such end-user interaction.
 
Finally, there are many ways you could improve the deployment aspects of the application. For example, Todd Prekaski's article Digitally signing Adobe AIR applications and David Tucker's tutorial Deploying Adobe AIR applications seamlessly with badge install are both highly relevant. Also check out Tom Lane's tutorial on building AIR applications with Flex Builder.