|
AjaxSwing renderers and updaters can
be used to support custom components and to override HTML generation for
standard components (java.awt.* and javax.swing.*). Only one custom renderer per application is allowed in
the Standard Edition, and if you need more renderers you need to purchase an
appropriate commercial license. AjaxSwing maintains a map of that pairs Swing components and their renderers.
When iterating children a container such as Window to generate HTML page AjaxSwing first
determines the name of the class of each child component. Then it tries to lookup
the renderer for this component in the map of renderers. If a renderer
is found, it is instantiated and used. If a renderer
is not found, AjaxSwing tries to find a default renderer that can support
this class. If no default renderers are found, AjaxSwing uses a universal
SnapshotRenderer that can render any
component as an image on a page. Thus, the job of supporting your own GUI
components requires coding of a renderer class
and then registering it with AjaxSwing.
Developing a custom renderer
Writing renderer is similar
to writing a servlet that only produces a portion of a page.
All renderers must implement com.creamtec.ajaxswing.rendering.ComponentRenderer
interface. If you look at ComponentRenderer interface, you would see that it is very
simple and defines two methods
void initializeDocument (Document document)
throws Exception
This method is by AjaxSwing
to initialize
the newly created HTML page. It gives the renderer an opportunity to include
external stylesheets and JavaScript files and to generate other header content. This
method is only called once per page creation. Updates to page are sent
via AJAX and applied only to the page elements that have changed.
void renderComponent (Document document, Object component)
throws Exception
This method is called to append
the HTML representation of the component to the page. This code can be as simple
as <IMG> tag or as complex as <IFRAME></IFRAME> section.
AjaxSwing requires that a renderer
produces a self-contained single HTML
element with component ID that may include HTML and JavaScript child elements.
This requirement allows AjaxSwing to uniquely identify the HTML fragment
that corresponds to the component. This fragment is used to compute
the hash for state comparison, and if the fragment is changed from the
previous AJAX request the new fragment is sent to the browser as
a partial update. For instance, if you are rendering some text and some JavaScript
to go with it, the generated HTML structure should look like this:
<div id="CustomControl10461487" style="position: absolute; left: 16px; top: 195px; width: 404px; height: 26px;">
<span style="position: absolute; left: 51px; top: 6px; white-space: nowrap; color: rgb(208, 208, 208);">
Custom Control with Renderer and Updater
</span>
<span style="position: absolute; left: 50px; top: 5px; white-space: nowrap; color: black;">
Custom Control with Renderer and Updater </span>
<script type="text/javaScript" defer>$(function(){
document.title = 'Custom Control is 3D enabled';
});
</script>
</div>
The key aspects of the above example are:
-
All generated HTML is
contained in a DIV with the Swing component ID (id =
HTMLPage.getComponentName())
-
The elements
use fixed positioning to acheive one-to-one correspondence with Swing GUI (use PositionedPageRenderer.appendComponentStyle())
-
JavaScript is appended
inside the DIV container and uses defferred execution through jQuery $
document.ready construct (use HTMLPage.appendScript())
The passed document
is the output document that will be returned to the client. For HTML generatation
it is an instance of com.creamtec.ajaxswing.rendering.html.AjaxPage. Component is the instance of the component to be
rendered. Since AjaxSwing uses positioning of components and styles, it is
highly recommended to invoke PageRenderer's appendComponentStyle() method which
will append coordinates and size of the component. See API JavaDoc and CustomComponentProcessor.java
on specifics of what methods to use and required parameters.
Registering the custom renderer with AjaxSwing
Once a custom renderer class has been developed, it has to
be registered with AjaxSwing. This is done by adding an entry to the application's
properties file in the conf directory. The entry follows format <render>.<package_name>.<class_name>=
<renderer_class_name>. For example, to use our example renderer com.creamtec.ajaxswing.examples.CustomControlProcessor
for component com.creamtec.ajaxswing.ui.CustomControl in your application MyFirstApp you will add the
following line to <AjaxSwing>/conf/MyFirstApp.properties
render.com.creamtec.ajaxswing.ui.CustomControl=com.creamtec.ajaxswing.examples.CustomControlProcessor
Similarly, if you want to use your CustomControlProcessor to render Swing's JSlider
you can use
render.javax.swing.JSlider=com.creamtec.ajaxswing.examples.CustomControlProcessor
Custom Updater
As you have learned, custom renderers allow you to extend
AjaxSwing by providing logic to render unsupported controls to HTML page. But
what if the control is an input field such as a text field or a radio button?
Generating HTML for it will allow the user to see it on the page, but if the
user changes the value of the field how would it get propagated back to the
UI element such as JTextField? The solution for this is to add a custom ComponentUpdater.
Component updaters are used by AjaxSwing engine to set values received from the
browser to the UI elements. It can be as simple as calling a setText() on a
JTextField and as complicated as synchronizing the JTable model to match the
new values received from the HTML table. Just like custom renderers, updaters
need to be registered for the controls that they support. For example, the line
below tells AjaxSwing to use CustomControlProcessor to update CustomControl
update.com.creamtec.ajaxswing.ui.CustomControl=com.creamtec.ajaxswing.examples.CustomControlProcessor
Custom updaters must implement ComponentUpdater interface and provide implementation
for updateComponent method. The implementation is specific to the control and
the rendering that was used to represent it on HTML page. The parameter values
submitted by the browser are available thru HttpRequestData structure. See Javadoc
for more information.
Implementation Example for a Custom Control
WindowsThemeDemo includes a custom control and a processor which acts as the renderer
and updater for it to support the control in HTML. The source files are provided
for your reference and as a guideline for implementation. Custom control in
this demo extends JComponent and contains a JCheckBox and a JLabel. The checkbox
specifies whether the label should be drawn with "3D" effect or not.
Run WindowsThemeDemo as a stand alone Java application to see how the control works
(go to Dialogs->Fixed Dialog in the menu). If you try to run the demo as-is
in AjaxSwing you would see familiar "Unsupported class" link saying
that com.creamtec.ajaxswing.ui.CustomControl is not supported. To solve this problem
we provide CustomControlProcessor which implements both ComponentRenderer and
ComponentUpdater, and register it in conf/WindowsThemeDemo.properties file. CustomControlProcessor
does in HTML what CustomControl does in GUI - draws a shade under the label
to make it look 3D-like. It's a simple but a complete real life example.
CustomControl.java
CustomControlProcessor.java
|