This article demonstrates, within the growing AAL framework, the use of XML to control the visual layout of forms, controls within forms, and the management of events. As in the previous article, which discussed XML driven menus generated at runtime, this article focuses on XML driven GUI elements. Within the context of the .NET environment, this includes forms (MDI, SDI, dialog), controls contained within forms, form/control event specification, and data container association.

Keep in mind that the work here represents a prototype in many ways--the XML schema, the form object model, the features (and lack thereof) that's been implemented, etc. This code will most likely undergo considerable refinement as case studies using the AAL are developed. Also, the much promised SourceForge site has not happened yet except for getting approval for the AAL project and some tinkering. I am definitely interested in finding talented people to work on this project, especially in the area of GUI-based design and modeling tools to support Form generation, component connectivity, and data modeling. The point being, this is a work in progress and it can be fun to see how something evolves over time and to see that there's a lot of room for others to contribute to the design and implementation.

A large application will have numerous forms implemented through single/multiple document interfaces, dialog boxes, and/or tab views. Creating a form, such as the one used above by a boatyard client, results in reams of form designer generated code. If, as Taka once said (but points out that he is not the author of this witticism), "every line of code is a liability", at some point the number of lines of code that generates an application specific GUI exceeds the number of lines it takes to generate a GUI from an XML specification (or any other specification, for that matter). The liability incurred by the .NET framework in the generation of GUI's is considerable. Even when considering the MFC framework and resource (.RC) files, there is a considerable amount of application specific code required for setting initial control state, presentation options not supported by the form wizard, initial value, and data management. When the issues of data binding, SQL statements, data set and data adapter management is added to the equation, a considerable amount of code is generated simply to manage the flow of data through the GUI. Using automation, form elements and data flow can be generalized into a set of definitions and relationships between those definitions, eliminating significant amounts of custom coding.

As an application grows in scope, there is also a tendency to couple GUI elements directly together. For example, in the above screenshot, it is tempting to directly connect elements of the Receiving / Process Vendor Invoice tabs to other tabs involved in the receiving and inventory management aspects of the application, or vice versa. This dependency ultimately results in the product becoming rigid--impossible to add features and requiring expensive refactoring/rewriting. Although it can be avoided with good discipline (which breaks down as deadlines approach), why not instead use a framework that eliminates the temptations of this kind of dependency-building? The cost benefit is substantial when you consider all the factors involved--early prototyping, the ability to perform unit testing, simplified debugging, elimination of refactoring, etc.

As a point of interest, the GUI in the above screenshot is specified by an XML file which was created by using the MFC version of the AAL to generate the XML code during the GUI script processing.

Managing numerous forms, even with XML (or is that especially with XML?) can be cumbersome. A simple step to address this issue is to break apart form definitions on the physical boundary of the form itself. In the AAL implementation, this includes tab pages, dialogs, and forms. A simple schema provides the ability to specify separate form files:

Incidentally, looking at the above code, the switch statement is a good candidate for refactoring, possibly by passing the application type directly to the Form Manager. This is why the code presented here is considered a prototype!

The XML file that specifies all of the startup conditions is set in the properties page of the BootstrapLoader project:

Plug-In Architecture

A brief note about the bootstrap loader and the plug-in architecture. The bootstrap loader is the EXE that is run to load a specific application solution. The idea here is that a specific application solution is derived at entirely through XML specification, scripting, and where applicable, application specific assemblies. The bootstrap loader is continually evolving to achieve this. Currently, the only component is loads is the Component Manager assembly. The remaining components are loaded and initialized by the Component Manager itself. Currently, the bootstrap loader also deals with reading in the GUI specification. This is for convenience only, and will shortly need to be migrated out of the bootstrap loader and into the Form Manager. For those interested, the XML file components.xml is specifies the components and their public interface points (methods) along with parameters. The interface point information is used by the Component Manager to set up some hashtables to quickly locate the MethodInfo data associated with a method. This is a key plug-in architectural concept and will be used extensively in the development of the script engine as it provides a central dispatching mechanism capable of invoking synchronous and asynchronous methods with instrumentation and profiling.

As I note below, I'm still learning the capabilities of .NET. The entire issue of specifying parameter information in an XML file is completely unnecessary, as this information can be extracted at runtime. While it's nice to have the information in a "documented" manner, this "documentation" can itself be generated at runtime also, so the documentation issue is moot. I will migrate the Component Manager implementation over to this self-discovery technique soon. Remember: prototype!

A column schema, used for defining special controls such as the Outlook Bar control (OK, this is a kludge, I think).

I am not to concerned regarding whether the schema is going to change any time soon. The schema is read into internal structures that can pretty much stay constant while the schema implementation might undergo some improvements.

The form type defines the following features of a form (I hesitate to use the word "attribute" because of its XML connotations):

Name

A unique identifier for the form used to reference all operations performed on the form by the application.

Caption

The form caption. In the case of a tab form, this is the text appearing on the tab.

DataDomainName

The identifier specifying the container domain for all control data within the form.

PreCreateEvent

The event invoked before the form is created and controls are placed on the form surface.

PostCreateEvent

The event invoked after the form is created and all controls are placed on the form surface.

OnCloseEvent

The even that is invoked prior to the form being closed and removed from the Form Manager's form list.

FormDimensions

The position and size of the form. This includes special values for auto-centering and auto-sizing.

ControlSetName

The XML data set defining all controls to be placed on the form surface.

Icon

The form's icon, displayed in the caption.

This is a good set of starting features and can be easily expanded.

Noteworthy Features

Automatic autosizing if only one FormDimensions record is provided (this is sort of an annoying implementation, as it is based on "missing" information. I think it would be better to explicitly state such things as auto-sizing, auto-centering, starting form state (minimized, normal, maximized), etc. Well, this is a prototype;

defines several attributes of a control. Again, this is an incomplete definition but quite sufficient for a starting point:

Name

The internal unique identifier for the control

Control

The type of control (the list is defined in the schema)

ControlDimensions

Position and size of the control

VarName

The variable name containing the control's scalar data

ListName

The variable name containing the control's matrix data

Justification

Left, right, or center. Applies primarily to text

RW

Read/write or read/only

Header

For matrix controls, such as comboboxes and lists, this defines the columns

Bitmap

Specifies an associated bitmap

Parent

Probably obsolete--specifies the parent control

Tooltip

The tooltip text

Font

The control font. This overrides the form font, which defaults to MS Sans Serif, 8 point

Options

Additional options, for example, showing grid lines

OnLeftClick

The left click event handler

OnDoubleClick

The double click event handler

OnRightClick

The right click event handler

OnLoseFocus

The lose focus event handler (hey, where's OnGainFocus???)

Noteworthy Features

Controls can be moved to a different dialog or other form without refactoring the class names, event handlers, data collections, etc. I do this a lot as a product is evolved and enhanced.

A control can specify another control set. This provides a mechanism to build GUI's by re-using existing GUI elements--design once, use many times. In the screen shot of the boatyard software, there are several GUI subsets that exist on different tabs. Please note that this feature is not yet implemented!

The list control, as currently implemented, only supports "report" mode. Personal bias. I have never, ever, needed to use a list control (nor wanted to) in any other mode.

VarName And ListName

A control is either scalar, such as a label, groupbox, button, text box, radio button, or checkbox, or a matrix, such as a combo-box, list box, or grid. Actually, in the case of matrix controls, there is also a scalar element--the selected row. These two elements of the control define the name for the scalar and matrix (if required) variables. When the control is created, the Form Manager instructs the DataHub to create slots for these variables within the DataSet specified by the associated form (if no data set name is provided in the associated form, then the form name is used). The reader may observe that instead of this mechanism, the structures and containing data could be dynamically created at runtime through a variety of mechanisms. One such mechanism is Activator.CreateInstance. I have chosen not to go this route for two reasons, primarily--the DataHub provides important instrumentation, event, and conversion capabilities, and the implementation is less language/framework dependent. The DataHub can be modified though, if you are particularly motivated or interested in this capability.

The header schema specifies display information regarding the columns in a matrix control, such as a combobox, list control, etc. The header is also used to specify the tabs in a tab control (probably not the best implementation, eh?)

Name

A placeholder name identifying the column

Width

The width of the column (autosizing is currently not supported)

Justification

The justification, which affects both the header (for list controls) and the text in the column

Visible

Whether the column is visible or not. This is an excellent mechanism to hide information, such as ID's

Specifies column specific information, currently used exclusively by the Outlook Bar control

Noteworthy Features

One of the most valuable techniques in managing data transactions (the user interface and the database) is the ability to hide columns of data. These columns usually contain primary key ID's or other information that is useful for managing data transactions. The ListView and ComboBox controls do not natively support this capability. Besides facilitating data transactions (which some might argue can be handled by the data binding capability of .NET, this technique is also useful for supporting other user-interface related features in which additional hidden information drives other elements of the GUI. This is all achieved by using a single matrix as the data source for both display information and control/transaction information.

Although currently not implemented, the header information will be expanded at a later date to specify additional editing capability found in several of the grid controls published here on The Code Project.

Noteworthy Features

You will note from this object model that the interface between the AAL control implementation and .NET's control implementation is through a "has a" relationship instead of a "is a kind of" (derived class) relationship. Similarly for forms. This is done intentionally (and many may disagree) to prevent direct access to the .NET framework implementation. Part of the purpose of the AAL is to manage interaction with system objects, and this is the mechanism used to achieve this.

In MFC, the concept of a form is actually implemented as a frame and a view. After working with .NET forms for a while and discovering some of the odd behaviors when adding toolbars and status bars, I realized that there is a good reason to have a similar frame-view architecture. The AAL implicitly implements this architecture by creating a panel control that lives in the extents of the form, making a sort of quasi frame-view architecture. When controls are added to the form, in actuality they are added to the panel's control list. The exceptions to this (so far) are the status bar and the tool bar which are added to the form itself. Internally, the form resizes the panel (i.e., the view) to accommodate the status bar and toolbar, as illustrated by this code:

There are also other points of confusion in the .NET framework related. One is with regards to splitters, and the other is with regards to the sizing of containers, such as panels, that are docked/anchored to the outer panel, which itself is docked/anchored. These issues are worthy of a completely separate article.

The dialog form implementation differs from the SDI Form implementation only in the configuration of the form properties and how the form is invoked. Note that a dialog form can have menus, toolbars, statusbars, etc. The only difference is that it acts as a modal form:

This is done primarily to ensure that an MDI menu is properly selected and that the child window is checked in the MDI child list of a menu. I found that the events Activated and MdiChildActivated did not work as expected. Therefore, the GotFocus event handler is used instead (but requires a kludge with the deleted flag, because the GotFocus event fires after the form manager has removed the form from its master list.

In all cases, a control derives from the AALControl class which encapsulates (a "has a" relationship) the .NET control. In certain cases, the .NET control is insufficient and a Smart... control is created with a "is a kind of" relationship. That is, it is derived from the .NET control. I will illustrate this architecture with several examples. The base class AALControl also provides a wrapper for the ControlInfo class, which is a hodgepodge collection of information extracted from the XML control specification and some common functionality for all controls, such as the ability to control font, docking, color, and other options. Now, I will not say that this is the best architecture, and in practical usage it often requires casting and seemingly unnecessary layers of objects to get to the native .NET control. I do in particular like the "has a" relationship as this gives the AAL a lot more control (ha ha ha) over the application's interface with the .NET's implementation of GUI controls. In other words, it prevents the application from directly accessing control properties, methods, and events. This is necessary so that the programmer does not wander from the AAL architecture!

This class creates the control. Because the Label control is missing the ability to set its justification (as far as I can tell), a SmartLabel control is created instead which overrides the CreateParams method, enabling the AAL version to set the justification:

Panels are treated as containers for forms, and are thus derived from the IForm interface. Because panels do not have menus or status bars, the IMenuHelper interface is omitted. The core control, AALPanel, is derived from AALControl as usual, and it instantiates a "smart" Panel derived object.

after creating the panel control, the Form Manager is instructed to create a panel form

the SmartPanel overrides the Dispose method to ensure that the associated form is properly cleaned up

the object model could be simplified further by having the AALPanelForm contain the Panel control. However, as a design decision, I believe it is better to be consistent in the manner in which controls are handled, especially when there is no significant performance or resource penalty associated with being consistent, as opposed to hyper-optimization.

Now I will happily admit that I come from the old school of C++/MFC programming, so the concept of programming properties and attributes dynamically just hasn't sunk in yet for me. The reader will note that there are a lot of different properties that can specified for controls and forms, but the AAL currently implements an extremely (emphasis on extremely) limited set, in which XML specified properties are passed to the control. Ideally, and in hindsight as I write this article, a generic XML specified property-value pair might be a better solution. Using .NET's dynamic type capability, the values can be set for the specific control as desired. I'll be considering this design change in the future and possibly migrate the implementation to this approach.

All controls use the AAL architecture for data management (where implemented at this point--remember, prototype!). This means that the controls interface with the Data Hub. Each form is associated with a specific workflow domain. If no domain is specified in the XML definition for the form, a default domain of the same name as the form is created. The issue of managing the dynamic creation of MDI child forms and potential naming contention is blissfully ignored for now.

Most controls, like a text box, radio button, or check box, have at least a scalar variable. Some controls, like a combo-box, list control or tree control have both an index variable (a scalar) and a list (a matrix). Note again that multiple-selection combo-boxes, lists, and tree controls are all happily ignored for the time being. All controls use a common data representation for their values and translate between this common data representation and the specific needs of the control. This ensures that a uniform data representation is available for all components that are part of an application built using the AAL.

Each form implements the ability to update all the controls/storage on a form bi-directionally or a specific control on a form. Additionally, the ability to update a list container for a specific control is provided. Note that this is a unidirectional update--from data container to control only:

Each control is responsible for implementing the Update... methods and for correctly translating to and from the Data Hub representation. This is pretty simple in .NET because of the implicit awareness each object has of its own type and the ability to convert from an object to a different type.

Please refer to the previous articles in this series (listed at the top of the page) for further information regarding data domains and Data Hub.

All events (and again the implementation is very bare-bones currently) utilize the Component Manager's Invoke method. Events in the AAL architecture are instrumented, meaning that an audit trail is automatically created. Handling control events directly within the application breaks this design criteria. Since events often trigger workflows, it is also appropriate that the event mechanism that the .NET framework provides be tied in with the scripted workflow mechanism that the AAL provides. Lastly, the AAL attempts to enforce a highly modularized approach to application design and coding, and this mechanism ensures that events comply with this architecture.

A typical implementation for handling an event is demonstrated in this code from the Button control (similarly with the IDH, the ICM should also be a global object in each assembly):

This prototype application is provided with the source code (which is hardwired to load the configuration files for this application in the bootstrapLoader.cs file).

Having perused both freeware, shareware, and expensive-ware products, I have yet to find something that lets me organize my thousands of digital images (and growing!) in:

a dynamic manner--I want keys that are not predefined by somebody else;

quickly--I don't want a lot of other overhead associated with cataloging the image

easily--I want to be able to easily create new keys and populate them with selection fields and be able to easily select one or more of those fields from one or more keys for each picture

relationally--for lookup, I want to be able to specify a complex combination of keys and fields

After some research, and like Matt Gullett's "The Life of a Project" pointed out for something different, I simply haven't found something that meets my needs in terms of simplicity, elegance, and capability. Besides, this is a great little project to prove the concepts behind the AAL.

Personally, I've found that using XML to specify form layout and data events is cumbersome and awkward. In raw editing mode, the tags and rapid growth of the file makes it difficult to navigate, read, and modify an XML file. Using my XML Data Editor helps, but because it's a generic editor, there are features I'd like to see that it simply doesn't support and navigation is still awkward. Some custom tools and layout editors would be the best solution for this kind of work. Any takers?

The distribution file is an MSI file that installs the source code to the specified target directory. The project files are VS.NET 2003 format. The Photo Album prototype currently resides in the "template" directory that is populated off of the target directory, and if no "app info" xml file is specified in the command line, the bootstrap loader defaults to loading the Photo Album app info file.

The code installs as the "Application Automation Layer" in the "Add or Remove Programs" utility, and must be removed in order to install a new version (some of the options when setting up an MSI don't seem to work). The MSI creates the following directories (for example, I overrode the default target directory during installation and specified "c:\temp\aal" instead:

The project solution file is found in the AAL directory. After building the solution (ignore the two warnings in the DataAccessLayer assembly), run the "BootstrapLoader.exe" application found in the "template" directory. To run the application from the IDE or to debug it, change the command line arguments and working directory for the BootstrapLoader project (a screenshot of this appeared at the beginning of the article in "The Bootstrap Process" section).

(However, note that as the CP2 Scripting Framework Project project takes shape, I may have to completely rethink the component method interface mechanism I'm currently using, especially since the next article should really delve into workflow management so more interesting things can be done with the Photo Album).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.