Demystifying the New HTML Interface Options
By Mike Pietraszak
The next generation of user interfaces is upon us. Tired, old, fixed-size applications are out. Stylized, typographic applications are in. The key technologies making this shift possible are HTML rendering and Cascading Style Sheets (CSS). You've probably noticed this trend while surfing the Web, or using the latest commercial software. User interfaces are "flattening out," and becoming more colorful and interactive. Figure 1 shows the same corporate application with a "traditional" and "new" interface. The data presented is the same, and both function the same, but the "new" version uses HTML rendering for its display.
Figure 1: Shifting to HTML user interfaces. It's out with the old (top), and in with the new (bottom).
To help developers take advantage of this shift, Microsoft has provided many options. But which is right for you? Static HTML 3.0? Dynamic HTML and script? Or compiled WFC code using the com.ms.wfc.html class? And how will you distribute your application? Through a Web page (via URL), or as a stand-alone EXE?
The New Look
Before you sort out the implementation and distribution options, you may still be wondering: "Why are developers shifting to HTML in the first place? What's so great about HTML?" HTML-rendering engines (such as Internet Explorer's SHDOCVW.DLL) inherently provide great features like flowed text (with transparency), support for irregular-shaped objects (bitmaps with transparency), hyperlinks, and CSS styles. Without an HTML-rendering engine, building applications with these features can be tedious. To provide transparency, you may have to create bitmap masks, or write your own background painting code. And you'll likely have to write some layout code yourself to resize and reposition elements when the application itself is resized. Using HTML, you get this functionality "for free."
Developers and designers are moving to HTML to take advantage of an alternative set of base controls. Links, tables, and spans are replacing menus, tree controls, and buttons. Flowed text is replacing fixed-size labels and scrollable text boxes. Figure 2 shows some of the element updates that were made while porting the application in Figure 1.
What's Hot |
What's Not |
Changeable look-and-feel via CSS |
Single look-and-feel for an application's lifetime |
Colorful textured backgrounds |
Gray dialogs |
Flowed DhText |
Fixed-size labels |
Odd-shaped transparent elements |
Fixed rectangular regions |
Hyperlinks |
Menu navigation |
DhDocuments |
Forms |
DhTables |
ListBoxes |
Flat DhForms or HTML <DIV> |
Gray 3D Buttons |
Collapsible lists |
Tree controls |
[F1] or off-line help |
Descriptive instructions built into the interface |
Cleaner Separation of Code
In the Dark Ages, all applications were monolithic chunks of code. Display code, business logic, and data access were co-mingled into a single, incoherent blob. Over time, developers learned the benefits of separating - or layering - these pieces of code. Separation of business logic from display meant that each could be updated independently without impacting the other.
Today a similar evolution is taking place, but this time the split is occurring within the display code layer. The behaviors of controls are being separated from their visual properties, as shown in Figure 3. By grouping these properties as HTML and CSS styles, a new level of flexibility is achieved. Not only can the underlying validation code be separated from the UI controls, but the "look-and-feel" of the application can also be independently modified.
Figure 3: Separation of code.
In HTML, styles are described using the <STYLE> tag, and they're assigned to a particular element by specifying the CLASS attribute for that particular element. For example, to create a "flat button" style, you could define a style like that shown in Figure 4, and then assign it to a <DIV> tag.
<html>
<style type=text/css>
<!--
div.mystyle
{
font-family: Tahoma;
font-size: 9pt;
color: black;
background: white;
border-style: solid;
border-width: 1px;
border-color: black;
width: 60;
}
-->
</style>
<div class=mystyle>
<center>Submit</center>
</div>
</html>
In WFC, you can achieve the same result by using a DhStyle class to describe the style, and a DhForm class to represent the <DIV> tag, as shown in Figure 5.
DhStyle mystyle = new DhStyle();
mystyle.setFont(new Font("Tahoma",9));
mystyle.setForeColor(Color.BLACK);
mystyle.setBackColor(Color.WHITE);
mystyle.setBorder(DhBorderStyle.SOLID, 1,
DhUnits.PIXEL, Color.BLACK);
mystyle.setWidthUnit(60,DhUnits.PIXEL);
mystyle.setAlign(DhAlignment.CENTER);
DhForm form = new DhForm();
form.setNewElements(
new DhElement[] {new DhText("Submit")});
form.setStyle(mystyle);
This pluggable nature of styles is demonstrated by the MyDhForm application shown in Figure 6. The application uses wfc.ui.Form as the outer frame to host wfc.ui.HTMLControl. The HTMLControl, in turn, hosts wfc.html.DhForm. That's where the styles come in: All the properties and attributes that comprise the look-and-feel of the application are grouped together as a "theme" class. The theme class contains DhStyle objects, as well as integers and strings representing visual attributes that can't be represented as properties on a DhStyle class.
From left-to-right, Figure 6 shows a null theme, a "marine" theme, and a "coffee" theme. The theme class can be changed independently of other form layout code, so the colors, fonts, borders, images, and alignment of elements can be changed without impacting the code that describes the core attributes of the elements, e.g. text, position, and order.Figure 6: Using DhStyle to create themes, left to right: null, marine, and coffee.
The code used to create the three themes in Figure 6 is shown in Listing One (beginning on page X). The themes can be triggered by un-commenting the declarations for PluggableTheme near the top of the source file. Each of the theme classes is defined later in the application. The "marine" theme is rather plain, and uses a navy and aqua color scheme. The "coffee" theme is a bit more hip, and uses different shades of brown, a dithered bitmap background, and a pencil logo image. All these attributes (including the image names) are stored in the theme classes.
The guts of the application, however, are held in a DhForm class - much like a <DIV> tag in HTML. You'll notice in the constructor that the elements are created first. The elements are a TitleBar, an optional image (DhImage), some text (DhText), a LinkPicker, an edit box (DhEdit), a drop-down list (DhComboBox), and a FlatButton. The TitleBar, LinkPicker, and FlatButton classes were created specifically for this sample. TitleBar and FlatButton are both derived from DhForm, which is similar to an HTML <DIV> tag. LinkPicker is derived from DhTable, which is an HTML table. The LinkPicker looks more like a list, however, because the individual cell borders aren't visible.
After the elements are created in the MyDhForm constructor, the core properties - those that can't be defined as style attributes - are set. In this case, the items in the drop-down list (DhComboBox) are added. Next, the styles for each element are set. For the MyDhForm application, each form element has its own DhStyle class, and the DhStyle classes are grouped in a theme class.
Finally, the elements (with their styles now defined) are added to the form by calling the setNewElements method. Note that order is important; the elements are added sequentially as a two-dimensional stream of HTML code. So, for example, the FlatButton must be added last if you want it to appear at the bottom of the form. Adhering to this order-based syntax allows you to reap the benefits of HTML's automatic resizing and layout capabilities.
So far, I've been referring to the MyDhForm code in Listing One as an "application," but that's a bit misleading. The code will not run on its own; it needs a "host" of some kind. That host can be either an HTML page, or a stand-alone EXE based on the wfc.ui.Form class. This hosting decision is one of the choices you'll need to make when creating an application with an HTML user interface, but it's not the only one. Using compiled code as in MyDhForm is a good option, but so is using HTML and script code. So what would lead you to choose one over the other?
HTML Client Options Explained
Once you've decided to implement your client user interface with HTML, there are many ways to target the HTML Document Object Model (DOM). The table in Figure 7 attempts to explain a few of the myriad choices, but it might be clearer to examine each option using a single sample, such as the one shown in Figure 8. When the sample is run, a string, "Hello", is shown. If the OK button is pressed, the string changes from "Hello" to "World".
Option |
When to Use |
WYSIWYG Designer? |
Require CAB/ Security Cert? |
Static HTML. Internet Explorer converts HTML document tags into DOM objects for display. |
When no user interaction is required, or user input is submitted via <form method=post></form>. |
Yes. The VJ6 HTML page designer provides visual HTML layout. |
No. |
HTML and script. HTML tags are loaded into the DOM from an HTML page. VBScript or JScript is used on the client to change the properties of objects in the DOM. |
Script is great for minimal client-side validation of fields, but lots of script code can prove fragile and unwieldy to manage. Script code is also easily viewable within a browser, so intellectual property is not secure. |
Yes. The VJ6 HTML page designer is script-aware. |
No. |
HTML and compiled wfc.html. HTML tags are loaded into the DOM from an HTML page. Compiled wfc.html classes bind at run time to objects in the DOM using ID tags, allowing property changes from code. |
Compiled code allows for more robust client-side logic, and easy componentization. By separating the HTML and compiled logic, designers can build pages and developers can attach logic. |
Yes. The VJ6 HTML page designer allows elements to be marked with ID tags. |
Yes. |
Compiled wfc.html. No HTML file is needed. Compiled wfc.html creates objects directly in the DOM. |
Elements are created in code, so maintaining and binding to HTML ID tags is unnecessary, but developers must implement the layout of the interface in code. A good option for data-driven display. |
No. Code only. |
Yes. |
Compiled wfc.ui. The DOM isn't used. This option creates ActiveX-like controls derived from Win32 USER-intrinsic controls, and Win32 GDI graphics. |
Enables creation of UI-elements that don't exist in HTML, such as tree controls. |
Yes. The VJ6 wfc.ui designer enables visual composition/design of controls. Once these controls are added to the toolbox, the VJ6 HTML page designer allows visual hosting of the controls on HTML. |
Yes. |
Figure 8: The HelloWorld application at run time.
Static HTML
The simplest choice is good ol' static HTML. "Static" is the best way to describe this option, but it's possible to achieve some level of interactivity with the model. Client-side data can be moved to the server with a form "post," or CGI scripts can be used to dish up different static pages. The upside of this model is that it's simple and clean. The pages are robust (no client-side compiled code or script), and there are no security issues (such as digital certificates or client-side permissions) to worry about.
As another plus, you can use the HTML page designer in Visual J++ 6.0 (VJ6) to build static pages. This choice is a good one when there isn't much interactivity or processing involved. An on-line specification is a good example of when to use static HTML; you can browse the specification much like a book, and little interactivity or processing is required.
Here's a sample of static HTML. The page has no way of changing itself, but the page can instead post (send) data to the server when the OK button is clicked:
<!-- HelloWorld.htm -->
<html>
<form action="/cgi-bin/server_logic.exe" method=post>
<span>Hello</span>
<input type=submit value=OK>
</form>
</html>
In this case, the server_logic.exe program could then produce and transmit a new page with "World" replacing "Hello" within the <SPAN> tag. This code won't change the "Hello" text without the server_logic.exe code running on the server, but it demonstrates what static HTML code might look like.
HTML and Script
The next rung up on the ladder is HTML and script. This is a good option when you are targeting a memory- or disk-constrained client and want lighter-weight pages that don't require a Virtual Machine and run-time classes (e.g. WFC). VJ6 is a great tool for building these types of pages because the WYSIWYG page designer also supports statement completion (IntelliSense) for script, as well as script debugging.
Script isn't compiled, however, so you can't be sure all your code will work unless you exercise all possible code paths before you deploy it. On the other hand, compiled code offers a higher level of run-time success because code must be successfully compiled before it can be executed. Compiled code offers another benefit over script for applications being deployed to the Internet: Script exposes your source code to everyone - even your competitors. Because script is embedded as readable text (mixed within your HTML tags), anyone can choose View source from the View menu of their browser. Your intellectual property is then up for grabs.
Here's a sample of HTML with script. When the button is pressed, the change method in script is called. The element with id=s_id is located, and its innerText property is assigned the new string:
<!-- HelloWorld.htm -->
<html>
<span id=s_id>Hello</span>
<input type=button value=OK onclick=change()>
<script language=jscript>
<!--
function change() {s_id.innerText = "World";}
//-->
</script>
</html>
If you choose compiled code over script, however, it's still not the end of the story. There are three options for compiled code: HTML and compiled wfc.html; compiled wfc.html only (no HTML); and compiled wfc.ui.
HTML and wfc.html
The earlier sample that described styles and themes (MyDhForm) used compiled code only in its implementation. Instead, it's also possible to define the static portions of the page in HTML (including styles) and to use compiled code to "attach" class objects to those HTML elements.
You might consider this mixture of static HTML and compiled code if you work in an environment where there are Web designers and developers. The designers can build great-looking pages using WYSIWYG editors like FrontPage, Visual InterDev, or the VJ6 HTML page designer. Then the design team can pass the pages to the development team. Somewhere during that handoff, important elements need to be tagged in the HTML with identifiers (IDs). The developers then write compiled code that hooks up to those elements with IDs to provide interactive capabilities.
The next sample consists of two files: an HTML file and a Java source file. The HTML file tags the "Hello" string with an ID of s_id. The compiled code creates a DhText object to represent this element. Once the static page has been loaded, the setBoundElements method is invoked. This binds the DhText object to the s_id element. When the button is pressed, the text property of DhText is set to the new string, and the page is instantly changed to reflect the new property.
To run this sample, you'll need to compile the source file and package the resulting class file in a Cabinet (CAB). That's easy to do with VJ6: First, create an Empty Project, at the New Project dialog box (see Figure 9).
Figure 9: Creating an empty project at the New Project dialog box.
Then select the Project | Add Web Page and rename the page from Page1.htm to HelloWorld.htm. After you click the Open button, the HTML Page Designer will appear. Pick the "Source" tab and replace all the default HTML code with the HelloWorld.htm source shown here:
<!-- HelloWorld.htm -->
<html>
<span id=s_id>Hello</span>
<input type=button value=OK id=b_id>
<object classid="java:com.ms.wfc.html.DhModule">
<param name=__CODECLASS value=HeyWorld>
<param name=CabBase value=MyBundle.CAB>
</object>
</html>
Next, select Project | Add Class and rename the class from Class1.java to HeyWorld.java. After you click Open, the source window will be displayed. Replace the code with the source shown in Figure 10.
// HeyWorld.java
import com.ms.wfc.html.*;
import com.ms.wfc.core.*;
public class HeyWorld extends DhDocument
{
DhText s = new DhText();
DhButton b = new DhButton();
public void onDocumentLoad(Object o, Event e)
{
s.setID("s_id");
b.setID("b_id");
b.addOnClick(new EventHandler(this.change));
setBoundElements(new DhElement[] {s,b});
}
private void change(Object o, Event e)
{
s.setText("World");
}
}
This class extends DhDocument, so you'll need to wait for the browser to finish loading and rendering the complete HTML page before you execute any code. If you don't wait, the element you're binding to may not have been parsed out of the HTML stream yet, so it may not exist. If you attempt to bind to a non-existent HTML ID, you'll get a null-pointer exception. To avoid this, put your code in the onDocumentLoad event handler, which is called automatically once the page is loaded.
Select Project | <ProjectName> Properties. Then click on the Output Format page, as shown in Figure 11. Check the Enable Packaging checkbox, and pick Cab Archive (.CAB) as the Packaging type. Also, rename the File name to MyBundle.CAB. Click OK to commit your choices.
Figure 11: Project properties for the CAB output format.
Now press [F5], or pick Start from the Debug menu. The HeyWorld.java file is compiled and automatically packaged into the MyBundle.CAB file before the application starts.
Compiled wfc.html
The previous sample uses IDs in the HTML page to locate, and bind to, the appropriate code elements. This, of course, means that your HTML and compiled code must always stay in perfect sync. If the IDs get removed by a lossy HTML editor, for example, you're sunk. Your compiled code will blow up with all the sound and fury of a null-pointer exception. Because of that logistical nightmare, you might want to consider using 100-percent compiled code for your application.
As with the previous sample, create an empty project, and set the project properties appropriately to create a CAB named MyBundle.CAB. This time, the HTML page is essentially blank, except for the <OBJECT> tag that loads the compiled code. There are no IDs to worry about. The HTML elements are created on-the-fly in the compiled code. Instead of extending DhDocument, as in the previous example, this sample extends DhForm. Because you're not binding to any IDed elements in the HTML page, you don't need to wait for the onDocumentLoad event - you can put your code directly into the constructor (see Figure 12).
<!-- HelloWorld.htm -->
<html>
<object
classid="java:com.ms.wfc.html.DhModule">
<param name=__CODECLASS value=HeyWorld>
<param name=CabBase value=MyBundle.CAB>
</object>
</html>
// HeyWorld.java
import com.ms.wfc.html.*;
import com.ms.wfc.core.*;
public class HeyWorld extends DhForm
{
DhText s = new DhText("Hello");
DhButton b = new DhButton("OK");
public HeyWorld()
{
b.addOnClick(new EventHandler(this.change));
setNewElements(new DhElement[] {s,b});
}
void change(Object source, Event e)
{
s.setText("World");
}
}
This option helps with the ID problem, but unfortunately, there is no visual WYSIWYG designer for this choice. You must design your forms in your head and write the associated source code by hand.
Compiled wfc.ui
The final HTML choice covered in this article actually has little to do with HTML, other than that it can be hosted on an HTML page (see Figure 13). The compiled-wfc.ui option is essentially an ActiveX control which uses standard Win32 UI components (from User32 and GDI). This is a good choice if you want to do lots of custom painting, or you want to use a Win32 control that doesn't exist as HTML intrinsic control, e.g. the Tab or Tree control.
<!-- HelloWorld.htm -->
<html>
<object classid="java:HeyWorld"
width=160 height=40>
<param name=CabBase value=MyBundle.CAB>
</object>
</html>
// HeyWorld.java
import com.ms.wfc.ui.*;
import com.ms.wfc.core.*;
public class HeyWorld extends Panel
{
Label s = new Label();
Button b = new Button();
public HeyWorld()
{
s.setText("Hello");
s.setBounds(0,0,40,16);
b.setText("OK");
b.setLocation(40,0);
b.addOnClick(new EventHandler(this.change));
this.setNewControls(new Control[] {s,b});
}
void change(Object source, Event e)
{
s.setText("World");
}
}
Again, to run this sample, you'll need to create an Empty Project and set the output format accordingly.
CABs: Still More Choices
If you examine the HTML in the previous three samples, you'll notice the <OBJECT> tags contain a mysterious parameter:
<param name=CabBase value=MyBundle.CAB>
What the heck does that do? Well, if you choose one of the compiled code options, you have yet another choice to make. Would you like the compiled classes to be downloaded each time the host URL is visited? Or would you like the compiled code to be permanently installed on the client?
The HTML tag, CabBase, is used to specify a fresh reload for each visit, while the CodeBase tag specifies a persistent client install. The CodeBase installation method can be further subdivided: Classes can be installed into a special database named the "Package Manager," or they can be installed to any arbitrary location on the client machine as .class or .zip files.
To install into the Package Manager database, a manifest called an Open Software Distribution file (OSD) is used. To install to an arbitrary location on the client's hard disk, an INF file manifest is the right choice. The OSD format is the preferred method, although the INF may be used for legacy support instances. For more information on the OSD formats, you can visit http://msdn.microsoft.com/workshop/management/osd/osdfaq.asp. Figure 14 further describes the three options for code download: CabBase CAB with OSD, CodeBase CAB with OSD, and CodeBase CAB with INF.
Option |
Description |
Installed in Package Manager? |
CabBase CAB with OSD |
Transient. CAB is downloaded when page is hit, classes are cached until page is reloaded. |
No. |
CodeBase CAB with OSD |
Installed. Browser uses OSD (packaged within the CAB) for installation. This option requires that classes be in a declared package. |
Yes. |
CodeBase CAB with INF |
Installed. Browser uses INF (packaged within the CAB) to install classes to LIB, TRUSTLIB, or other directory. |
No. Installed on client in any location. |
CabBase CAB with OSD
If you decide to install "fresh" classes each time your application URL is visited, the CabBase parameter is the right choice. This is often a good option when developing and debugging your code. The application class, HeyWorld, is located and activated through the use of a Java Moniker (classid="java:<primary classname>") and a special code class parameter (param name=__CODECLASS value="<application classname>"), as shown in Figure 15.
<!-- HelloWorld.htm -->
<html>
<object classid="java:com.ms.wfc.html.DhModule">
<param name=__CODECLASS value=HeyWorld>
<param name=CabBase value=MyBundle.CAB>
</object>
</html>
// HeyWorld.java
// Package declaration optional
import com.ms.wfc.html.*;
import com.ms.wfc.core.*;
public class HeyWorld extends DhForm
{
DhText s = new DhText("Hello");
DhButton b = new DhButton("OK");
public HeyWorld()
{
b.addOnClick(new EventHandler(this.change));
setNewElements(new DhElement[] {s,b});
}
void change(Object source, Event e)
{
s.setText("World");
}
}
CodeBase CAB with OSD
If instead, you choose to install the compiled class on the client machine in the Package Manager database, you will need to make several changes to the HTML and Java source. The HTML must be altered to include the following <OBJECT> parameter modifications:
Figure 16: Advanced CAB settings (friendly name).
<!-- HelloWorld.htm -->
<html>
<!-- NiceName used for class installation and versioning. >
<object classid="java:com.ms.wfc.html.DhModule">
<param name=__CODECLASS value=com.mycorp.HeyWorld>
<param name=UsesLibrary value="NiceName">
<param name=UseslibrarycodeBase value="MyBundle.cab">
<param name=Useslibraryversion value="1,1,1,1">
</object>
Finally, the Java source file will need to be modified. Because it will be installed in the Package Manager database, the class must be declared as a member of a specific package - in this case, the com.mycorp package (see Figure 17). The fully-qualified class name, including the package prefix, must exactly match the __CODECLASS defined in the HTML page.
// HeyWorld.java
// Package declaration required.
package com.mycorp;
import com.ms.wfc.html.*;
import com.ms.wfc.core.*;
public class HeyWorld extends DhForm
{
DhText s = new DhText("Hello");
DhButton b = new DhButton("OK");
public HeyWorld()
{
b.addOnClick(new EventHandler(this.change));
setNewElements(new DhElement[] {s,b});
}
void change(Object source, Event e)
{
s.setText("World");
}
}
CodeBase CAB with INF
If you have already created and deployed your own library of classes (e.g. for data access or complex calculations), you must likely have used a CAB and an INF file to install the classes to a specific location on end users' machines. The most common places for libraries like these are c:\Windows\Java\Lib, and c:\Windows\Java\Trustlib.
You can continue to use this method, which uses GUIDs (instead of Java Monikers) and INF files. However, unlike OSD files, which are automatically created by the VJ6 build engine, INF files must be created manually. Once they have been authored, however, the VJ6 packaging engine may be used to include the INF in the resulting CAB. Simply add the INF to the project directory (see Figure 18).
<!-- HelloWorld.htm -->
<html>
<!-- Classid used for class installation and versioning. >
<object classid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
codebase="MyDU.CAB#Version=1,1,1,1">
</object>
<!-- Run the program>
<object classid="java:com.mycorp.HeyWorld">
</object>
</html>
// HeyWorld.java
// Package declaration optional
import com.ms.wfc.html.*;
import com.ms.wfc.core.*;
public class HeyWorld extends DhForm
{
DhText s = new DhText("Hello");
DhButton b = new DhButton("OK");
public HeyWorld()
{
b.addOnClick(new EventHandler(this.change));
setNewElements(new DhElement[] {s,b});
}
void change(Object source, Event e)
{
s.setText("World");
}
}
Again, it's worth noting that this method is not recommended. Instead, the Package Manager should be considered the primary delivery destination for compiled classes.
Managing the Package Manager
The Package Manager is a special data store engineered specifically for use with the Microsoft VM for Java to download and version Java classes on a client machine. Additional details about how the Package Manager works can be found in the Microsoft SDK for Java documentation at http://www.microsoft.com/java/sdk.
As mentioned earlier, if you are using a CodeBase with an OSD, the compiled classes are installed into the Package Manager. Once installed, Package Manager classes can be removed using the Windows Explorer to view the \Downloaded Program Files directory (see Figure 19). By clicking on a Distribution Unit (the collection of Java packages in a CAB) in this view and choosing the Dependency page, the associated package names can be viewed. If a Distribution Unit is deleted, all packages within that unit are removed.
Figure 19: Windows Explorer view of installed Distribution Units.
Alternatively, the Package Manager Explorer can be used to view packages installed in the Package Manager, and delete them at a more granular package level (see Figure 20). This tool is available on the VJ6 CD at \Common\Tools\VJ98\Unsupprt\jpmview\jpmview.exe.
Figure 20: The Microsoft Package Manager Explorer.
Application Distribution
So far, the options we've explored involve HTML pages and distribution via URLs. However, by hosting a the wfc.ui.HTMLControl, you can also build applications that use an HTML-based user interface, but are run like traditional EXEs - not as pages within a stand-alone browser.
To turn the MyDhForm sample from Listing One into an executable application, start with a new project and use the Windows Application template from the Applications folder. Once the project is created, add a class to the project named MyDhForm.java, and paste the contents of Listing One into the file.
Select the Form1.java file, then select View | View Code. Above the class constructor, add the following declaration:
MyDhForm myDhForm1 = new MyDhForm();
Next, view the Form1.java file in the Designer. Select View | View Toolbox, then locate the HTMLControl in the Toolbox. Add this control to the form, and set the control's Dock property to Fill using the Properties Window grid. With the HTMLControl still selected in the Properties Window, click on the "Events" (lightning bolt) button. Locate the documentReady event, and double-click on its name. An event-handler method will be created. Modify it to look like Figure 21.
private void HTMLControl1_documentReady(Object source,
DocumentReadyEvent e)
{
DhDocument doc = HTMLControl1.getDocument();
doc.setNewElements(
new DhElement[] { myDhForm1.title, myDhForm1 });
doc.setScroll(myDhForm1.theme.scrollBars);
if (myDhForm1.theme.docColor != null)
{
doc.setBackColor(myDhForm1.theme.docColor);
doc.setBorderColor(myDhForm1.theme.docColor);
}
if (myDhForm1.theme.backgroundImage != null)
doc.setDOMAttribute("background",
myDhForm1.theme.backgroundImage);
if (myDhForm1.theme.linkColor != null)
doc.setDOMAttribute("link",myDhForm1.theme.linkColor);
this.setText(MyDhForm.titleText);
}
This attaches the MyDhForm class to the HTMLControl. And you're ready to run!
Conclusion
Making the decision to use HTML for your user interface layer is easy. Choosing how to implement that user interface, however, can prove difficult. The objective of this article is to help describe the landscape, not to plot your course. There are many options available to developers, and choosing the right path requires a serious evaluation, not only of the technology options, but also of your own skills and the strengths within your organization.
The examples referenced in this article are available for download from the Informant Web site at http://www.informant.com/ji/jifile.asp. File name: ji9902mp.exe.
Mike Pietraszak is frequently a conference speaker and author on advanced Java topics like WFC, J/Direct, and Java-COM. He has presented at conferences such as the 1998 Visual J++ Developer Conference and Exposition and Microsoft Tech·Ed 98. Mike can be reached at pzak1@hotmail.com.
Begin Listing One - MyDhForm.java
import com.ms.wfc.html.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
public class MyDhForm extends DhForm
{
FlatButton button;
String e_forms[] = {"Reimbursement 416F-4","Travel 418D",
"Acquisition 418D-2"};
String e_form_urls[] = {"<URL for 416F-4 goes here>",
"<418D URL>","<418D-2 URL>"};
static String titleText = "Request 471B-2";
TitleBar title;
DhImage image;
// PluggableTheme theme =
// new PluggableTheme(); // null theme
// PluggableTheme theme =
// (PluggableTheme) new MarineTheme();
PluggableTheme theme =
(PluggableTheme) new CoffeeTheme();
public MyDhForm()
{
// Create elements to be added to the document (form).
title = new TitleBar(titleText,theme.titleStyle);
LinkPicker picker = new LinkPicker(
"Related Forms", // List title.
e_forms, // Items.
e_form_urls, // Valid link urls for items.
theme.headingStyle, // Style object for list title.
theme.itemStyle); // Style object for items.
DhText description = new DhText(
"All requests filed by <b>10:00am</b> on the " +
"<b>last Friday</b> of each month will be " +
"processed by the following Tuesday. All other " +
"requests will be processed within 24 hours, " +
"excluding <a href=holiday.htm>holidays</a> and " +
"weekends.");
FlatButton button = new FlatButton("Submit",
theme.buttonStyle,theme.activeButtonStyle);
if (theme.logoImage != null)
image = new DhImage(theme.logoImage);
DhComboBox combo = new DhComboBox();
// Set properties for document elements
combo.addItem("000 - Accounting");
// Set styles and style attributes for document elements.
picker.setAlign(theme.pickerAlignment);
description.setStyle(theme.formStyle);
button.setWidthUnit(theme.buttonWidth,
theme.buttonWidthUnit);
if (theme.logoImage != null)
image.setAlign(theme.logoAlignment);
this.setStyle(theme.formStyle);
// Now add all of the elements (except the title)
this.setNewElements(
new DhElement[] {new DhText("<br>"),picker});
if (theme.logoImage != null)
this.setNewElements(new DhElement[] {image});
this.setNewElements(new DhElement[] {description,
new DhText("<hr>Name: "), new DhEdit(),
new DhText("<br>Dept:" + "   "), combo,
new DhText("<br><br>"), button});
}
class PluggableTheme
{
// The styles for the form.
DhStyle formStyle = new DhStyle();
DhStyle titleStyle = new DhStyle();
DhStyle itemStyle = new DhStyle();
int pickerAlignment = DhAlignment.RIGHT;
int logoAlignment = DhAlignment.LEFT;
DhStyle headingStyle = new DhStyle();
DhStyle buttonStyle = new DhStyle();
int buttonWidth = 60;
int buttonWidthUnit = DhUnits.PIXEL;
DhStyle activeButtonStyle = new DhStyle();
String backgroundImage;
String logoImage;
int scrollBars = DhScrollBars.NO;
String linkColor = "BLACK";
Color docColor;
}
class CoffeeTheme extends PluggableTheme
{
public CoffeeTheme()
{
// Define all styles.
formStyle.setFont(new Font("Tahoma",9));
formStyle.setMarginWidth(
DhMargins.TOP,-8,DhUnits.PIXEL);
formStyle.setMarginWidth(
DhMargins.LEFT,6,DhUnits.PIXEL);
formStyle.setMarginWidth(
DhMargins.RIGHT,6,DhUnits.PIXEL);
titleStyle.setBackColor(Color.BLACK);
titleStyle.setForeColor(Color.WHITE);
titleStyle.setFont(new Font("Tahoma",9,
FontSize.POINTS,FontWeight.HEAVY,
false,false,false));
titleStyle.setBorderWidth(
DhBorders.ALL,0,DhUnits.PIXEL);
titleStyle.setPaddingWidth(
DhBorders.ALL,2,DhUnits.PIXEL);
titleStyle.setBorderColor(DhBorders.ALL,Color.BLACK);
titleStyle.setMarginWidth(
DhMargins.ALL,0,DhUnits.PIXEL);
itemStyle.setBackColor(new Color(204,204,153));
itemStyle.setFont(
new Font("Arial",8,FontSize.POINTS));
headingStyle.setBackColor(Color.BLACK);
headingStyle.setFont(new Font("Arial",8,
FontSize.POINTS,FontWeight.HEAVY,
false,false,false));
headingStyle.setForeColor(Color.WHITE);
buttonStyle.setBackColor(Color.WHITE);
buttonStyle.setForeColor(Color.BLACK);
buttonStyle.setBorder(DhBorderStyle.SOLID,1,
DhUnits.PIXEL,Color.BLACK);
activeButtonStyle.setBackColor(
new Color(204,204,153));
activeButtonStyle.setForeColor(Color.BLACK);
activeButtonStyle.setBorder(DhBorderStyle.SOLID,1,
DhUnits.PIXEL,Color.BLACK);
backgroundImage = "file:c:\\vjmag\\bkgnd.gif";
logoImage = "file:c:\\vjmag\\pencil.gif";
linkColor = "BLACK";
}
}
class MarineTheme extends PluggableTheme
{
public MarineTheme()
{
// Define all styles here.
formStyle.setFont(new Font("Times New Roman",9));
formStyle.setMarginWidth(
DhMargins.LEFT + DhMargins.RIGHT,6,DhUnits.PIXEL);
formStyle.setBackColor(new Color(0,0,100));
formStyle.setForeColor(Color.WHITE);
docColor = new Color(0,0,100);
pickerAlignment = DhAlignment.LEFT;
titleStyle.setBackColor(new Color(0,153,153));
titleStyle.setForeColor(Color.WHITE);
titleStyle.setBorderWidth(
DhBorders.ALL,0,DhUnits.PIXEL);
titleStyle.setPaddingWidth(
DhBorders.ALL,2,DhUnits.PIXEL);
titleStyle.setBorderColor(DhBorders.ALL,Color.WHITE);
titleStyle.setMarginWidth(
DhMargins.ALL,0,DhUnits.PIXEL);
titleStyle.setFont(new Font("Times New Roman",11,
FontSize.POINTS,FontWeight.BOLD,true,false,false));
itemStyle.setBackColor(Color.WHITE);
itemStyle.setForeColor(new Color(0,0,100));
itemStyle.setFont(
new Font("Times New Roman",8,FontSize.POINTS));
headingStyle.setBackColor(new Color(0,153,153));
headingStyle.setFont(new Font("Times New Roman",8,
FontSize.POINTS,FontWeight.HEAVY,
false,false,false));
headingStyle.setForeColor(Color.WHITE);
buttonStyle.setBackColor(Color.WHITE);
buttonStyle.setForeColor(new Color(0,0,100));
buttonStyle.setBorder(DhBorderStyle.SOLID,1,
DhUnits.PIXEL,Color.BLACK);
activeButtonStyle.setBackColor(new Color(0,0,100));
activeButtonStyle.setForeColor(Color.WHITE);
activeButtonStyle.setBorder(DhBorderStyle.SOLID,1,
DhUnits.PIXEL,Color.BLACK);
linkColor = "008888";
}
}
}
// A vertical table -- used like a menu or list box.
class LinkPicker extends DhTable
{
// If you don't want to navigate to HTML pages from this
// picker, you can replace the DhHyperlink elements with
// simple DhText elements. Then you can catch the onClick
// event for the DhText elements in the
// com.ms.wfc.ui.Form host and change to a new DhForm.
public LinkPicker(String title, String e_forms[],
String urls[], DhStyle headingStyle, DhStyle linkStyle)
{
DhCell cell[] = new DhCell[e_forms.length + 1];
DhHyperlink link[] = new DhHyperlink[e_forms.length+1];
DhRow row[] = new DhRow[e_forms.length + 1];
cell[0] = new DhCell(title);
row[0] = new DhRow();
row[0].setNewElements(new DhElement[] {cell[0]});
row[0].setStyle(headingStyle);
this.setNewElements(new DhElement[] {row[0]});
for (int i=0; i < e_forms.length; i++)
{
cell[i+1] = new DhCell();
link[i+1] = new DhHyperlink(urls[i],e_forms[i]);
cell[i+1].setNewElements(new DhElement[]{link[i+1]});
row[i+1] = new DhRow();
row[i+1].setNewElements(new DhElement[] {cell[i+1]});
row[i+1].setStyle(linkStyle);
this.setNewElements(new DhElement[] {row[i+1]});
}
this.setCellPadding(2);
this.setCellSpacing(0);
}
}
// The heading for the form.
class TitleBar extends DhForm
{
String text;
public TitleBar(String t, DhStyle style)
{
text = t;
this.setStyle(style);
this.setNewElements(new DhElement[]{new DhText(text)});
}
}
// Cool flat button.
class FlatButton extends DhForm
{
DhStyle active;
DhStyle normal;
public FlatButton(String text, DhStyle n, DhStyle a)
{
active = a;
normal = n;
this.setNewElements(new DhElement[]{new DhText(text)});
this.setStyle(normal);
this.setAlign(DhAlignment.CENTER);
this.addOnMouseEnter(
new MouseEventHandler(this.mouseEnter));
this.addOnMouseLeave(
new MouseEventHandler(this.mouseLeave));
}
void mouseEnter(Object source, MouseEvent e)
{
this.setStyle(active);
this.setCursor(DhCursor.HAND);
}
void mouseLeave(Object source, MouseEvent e)
{
this.setStyle(normal);
}
}
End Listing One