Taquis - PC Base FFT Spectrum Analyzers, Oscilloscopes, Data Analysis, Data Acquisition, and Application Frameworks   AtOne Application Framework
"A Framework working with you, not against you..."

 
Home
Features
Programming
License
Downloads
Directions

Contact
info@taquis.com


 
 

Programming Controls

At this point in time AtOne comes with built in support for the Static text (GuiStaticText), Edit box (GuiEditBox), List box (GuiListBox), Combo box (GuiComboBox), Scroll bar (GuiScrollBar), Push button (GuiPushButton), Radio button (GuiRadioButton), Check box (GuiCheckBox), Group box (GuiGroupBox), Tab control (GuiTabControl), Track bar (GuiTrackBarControl), Progress bar (GuiProgressBarControl) and List view (GuiListViewControl) WIN32 common controls. Given time AtOne will support the remaining common controls such as Tree view, Animation control and Date time picker. AtOne also provides the following additional controls,
 
GuiRealEditBox An edit box control that accepts only numerical input and validates the input data, requiring that it lies within a program specified range. The resultant data is a real (ie. as a double).
GuiIntegerEditBox An edit box control that accepts only numerical input and validates the input data, requiring that it lies within a program specified range. The resultant data is an integer (ie. as a type int).
GuiStaticIcon A control that displays a magnified image of an icon in its client area with program defined styles controlling the effects applied to the image. This control is typically used to display the application icon in an about dialog box. This control supports transparent colour. 
GuiStaticBitmap A control that displays an image of a bitmap in its client area with program defined styles controlling the effects applied to the image. This control supports transparent colour and is typically used to display copyright information in an aplication about dialog box. 
GuiColourPaletteGrid A control that displays a colour grid of program defined size allowing the user to select colours from a palette using either mouse or keyboard.
GuiLED A control that displays a solid colour in its client area wih program defined on and off colours in a manner similar to a LED (light emitting diode). This control also allows mouse input and can therefore be made to respond to mouse clicks within its client area.
GuiSplashPanel This class is typically only use as a window class and not a control. This window will display a splash panel describing a program. The panel will be displayed for a program defined period of time and supports customisation of drawing through derivation. The splash panel displays a bitmap using colour mapping to ensure that the image colours are consistent with the current Windows colour scheme. 

Requirements for Controls

Controls, typically being used in dialog boxes, must support the protocol set down by the AtOne dialog standard. First and foremost a control must be registered with the window class registry using the registerWindowClass() macro. This in turn requires that the control has a standard form constructor. Controls must also have class identity and should therefore make use of the declClassIdentity and implClassIdentity() macros. These basic requirements are discussed in detail in Programming Dialogs. However, controls in dialog boxes are typically also required to perform data validation upon any input that they receive from the user. 
 

Input Storage and Input Validation

Any control that is non-static (ie. accepts mouse and keyboard input) will typically be used to gather information from the program user. This information necessarily needs to be stored somewhere when the user presses the OK (or Apply) button on the dialog box. This is true of any non-static control except those that are there only to signal events (push buttons for example). Lets say we have an edit box in which the user enters a number. When the user presses the OK button a sequence of processes must occur before the dialog can close. 

Firstly, the number in our edit box is in a string form and must therefore be converted to a number. The number conversion may fail due to an invalid syntax, or the converted number may lie outside a pre-defined valid input range, in which case the dialog box must not close. Instead, focus should be set to the edit control and an error message displayed. This validation process is conveniently and automatically handled through defining and implementing the virtual method canApply(). This method is called by the standard keyboard navigation code when the OK or Apply buttons are pressed. The canApply() method returns true or false depending upon if the input data is valid. In the case of failure (false), the canApply() method will set the pFailedWindow pointer reference (passed in as an argument to canApply()) to itself. By doing so the navigation code will then know which window to set input focus to. During this checking process canApply() should also build an error message describing the problem. When a canApply() failure occurs the standard navigation code will call the virtual showError() method to display the error message. Therefore you will have to implement the showError() method along side canApply() when implementing controls that receive input data. As an example, consider the case of the GuiRealEditBox control. This control implements these methods as,

bool GuiRealEditBox::canApply(GuiWindow*& pFailedWindow)
{
  bool bCanApply = this->apply(true);

  if (!bCanApply && pFailedWindow == 0)
  {
    pFailedWindow = this;
  }

  return (bCanApply);
}

void GuiRealEditBox::showError() const
{
  GuiApp  App;

  ::MessageBox(handleWindow(),
               ApplyErrorMessage,
               App.applicationName(),
               MB_ICONEXCLAMATION);
}

The conversion work is actually done in the apply() method call which does the conversion and builds the error message which it stores in the class attribute ApplyErrorMessage. When true is specified in the apply() method call it means that a test apply operation will be carried out. By test we mean that the data will be validated but not stored. 

What happens if canApply() succeeds? If canApply() succeeds standard navigation code will continue to query all child controls of the dialog until all children have returned true to the canApply() call. If that happens then the standard navigation code will call the virtual apply() method for all children of the dialog box. When this happens the control should do the conversion (which should always succeed) and store the result somewhere. In AtOne this is customarily supported through the concept of an "associated storage". All this essentially means is that the control maintains a pointer to a destination buffer where it will write the control input to. By convention this pointer is initialised through a method called associateStorage(), which takes as its argument, a reference to the result buffer. In the GuiRealEditBox control this method is,

void GuiRealEditBox::associateStorage(double& rStorage)
{
  OsiString Default;

  Storage = &rStorage;

  Default.sprintf(_T("%.*g"), Precision, *Storage);
  caption(Default);
  notifyParent(EN_CHANGE);
}

and is used to initialise the storage location attribute (Storage) and to synchronise the edit box text with the value currently in the storage buffer. The apply() method is,

bool GuiRealEditBox::apply()
{
  GuiWindow::apply();

  return (apply(false));
}

and is used to convert the data and write the result into the buffer specified in associateStorage(). Note that for the apply method we always call the parent apply()  method that we have over-ridden to ensure correct behaviour. The data conversion and storage is done in the call to the local apply() method, passing false as its parameter to indicate that we are doing the apply operation. This method has been ommitted for brevity. It is implemented in guictrl.cpp should you wish to refer to it. 

In summary then you will typically have to implement an associateStorage() method and the canApply(), apply() and showError() virtual methods for every control written. Exceptions to this rule will occur depending upon the nature and purpose of the control. The pre-existing code in guictrl.hpp and guictrl.cpp serve as a good reference to these requirements and exceptions. 
 

Style Issues

Generally speaking, all the window / control classes in AtOne follow a consistent style that I encourage you to conform with should you wish to code a completely new control or merely sub-class a WIN32 control. First and foremost in the style requirement is the replacement of windows styles with corresponding accessor methods. Some may regard this as unecessary but I believe not, because many of the style values (that are usually just bit fields) follow subtle rules that are easily forgotten (ie. two styles are mutually exclusive although their bit fields overlap). By using methods these quirks can be hidden from the user ensuring that the styles are always used correctly and consistently. This approach also gives the opportunity to give the various styles more meaningful an consistent names, quite often absent in the WIN32 style defines. 

Likewise the WIN32 messages that are used to interface with the control are given method wrappers for largely the same reasoning. Interfacing through methods is safer than through messaging as it is type safe. Once the method is correctly coded you can never use it incorrectly, whereas sending messages directly may result in erroneous casting. Although building methods for each message is initially time consuming it is time well spent and will make programming with that control that much easier. The look and feel of your code will benefit also and in the case of completely new controls you need not use messaging at all since you can use your control methods directly. 

To get the best feel for the style study the class definitions for the common WIN32 controls in guictrl.hpp and compare them with the styles and messages of the corresponding WIN32 controls. 
 

In Summary

In writing controls for AtOne your controls will need to be registered with the WIN32 class registry. This, in turn, requires that you implement a standard form constructor along with class identity methods. You may also have to implement an associateStorage() method and the canApply(), apply() and showError() virtual methods depending upon the nature of the control being developed. Finally, it is recommended that you develop controls in a style consistent with those in AtOne.
 

State Management


"We use Zeus for Windows and Watcom C/C++ 11.0 as our development environment of choice..."

Paavo Jumppanen
Creator of AtOne Application Framework


This document was last modified on 1st September, 2001
Copyright (C) 2001, Paavo Jumppanen
All rights reserved.