A first cut version of the page class will be generated. This includes the constructors, empty stubs for the most important life-cycle(Page Life Cycle)-methods and empty stubs for all widget event methods.
Every time a XMA page becomes
visible protected
void enter() is called. For DialogPages
opened with their invoke()
method, this method is called exactly once per instance. For NotebookPages
this method is called every time the user navigates to the page. For
EmbeddedPages
it depends on their parent page. Their enter() is called
every time enter() is called on their parent. If they are
embedded dynamically into a page which is already visible,
enter() is called at the time of embedding. If
DialogPages are embedded, they are treated just like
EmbeddedPages.
When the method is called, all widgets and widget models are already initialized.
In this method you will typically read some input parameters(Component Properties)and do a remote procedure call(Remote Procedure Calls) to fill the rest of the widgets with data.
String strParamSID = getTypedComponent().getSparbuchid();
idSparbuch.set(strParamSID);
newRemoteCall("search").execute();![]() | |
Principally you can do any kind of initialisation here. |
![]() | |
Static initialization of widgets which are supported by the guidesigner (e.g. labels, layout, etc) must be done there. |
![]() | |
Initial enabling/disabling of widgets is better done in stateChanged(). |
Every time a user event which changes any widget of the page happens, the widget models are updated from their widgets and determineState() and stateChanged() of the affected page are called in sequence exactly once after the event method. They are called the first time immediately after enter(). During the creation of a page and during the execution of event methods no events are triggered.
In public void determineState() you should calculate the current state of the page from the current values of the widget models.
In public void stateChanged() you should enables or disable widgets according to the current state of the page. Do not forget to disable all action-buttons if there are any errors from validation of the user inputs.
if(getErrorCount()>0){
okW.setEnabled(false);
} else{
okW.setEnabled(true);
getShell().setDefaultButton( okW );
}Almost all events are handled by the runtime. User input is validated and all valid changes in the widgets are propagated automatically to the widget models. There are just a few events which are handled directly by the programmer. The triggering SWT-Event is not passed as argument to your event method, but it can be accessed by calling getCurrentEvent() inside your event method.
If you want to execute some code when the user presses a push button, you set the Property YN SelectionEvent of the push button in the guidesigner to true. For every push button with this property set to true, an empty method stub is generated.
protected void <widgetName>Event()This method is called every time the user selects the button.
Per default the property YN SelectionEvent is true for all PushButtons and the corresponding methods are generated. You have to set the property to false in the guidesigner if you do not want to implement the method.
If you want to execute some code when the user selects an item in a table or tree, you set the property YN SelectionEvent of the widget to true in the guidesigner. Then an empty method stub is generated.
protected void <widgetName>Event()This method is called every time the user selects an item in the tree or table.
![]() | |
If you only want to enable or disable some widget depending on the selection state of the table or tree, you do not have to generate this method. You better do that in the method stateChanged(). |
If you want to execute some code when the user double clicks on an item in a table or tree, you set the property YN DoubleClick Event of the widget to true in the guidesigner. Then an empty method stub is generated.
protected void <widgetName>DoubleClick()
This method is called every time the user double clicks on an item in the tree or table.
![]() | |
It is not recommended to attach event handler directly to SWT-widgets. For all events handled by the runtime, this is explicitly forbidden, since it would interfere with the XMA-Runtime. |
In the rare case you have to use your own event handler on an SWT-widget, you have to disable all events for the runtime during your event processing.
This is best be done by using the XMA event adapters for SWT (provided with XMA since version 1.8.5). There exist event save event handling adapters for selection events on widgets not supported by the Guidesigner like toolbar items, mouse events, keyboard events, focus events and drag and drop. See the javadoc for: XMAFocusAdapter, XMAKeyAdapter, XMAMouseAdapter, XMASelectionAdapter, XMADragSourceAdapter, XMADropTargetAdapter.
You use these adaptors pretty much like the event adaptors
delivered with SWT. The main difference is, that you do not overload
e.g. widgetSelected() directly but the method
widgetSelectedImpl() instead. This is because the
original method contains the event disabling code and calls the
corresponding -impl method in all XMA-adaptors. Additionally it
needs a your page as parameter for its constructor to do its work.
The adaptor also catches all exceptions not handled by the -impl
method, logs it and shows it to the user.
itemW.addSelectionListener(new XMASelectionAdapter(this) {
public void widgetSelectedImpl(SelectionEvent event) {
// place your event handling code here ...
}
});Note: An added listener is only notified if its Widget or Control has the focus!
Old code in XMA-applications often still has event disabling code like the next example directly in the event handling methods.
IDialogPage dialog = getDialogPage();
try {
dialog.setEventsEnabled(false);
... // your event handling code goes here
} finally {
dialog.setEventsEnabled(true);
}This still works ok, but may be missing exception handling code. Unhandled exceptions in event listeners directly attached to SWT-widgets lead to unexpected behaviour. It is recommended to upgrade to the XMA-adaptors mentioned above.
Keys with a certain function which are available from the whole application (like the F1 help) can be implemented by using the XMAKeyAdapter. In this case the listener cannot be added to a Widget or Control as it would be only notified when its item has the focus. Instead it is added to the Display.
The following listing shows an example of adding an ESC hot key. Create a suitable adapter:
XMAKeyAdapter adapter = new XMAKeyAdapter(this){
public void keyPressedImpl(KeyEvent event) {
if (event.keyCode == SWT.ESC){
getDialog().closeCancel();
}
} Then set this adapter at the Display as filter:
getShell().getDisplay().addFilter(SWT.KeyDown, new TypedListener(adapter));
KeyEvent.keyCode identifies the actual pressed key, whereas KeyEvent.character represents the value resulting from a key after all modifiers were applied (like CTRL or SHIFT). The class SWT offers constants identifying keyboard keys, but comparisons with a character is possible as well.
Direct keyboard access to a widget is easy to implement. You
simply mark the access key in its label with an ampersand. e.g. a
button labeled &OK is shown as

Alt+O
.You do not need to code anything in Java. The label is entered in the guidesigner. Access keys are possible for all kinds of widgets (buttons, labels, menu items) even if the label is realized as a separate label object.
Navigation between the controls using the tab-key is controlled by the order of the widgets in the outline of guidesigner. The widgets are traversed with the tab-key in the very same order as they are shown in the outline view(Outline View). To alter this order, you simple move the widget around in the outline view with drag an drop using the mouse.
There exist two methods to close a dialog by an API call (as needed in the event methods for the OK or close button).
public void closeOK() closes the dialog and set the exit status to true
public void closeCancel() closes the dialog and set the exit status to false
The following methods can be overridden if you need some more control over the closing of the page.
protected boolean close() will be called every time the dialog is tried to be closed by user interaction (like by the click on the close icon). If you return true, the dialog will actually be closed. If you return false, the dialog will remain open. This method is only available on DialogPages. It will not be called after closeOK() or closeCancel().
protected void leave() will be called every time the page becomes invisible. For DialogPages this method is called exactly once per instance. For NotebookPages and WizardPages this method is called every time the user navigates off the page. In this method some cleanup code can be placed. It will be called after close(), closeOk() or closeCancel().
The runtime catches all exceptions thrown by your code and shows them in a modal message box. If you want to change the way the runtime shows exceptions, you can overwrite the method showException() and implement your own user notification.
Errors detected by validators (Formatters/Validators) are shown in the status bar. You can add additional error messages with the method setError() on the dialog page. It assigns an error message to a widget. Only one message will be shown in the status bar at any given time. If there exists an error for the widget currently owning the focus, this error will be shown, else the error of the next widget in the tab order will be shown. If there exists a validation error and an error set with setError() for the same widget, the validation error will be shown.
Do not forget to remove the error message with clearError() when the error condition no longer exists.
Warning and info messages can be assigned to widget, too using the methods setWarning() and setInfo(). Warning and info messages are showed in the status bar, too. They are only showed, if no error is set for any widget. The precedence rules are quite similar to the rules for the errors. If there exists a warning for the widget currently owning the focus, this warning will be shown, else the warning of the next widget in the tab order will be shown. If there exists no warning, the info for the widget owning the focus if exists, else the info for the next widget in tab order will be shown. If there exists no errors, warnings or infos, the status bar will be empty.
Warnings and infos have to be removed using clearWarning() and clearInfo().
If you create errors, warnings or infos assigend to widgets on an embedded page, these errors, warnings and infos will be automatically removed if the embedded page is removed.
You can determine the total number of widgets with errors on the dialog with the method getErrorCount(). It counts all validation errors and erros set with setError(). Warnings and infos are not counted. This method will be useful for enabling/disabling of buttons.
The DialogPage
offers two protected attributes for status bar manipulation: DialogPage.statusBar
and the DialogPage.statusBarComposite,
the parent of the status bar. Per default the statusBar
fills the statusBarComposite to 100%. If another
appearance is wished (as an additional output field) the
statusBar can be reattached and additional widgets can
be attached to the statusBarComposite. The reattachment
can be done for one specific DialogPage
by overriding (and call to the super method) DialogPage.createWidgets()
and DialogPage.removeWidgets().
If the status bar should be changed for all DialogPages
then this is done in the project specific DialogPage
parent by overriding DialogPage.initGUI()
and DialogPage.removeWidgetsBase().
Note: Always call statusBarComposite.layout(false) after reattachment!
To set the focus to a desired control you call setFocus() on the DialogPage and pass the desired widget as parameter.
getDialogPage().setFocus(nameW);This method works even in situations, where calling
setFocus() directly on SWT-Controls does not work, for
example if called in the enter() method of a dynamically
embedded page.I first cut version of the page class will be generated. This includes only the constructor.
You will have to implement the server side event methods(Server side) here.
You can navigate to other pages the same way as on the client side. The member variables of the generated class pointing to embedded pages like notebook pages are synchronized during the remote procedure call, too. So they always point to the server side copies of the notebook pages, if they have been created on the client side.
You can only call dialog pages within the same component as the caller directly. To call a dialog of a different component see Calling a Component. You call the component, which in turn will call one of its dialog pages.
To call a modal dialog page within the same component, you create an instance of the page, eventually set some input data and call invoke() on it.
There are three constructors to create a dialog page:
public <classname>(PageClient parent)
creates the page with the given page as parent. It is created inside the same component as parent and uses the window of parent as parent window. Use this method if want to call the page from an other page.
public <classname>(ComponentClient component, Shell parent)
creates the page inside the given component with the given shell as parent window. Use this method if you call the page from the component.
public <classname>(ComponentClient component)
creates the page inside the given component without a parent window. Use this method only if you do not have a parent page or parent window.
// create the dialog page
SpardetailDialog dialog = new SpardetailDialog(this);
// enter some data
dialog.idSparbuch.set( idSparbuch );
// open the dialog
boolean ok = dialog.invoke();
// read back some result data
if(ok) { saldo = dialog.betSaldo.toDouble(); }
// free the page model
dialog.removeModel(); If the property
ynModelLazyGenerated of the dialog page is false, which
is the default, its widget models are created from the constructor.
You can access and use the widget models immediately after calling the
constructor. The SWT-widgets do not exist at this time.To actually show the dialog page call invoke() on it. For modal dialogs invoke() blocks until the Dialog is closed. It returns true if the dialog is closed with closeOk(). It returns false if the dialog is canceled. For non modal dialogs invoke just opens the dialog and returns immediately.
After the dialog is closed you can still access its widget models. Only the SWT-widgets are already destroyed.
After the dialog is closed you should free its model, by calling removeModel(). This saves memory by removing the server side page during the next remote procedure call. This is especially important if you implement several pages within a component.
When a component is closed, all its pages are freed automatically.
With XMA it is possible to create pages which can be visually embedded in other pages. Such embedded pages show their content in place inside the parent page.
First you create(Create a new Page)an EmbeddedPage in the guidesigner. Then you create the widgets of the page the same way as for any other page.
The size of the embedded page you see in the guidesigner is maybe not the size the page will have in its embedded place. The actual size of an embedded page is determined by its user who defines its FormData.
Programming am embedded component is pretty much the same as programming an other page(Programming a Client Side Page).
You cannot directly embed a page outside its component. If you want to use the embedded page in different components, you have to create an embedded component(Embedded Component).
To embed the page inside the same component, you create a
XMA Container with the guidesigner. XMA
Container is a special widget which serves as placeholder for
the embedded page. You create(Create a new Widget) it
like any other widget. You need to choose the embedded page for the
property "EmbeddedPage". The place and size of the embedded page is
defined by the place and size of the XMA Container.
The guidesigner does not generate a widget for the XMA Container. The code for embedding the embedded page is generated instead. You can access the embedded page with the instance name you entered for the XMA Container.
You can communicate with the embedded page via its member variables.
To give you more flexibility, DialogPages have been made embeddable, too. DialogPages can not be directly statically embedded in the guidesigner. Within its component DialogPages can be dynamically embedded using Tasks(Calling Components)or the technique described in Dynamically Embedding a Component(Dynamically Embedding a Component). Just pass the DialogPage as argument instead of a component.
To embed the DialogPage in a different component, you have to embed its component(Embedding the Component).
The guidesigner generates a base class for each page. This class has the name of the page class with "Gen" appended. You must not modify this class since it will be overridden every time you generate the component.
The generated base class contains member variables for
every widget
every widget model(What are Widget Models good for?)
every embedded page e.g. NotebookPage
an array of all widget models which is used by the runtime
There are generated methods for
creating all widget models, initializing them and set the validators
creating all widgets, initializing them, setting all properties defined in the guidesigner and defining the layout
attaching all widget models to their respective widgets
dispatching events to the correct widget event method(Widget Events)
removing all widgets
removing all widget models
All these methods are called by the runtime.
The generated base class contains member variables for
every widget model(What are Widget Models good for?)
every embedded page e.g. NotebookPage
an array of all widget models which is used by the runtime
There are generated methods for
creating all widget models, initializing them and set the validators
transferring data(Generated Transfer-Code for Atomic Widget Models) from the widget models to business data objects and back
removing all widget models
You should call the transfer methods whenever you need to synchronize the widget models with the business data objects. The other methods are called by the runtime.
The information provided here is intended to be some background information to get a better understanding of what is going on when pages are opened or closed. Most of the methods presented here are implemented by the generated base class.
The following methods are called by the runtime to initialize a page.
In the Constructor(Calling a Dialog Page) of the page the following information is stored in the page.
If the page is statefull or stateless on the server. This information is supplied by the generated base class. You define it by defining the property ynStateless of the page in the guidesigner.
The component containing this page. You pass this information to the constructor, or it is derived from the parent page.
The parent page of this page. This information is only supplied if you pass it to the constructor.
For dialogs pages: The style of the shell and the parent shell. The style of the shell is supplied by the generated base class. You define it by defining the properties codModality, ynClose, ynMin, ynMax, ynResize of XMADialogPage in the guidesigner. The parent shell is only supplied if you pass it to the constructor.
For notebook pages: The parent notebook. It is supplied by the generated base class of the containing page.
Next createModels() is called (in the constructor). If you set the property ynModelLazyGenerated to true in the guidesigner, this method is called when the page is becoming visible the first time. It is called directly before createWidgets(). If the property ynModelLazyGenerated is set to false, is called directly from the constructor of the generated base class. Default is false and calling it from the constructor.
The method is implemented in the generated base class.
It creates all widget models(What are Widget Models good for?) of the page and sets their respective validators(Formatters/Validators). You define the validators in the guidesigner.
It registers the page at the component. From this time on, all widget models of the page will be synchronized with the server.
It creates all subpages with ynModelLazyGenerated set to false.
After invoking the page (by calling invoke()) createWidgets() is called. This method is called when the page is becoming visible the first time. If the widget models(What are Widget Models good for?) are not already created (ynModelLazyGenerated is true), createModels() is called first. The method is implemented in the generated base class. Here the main part of the SWT related initialization is done. Everything that is done here is to be defined in the guidesigner. It is first called on the page itself and then on all subpages.
It creates the composite of the page, if not already created
It instantiates the widgets with its styles
It sets constant texts (title, labels, tool tips) and images
It registers the event adapter
It defines the layout (attachments, distances, sizes)
It sets the initial focus
It defines the tab order
Next enter() is called. This method is called every time when the page is becoming visible. For DialogPages this method is called exactly once. For NotebookPages and WizardPages this method is called every time the user navigates to the page. For EmbeddedPages it depends on the page where they are embedded.
In this method you will place your initialization code for the page; e.g. populating the widget models with data.
It is first called on the page itself and then on all subpages.
Next determineState() and stateChanged() are called. This methods are called every time one of the following events is fired by SWT: verifyText, modifyText, focusLost, widgetSelected, widgetDefaultSelected. This means on every keystroke on an editable widget and every time a selection or the focus changes. For widgetSelected and widgetDefaultSelected they will be called after the event method you defined in the guidesigner for the widget.
This methods will also be called immediately after enter() . So it will be guarantied, they are called once before the page becomes visible.
In this methods you will place your code for enabling and disabling widgets.
Both methods are first called on the page itself and then on all subpages.
Upon termination of a page, as by closing a dialog, the runtime calls the following methods.
close() is called every time the dialog receives a close event. It is not called if you call closeOK() or closeCancel() .
In this method you can do some cleanup and decide if you really want the dialog to be closed. If you return false, the dialog will not be closed and the rest of these methods will not be called.
leave() is called every time the page ends to be visible. For DialogPages this method is called exactly once. For NotebookPages and WizardPages this method is called every time the user navigates to another page. For EmbeddedPages it depends on the page where they are embedded.
In this method you can place some cleanup code.
It is first called on all subpages and then on the page itself.
Next removeWidgets() is called. The method is implemented in the generated base class.
It sets all member variables for the widgets to null.
It is first called on all subpages and then on the page itself.
removeModel() is not called automatically by the runtime, since you may still need to be able to read the data of the page after the dialog is closed. You have to call it yourself, if you have more dialog pages in your component. When the component terminates all pages of the component are freed automatically.
The method is implemented in the generated base class.
It sets all member variables for the widget models to null.
It de-registers the page at the component. With the next RPC the page will be freed at the server.
Normally page initializations are done by the enter() method (and often an RPC is called from there). This method will be called after the page window and its widgets are created (but still invisible). Unfortunately the invoke mechanism has not finished completely at this point so that a simple closeCancel() produces an unexpected behavior (a NullPointerException). Also for more tricky solutions like using Display.asyncExec() a safe, consistent system status can not be guaranteed.
A better way is to ensure the correct page initialization before invoking the page window with its widgets. Models (on client and server side) are already created in the page's constructor (if in the gui designer ynModelLazyGenerated is false - which is the default). At the client side the GUI related objects like widgets, the dialog or the shell is not existing at this point. These are created in the invoke() method (see Creation of the page). So you can implement an initialization method, for example init(), that can also call a RPC method for server side initialization. Call this method before invoke(). If an error occurs the page needs to be invoked. The following demo code, based on the auto generated sample from the XMA project creation wizard, should show this in detail.
Let‘s assume, that a main page TestDlg in a component Example has a RPC called init(), that is called from the client's enter() method. In the example below we can a NullPointerException because the inconsistent state caused by the exception thrown in enter().
Bad example:
// client.Example:
public void invoke(Composite parentComposite) {
if ( parentComposite instanceof Shell ) {
new TestDlg(this,(Shell)parentComposite).invoke();
} else {
new TestDlg(this).invoke();
}
}
// client.TestDlg:
protected void enter() {
newRemoteCall("init").execute(); // pray for no exception here
}
// server.TestDlg:
public void init (RemoteCall call, RemoteReply reply) {
simpleCombo1.add(new String[] { "colt", "hat", "horse" });
simpleCombo1.select("hat");
if ( goodBadUgly() != GOOD )
throw new AppException("Not the good guy.");
}Instead we can simply call the RPC in a method called before invoke(), for example init() (the server's init() stays the same).
Good example:
// client.Example:
public void invoke(Composite parentComposite) {
TestDlg dlg = null;
if ( parentComposite instanceof Shell ) {
dlg = new TestDlg(this,(Shell)parentComposite);
} else {
dlg = new TestDlg(this);
}
dlg.init(); // exception will break here
dlg.invoke();
}
// client.TestDlg:
protected void init() {
newRemoteCall("init").execute(); // no fear
} What happens now if an Exception is thrown by init()? The invoke() will never be reached, so the page window will not be displayed. The Exception will be caught outside Example.invoke() by the XMA Runtime and displayed in a generic message box.
On the client side there exist some different base classes for pages. The generated base class of your page class is always derived from one of these. On the server side all pages are derived from PageServer.
A DialogPage is a page representing a dialog. It has its own window.
For using this class see Programming a Client Side Page.
A NotebookPage represents one page of a notebook. It becomes visible, when the corresponding tab is selected. It becomes invisible, every time an other tab is selected. It can only exist inside an other page.
To create a NotebookPage, first you create a tabfolder in the guidesigner. Then you create the NotebookPage as child of the tabfolder in the guidesigner.
From the enclosing page, you can access the NotebookPage via the variable with the instance name you has chosen in the guidesigner. You can use this instance on client and server side.
If you want to show some tabs in the tabfolder only if a certain condition is true, you can do the following:
You prevent the automatic addition of the tab in the generated code by setting the property YNDynamic(Notebook Page) of the corresponding NotebookPage to true in the guidesigner.
You create the NotebookPage and add it to the Notebook by calling addPage() in enter() of the Page containing the Notebook, if your want to show it.
pageOne = new PageOne(tabfolder);
tabfolder.addPage(pageOne);You can remove it again later by calling removePage() .
An EmbeddedPage represents a rectangular area inside a page. It can be used to show the same group of widgets on different pages. It can also be used as main page of an embeddable component(Embedded Component). It is visually embedded into an other page and can only exist inside an other page or inside an embeddable component.
For using this class see Embedded Page.
A special DialogPage is a WizardPage, which is a frame for several EmbeddedPages (see EmbeddedPage) and navigates thru them. Normally the last page finishes the use case. Read more in Wizards
An AppShell represents a main window of an application. There are two classes derived from AppShell:
Implements SWT-menus the shell's menu bar. It looks like a standard windows application menu.
Implements a tree used as menu (usually located at the left side of the window). Its menu looks like the navigation tree of web application
For using these classes see .
A wizard as user interface term means a multi step dialog, that is well focused to one use case and will be normally committed on the last step. Wizards in openXMA are realized by
A frame dialog that can be created by the GUI designer.
N embedded pages ().
A more or less complex flow of these sub pages. The simplest and most common flow is a linear followup of all the pages.
Wizards often have a start (welcome) page with explaning texts, also can each page have an explanation area on top with a headline, some explaining text and maybe an icon.
Some wizards can be finished earlier, not only on the last page, in that case the values of the last, skipped pages will be taken as default.
To implement a simple wizard (e.g. linear flow, server call only on the finish button) in XMA following things has to be done:
Create an XMA Wizard Page
Create the embedded pages
Create an array of WizardSubPageInfo for them and pass it to the setPageInfos() method of the XMA Wizard Page on client side.
Implement the saveModels() method of the sever side XMA Wizard Page.
The wizard-relevant properties for each sub page are defined by the WizardSubPageInfo attributes, according tho that there are several constructors. For each page can be defined
is ist the first or last page iun the wizard flow
can the wizard finished here, if it is not the last page
an explanation title and text
an explanation icon
In some cases a server-side initialization is necessary for a new sub page, sometimes a server-side check of the data entered in a sub page is needed before leaving the page. Be aware, that RPC calls always can result in thrown exceptions. In case of a wizard, the behaviour we want to have normally is, that the current page should stay and a message should be displayed. So it's not a good idea, to do such server invocations in the sub pages enter() and leave() methods.
A much better place is an overwritten WizardPageClient.getNextPageInfo() method, where a server call can be made before or after the superclass implementation call:
protected WizardSubPageInfo getNextPageInfo( WizardSubPageInfo act, boolean nextPressed ) {
if ( act != null && act.page instanceof AddressPage )
((AddressPage) act.page).newRemoteCall("exit").execute();
WizardSubPageInfo next = super.getNextPageInfo(act,nextPressed);
if ( next.page instanceof AddressPage )
((AddressPage) next.page).newRemoteCall("init").execute();
return next;
}The example above shows such an overridden implementation, that calls an init() and an exit() RPC for the sub page AddressPage. The exit() RPC could e.g. do a check against the database an throw an AppException on fail - in that case AddressPage stays on screen and the exception message would be sicplayed in a message box.
By implementing (overriding) getNextPageInfo() every kind of wizard flow is possible. One possible solution could be to have more than one flows (let's call them lane in that case) with options to jump from one to another flow (changing lane) depending on user descisions on some pages.
An other idea is to subclass WizardSubPageInfo and build a tree structure with the instances. The descision logic can also resider in that classes/entities. So the code of getNextPageInfo() can be kept small and simple.
Also server side control of the wizard flow can be be easily done in that way.