Foundation Class Showdown

Comparing WFC and JFC by building a real application.

David M. Johnson

The original version of this article is still online at Dr. Dobb's Journal but the illustrations are missing.

The recent announcement of Microsoft Visual J++ 6.0 and the Windows Foundation Classes (WFC) caused quite a stir in the Java community. According to Microsoft, WFC provides Java developers with a complete framework for creating full-featured and high-quality Windows applications with Java. WFC applications will run only under Windows, but this is not a limitation. This allows them to take advantage of the rich functionality that Windows provides.

On the other hand, Java purists argue that WFC is unnecessary: Sun’s standard version of Java and the Java Foundation Classes (JFC) already provide a framework for developing full-featured and high-quality applications for Windows and all other platforms. The purists see WFC as another Microsoft ploy to drive developers to Windows-only Java extensions and thus destroy the cross-platform promise of Java.

So whom are we to believe? Does Java have what it takes or is WFC the only answer for Java development under Windows? In this article I will provide an objective comparison of JFC and WFC by discussing how each was used in the development of a real application: an Internet Relay Chat (IRC) chat-client. Now, whether an IRC chat client constitutes a real application depends upon your point of view. For the purposes of this article a real application is an application that serves a useful function and provides a modern GUI complete with tool-bars, tool-tips, accelerator keys, menus with icons, tabbed dialogs and a Multiple Document Interface (MDI).

I am going to start off by presenting the requirements for the IRC chat client application, which I’m calling Relay for lack of a better name. Following that I will discuss the implementation of the chat engine that will be used in both the JFC and WFC versions, the implementation of Relay-JFC, the implementation of Relay-WFC and finally I’ll take a look at how the resulting applications compare in terms of performance and usability.

Chat Client Requirements

The requirements for the Relay chat client are simple. Using Relay you should be able to log into an IRC chat server, join one or more chat channels, view the messages being sent to those channels, view the nicknames of users in those channels and send messages to those channels. Relay must do this by using the standard IRC protocol as specified in Internet RFC1459. Table 1 shows these requirements in a little more detail.

Table 1: IRC Chat Client Requirements

Chat Engine Architecture

The first step in the development of Relay was the development of a GUI independent chat engine to be re-used in both the JFC and WFC implementations of the chat client. Figure 1 shows the class and interface relationships that make up the chat engine architecture.

The chat engine itself, represented by the IChatEngine interface, is responsible for managing the connection to the IRC server, parsing incoming messages and sending outgoing messages. The chat engine works with the chat view manager, represented by the IChatViewManager interface, to ensure that incoming messages are displayed in the appropriate views.

The chat application, represented by the IChatApp interface, manages the user-interface for the chat client. This includes the menus, toolbars, dialogs and the creation of MDI client windows to server as views for each chat channel. The core chat engine provides implementations for the IChatEngine and IChatViewManager interfaces. The JFC and WFC versions will each provide their own implementations of the IChatApp and IChatView interfaces.

jfc_ss2.gif (18153 bytes)

Figure 2: Relay-JFC Running Under Windows NT

JFC Overview

Sun created the JFC to address the widely acknowledged problems with Java’s original AWT user-interface toolkit. AWT was based on the native GUI components, known as peers, of each platform and this caused subtle but problematic variations in behavior and look-and-feel from platform to platform. JFC addresses this problem by avoiding the use of peers altogether. JFC components are written in pure Java to ensure identical behavior across all platforms. JFC provides pure Java or "lightweight" components to replace all of the fundamental GUI components found in AWT such as buttons, edits, combo-boxes, radio buttons, scrollbars and menus.

AWT was criticized for its "lowest common denominator" approach to GUI portability. AWT only provided the GUI features that were common to all of its platforms. JFC addresses this problem by providing a very rich set of GUI components including table controls, tree controls, image buttons, internal frames as well as a rich-text and HTML capable text editor framework.

JFC 1.1, which was released in February of this year, includes the "Swing" GUI components, an accessibility API and a pluggable look-and-feel architecture. The pluggable look-and-feel architecture enables applications to dynamically select the Windows, Motif or the new Java look-and-feel at runtime. It is also possible to develop your own pluggable look-and-feel. Due to a licensing issue, the Windows look and feel is only available on Windows platforms. JFC will become part of the standard Java Development Kit (JDK) and the run time environment when the JDK 1.2 is released later this year. At that point JFC will be expanded to include a 2D API and drag-and-drop support.

Developing Relay-JFC

Relay's user-interface requirements are relatively simple, but fulfilling these requirements with AWT would have been very difficult. Luckily, JFC provides all of the components required for Relay. Table 2 summarizes the components that are used in each part of Relay-JFC and Figure 2 shows a screenshot of Relay-JFC running under Linux. Most of these components are pretty standard run-of-the-mill components that need no introduction. I am going to cover the more interesting components that were used in Relay-JFC: JDesktopPane, JInternalFrame, AbstractAction and JTextPane.

Table 2: JFC Components used in Relay-JFC

Internal Frames

The MDI style interface used by Relay-JFC is provided by the JFC internal frames classes. The major players in the JFC internal frame architecture are the JInternalFrame, JDesktopPane and the DesktopManager interface. Internal frame windows, similar to MDI client windows under Windows, must be derived from the JFC JInternalFrame class.

The JFC JDesktopPane class serves as container for internal frames, but delegates the responsibility of managing the activation, opening, closing, dragging resizing and iconification of the internal frames to a DesktopManager interface. You can provide your own DesktopManager implementation, but you will probably need to do this only if you are developing your own pluggable look-and-feel. Example 1 shows a simple JFC desktop pane application that will create a new internal frame every time you hit the "Add Internal Frame" button.

Example 1


import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
/** 
 * Frame with desktop pane and button that creates internal frames.
 */
public class example1 extends JFrame {
   private JDesktopPane _desktop = new JDesktopPane();
   private JButton _button = new JButton("Add Internal Frame");
   public example1() {
      super("example1");
      // Add button in the north and desktop pane in center
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(_button,"North");
      getContentPane().add(_desktop,"Center");
      // Add listener to button to create new internal frame
      _button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            _desktop.add(new intframe(),JLayeredPane.PALETTE_LAYER);   
         }
      });
      // Add listener to window to exit on window close
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
      // Size and display the frame
      setVisible(true);
      setSize(500,400);
   }
   public static void main(String[] args) {
      new example1();
   }
}
/**
 * Internal frame containing a button 
 */
class intframe extends JInternalFrame {
   public intframe() {
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(new JButton("Internal Frame"));
      setSize(200,200);
      setVisible(true);
   }
}

Actions

Relay-JFC supports a small set of user actions such as connect, disconnect, join and setup. The user may initiate these actions from the toolbar or from the main menu. The JFC Action interface provides a way to encapsulate such actions so that JFC can manage the presentation of toolbar icons, menu icons and tooltip help text.

To take advantage of the JFC action architecture, you must provide an implementation of the Action interface for each user action that your application will support. JFC provides a default implementation of Action called AbstractAction that supports the standard properties that the JFC toolbars and menubars will expect. If you derive your action classes from AbstractAction, then you will only have to implement the actionPerformed() method for each of your actions. Example 2 shows a simple JFC application that uses AbstractAction classes to provide two menu items and two corresponding toolbar buttons.

Example 2

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
/**
 * Frame with menu bar, toolbar and two actions.
 */
public class example2 extends JFrame {
   JMenuBar _menubar = new JMenuBar();
   JToolBar _toolbar = new JToolBar();
   Action _action1 = new Action1();
   Action _action2 = new Action2();
   public example2() {
      super("example2");
      getContentPane().setLayout(new BorderLayout());
      // Create menu and add actions
      JMenu menu = new JMenu("Menu",false);
      menu.add(_action1);
      menu.add(_action2);
      _menubar.add(menu);
      setJMenuBar(_menubar);
      // Create toolbar and add actions
      _toolbar.add(_action1);
      _toolbar.add(_action2);
      getContentPane().add(_toolbar,"North");
      // Add listener to window to exit on window close
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
      // Size and display frame
      setVisible(true);
      setSize(500,400);
   }
   /** Private inner class for action 1 */
   private class Action1 extends AbstractAction {
      public Action1() {
         super("Action1");
      }
      public void actionPerformed(ActionEvent e) {
         System.out.println("You requested action 1");
      }
   }
   /** Private inner class for action 2 */
   private class Action2 extends AbstractAction {
      public Action2() {
         super("Action2");
      }
      public void actionPerformed(ActionEvent e) {
         System.out.println("You requested action 2");
      }
   }
   public static void main(String[] args) {
      new example2();
   }
}

Text Pane

One of the requirements for the Relay chat client is to display color-coded chat messages. Informational messages, such as "HaXXor has left the #raleigh channel" or "RaTBoY has quit IRC," should be displayed in a different colors than that of the chat conversation. To accomplish this, Relay-JFC uses the JFC JTextPane class. JTextPane is part of a JFC framework for developing viewers and editors for HTML, RTF and similar formats. As a result of this, the JTextPane API is a somewhat complex. Example 3 shows a simple JFC JTextPane application that displays red text and blue text when you hit the corresponding buttons.

Example 3

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.text.*;
/**
 * Frame with text pane and buttons to display red and blue text.
 */
public class example3 extends JFrame {
   private JTextPane _display = new JTextPane();
   private JButton _redbutton = new JButton("Add Red Text");
   private JButton _bluebutton = new JButton("Add Blue Text");
   private StyleContext _styles = new StyleContext();
   public example3() {
      super("example3");
      // Create panel to hold blue and red buttons
      JPanel buttonpanel = new JPanel();
      buttonpanel.setLayout(new FlowLayout(FlowLayout.CENTER));
      buttonpanel.add(_redbutton);
      buttonpanel.add(_bluebutton);
      
      // Add button panel in the north and text pane in center
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(buttonpanel,"North");
      getContentPane().add(_display,"Center");
      // Define named styles for displaying blue and red text
      Style def = _styles.getStyle(StyleContext.DEFAULT_STYLE);
      Style bluestyle = _styles.addStyle("blue",def);
      Style redstyle = _styles.addStyle("red",def);
      StyleConstants.setForeground(bluestyle, Color.blue);
      StyleConstants.setForeground(redstyle, Color.red);
      // Add listener to blue button to display blue text
      _bluebutton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            display("Here is some blue text\n","blue");
         }
      });
      // Add listener to red button to display red text
      _redbutton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            display("Here is some red text\n","red");
         }
      });
      // Add listener to window to exit on window close
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
      // Size and display the frame
      setSize(500,400);
      setVisible(true);
   }
   /** Display text using specified style */
   public void display( String text, String style ) {
      Document doc = _display.getDocument();
      try { 
         doc.insertString(doc.getLength(),text,_styles.getStyle(style)); 
      }
      catch (Exception e) {
         System.out.println("Error displaying text!");
         e.printStackTrace();
      }
   }
   public static void main(String[] args) {
      new example3();
   }
}

JFC Limitations

Although I found the design of JFC to be excellent, I also ran into some serious limitations. Probably the most alarming problem was the overall sluggishness of JFC, especially when using internal frames. JFC also exhibited some strange drawing behavior. For example, when a simple yes-no message box is dismissed, the whole application is repainted.

Another limitation is common dialogs. Windows provides common dialogs for opening files, selecting colors and choosing fonts. JFC was originally slated to include common dialogs, but due to scheduling problems they were dropped. Some of the common dialogs were shipped in the com.sun.java.swing.preview package, but the font dialog was left out entirely.

If you have AWT components that you wish to reuse with JFC, then you should be aware of another problem. Heavyweight AWT components, such as those derived from the Panel and Canvas classes, do not mix well with JFC. AWT classes that are not based on native peers, such as the layout managers, work fine with JFC. However, heavyweight AWT components do not mix well with JFC. There is an article on the Javasoft web site that discusses this issue in more depth [1].

WFC Overview

Microsoft developed WFC to provide Java programmers access to the rich set of GUI components and other services of Windows. WFC is implemented using Microsoft’s J/Direct technology, which allows a Java program to call individual functions within the Windows API. WFC builds upon J/Direct to provide an object-oriented interface to Windows in the form of a Java class library. (See Examining Microsoft’s J/Direct in the January 1998 issue of Dr. Dobb’s for more on J/Direct). WFC also wraps the HTML object model, so programmers can program HTML forms in the same way that they program standard GUI forms.

By simply providing wrappers around the Windows GUI components and COM interfaces, Microsoft has been able to provide a set of components that match the rich set of components found in JFC. WFC also allows access to the hundreds of ActiveX controls available from 3rd party developers. Windows programmers, whether they come from a Visual Basic or MFC C++ background should find that WFC is familiar territory.

Developing Relay-WFC

WFC provides all of the GUI components necessary for the Relay chat client right out of the box. For every JFC component required by Relay-JFC there is an equivalent WFC component. Table 3 summarizes the components that are used in each part of Relay-WFC and Figure 3 shows a screenshot of Relay-WFC running under Windows 95. In this section I will focus on the WFC Form, Menu and RichEdit classes. I will use the classes to develop examples that are similar the to examples that I presented in the Relay-JFC section.

Table 3: WFC Components used in Relay-WFC

Form

The WFC Form class is the base class for windows and dialogs in WFC. WFC does not make a distinction between windows and dialogs. A class derived from Form can also be used as a MDI client frame by simply specifying an MDI container Form as its MDI parent. JFC is somewhat less convenient. In JFC, frames must be derived from JInternalFrame if they are to be used within a JDesktopPane.

The WFC Form class is used as the base class for the Relay-WFC main MDI container window, the MDI client chat windows and the dialogs. Example 4 illustrates the use of Forms in an MDI style interface. It shows a simple WFC MDI application that will create a new MDI frame every time you hit the "Add New MDI Client Frame" button.

Example 4

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
/** 
 * MDI container form with button that creates MDI client frames.
 */
public class example4 extends Form {
   Container components = new Container();
   Button button1 = new Button();
   public example4() {
      setSize(new Point(300, 300));
      setText("example4");
      setIsMDIContainer(true);
      button1.setDock(ControlDock.TOP);
      button1.setText("Add MDI Client Frame");
      // Add event handler to button
      button1.addOnClick(new EventHandler(button1_click));
      setNewControls(new Control[] { button1 });
   }
   /** Handle button click by creating new MDI client frame. */
   private void button1_click(Object sender, Event e) {
      mdiframe frame = new mdiframe();
      frame.setMDIParent(this);
      frame.setVisible(true);
   }
   public static void main(String args[]) {
      Application.run(new example4());
   }
}
/**
* MDI client frame with a button.
*/
class mdiframe extends Form {
   Container components = new Container();
   Button button1 = new Button();
   public mdiframe() {
      button1.setDock(ControlDock.FILL);
      button1.setText("MDI Client Frame");
      this.setNewControls(new Control[] { button1 });
   }
}

Menu

WFC does not provide anything similar to JFC’s Action architecture. In fact, the Action architecture runs counter to the design philosophy of WFC. The designer of WFC, Anders Hejlsberg, has criticized the JDK 1.1 event model because it requires you to implement a listener class if you want to be notified of an event [2]. Implementing a listener class is not only inconvenient, but also inefficient. Every listener is, after all, a class and classes take up memory, disk space and network bandwidth if used in an applet.

To allow for more efficient event handling in WFC, Microsoft introduced the new delegate keyword to Java. A delegate is a function pointer that is to be used as a callback. WFC allows you to register a delegate for each event you wish to handle. This is similar to the style of event handling found in X-Windows and Motif as well as Rogue Wave’s zApp. Example 5 shows a simple WFC application, which uses delegates to implement simple actions for two menu items and two corresponding toolbar buttons.

Example 5

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
/**
 * Frame with menu bar, toolbar and two actions.
 */
public class example5 extends Form {
   Container components = new Container();
   MainMenu mainMenu1 = new MainMenu();
   MenuItem menuItem1 = new MenuItem();
   MenuItem menuItem2 = new MenuItem();
   MenuItem menuItem3 = new MenuItem();
   ToolBar toolBar1 = new ToolBar();
   Button button1 = new Button();
   Button button2 = new Button();
   public example5() {
      setText("example5");
      setSize(new Point(300, 300));
      // Create menu and add event handlers
      menuItem2.setText("Action1");
      menuItem2.addOnClick(new EventHandler(do_action1));
      menuItem3.setText("Action2");
      menuItem3.addOnClick(new EventHandler(do_action2));
      menuItem1.setText("Menu");
      menuItem1.setMenuItems(new MenuItem[] {menuItem2, menuItem3});
      mainMenu1.setMenuItems(new MenuItem[] {menuItem1});
      setMenu(mainMenu1);
      // Create toolbar buttons and add event handlers
      toolBar1.setDock(ControlDock.TOP);
      button1.setLocation(new Point(0, 0));
      button1.setText("Action1");
      button1.addOnClick(new EventHandler(do_action1));
      button2.setLocation(new Point(75, 0));
      button2.setText("Action2");
      button2.addOnClick(new EventHandler(do_action2));
      setNewControls(new Control[] {
      button2, 
      button1, 
      toolBar1});
   }
   private void do_action1(Object sender, Event e) {
      System.out.println("You requested action 1");
   }
   private void do_action2(Object sender, Event e) {
      System.out.println("You requested action 2");
   }
   public static void main(String args[]) {
      Application.run(new example5());
   }
}

RichEdit

Where JFC provides a group of classes which work together to form a mini framework for developing text, RTF and HTML editors, WFC just provides a simple Rich Text Format control which will meet the needs of most programmer. This control, RichEdit, certainly meets the needs of Relay-WFC. Example 6 shows a simple WFC RichEdit application that displays red text and blue text when you hit the corresponding buttons.

Example 6


import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
/** 
 * Shows inserting colored text into a RichEdit control.
 */
public class example6 extends Form {

   Button btnRed = new Button();
   Button btnBlue = new Button();
   RichEdit richEdit1 = new RichEdit();
   Container components = new Container();

   public example6() {
      initForm();    
   }
   public void dispose() {
      super.dispose();
      components.dispose();
   }
   private void btnRed_click(Object source, Event e) { 
      appendText("This is red text\n",Color.RED);
   }
   private void btnBlue_click(Object source, Event e) {
      appendText("This is blue text\n",Color.BLUE);
   }
   public void appendText(String str, Color col) {
      int begin = richEdit1.getText().length();
      richEdit1.select(begin,begin);
      Clipboard.setDataObject(str);
      richEdit1.paste();
      int end = richEdit1.getText().length();

      richEdit1.select(begin,end-1);
      richEdit1.setSelColor(col);
      richEdit1.select(0,0);
   }
   private void initForm() {
      this.setText("example6");
      this.setAutoScaleBaseSize(new Point(5, 13));
      this.setBorderStyle(FormBorderStyle.FIXED_DIALOG);
      this.setClientSize(new Point(328, 211));

      btnRed.setLocation(new Point(8, 8));
      btnRed.setSize(new Point(75, 23));
      btnRed.setText("Red Text");
      btnRed.addOnClick(new EventHandler(this.btnRed_click));

      btnBlue.setLocation(new Point(88, 8));
      btnBlue.setSize(new Point(75, 23));
      btnBlue.setText("Blue Text");
      btnBlue.addOnClick(new EventHandler(this.btnBlue_click));

      richEdit1.setLocation(new Point(8, 40));
      richEdit1.setSize(new Point(312, 160));

      this.setNewControls(new Control[] {richEdit1,btnBlue,btnRed});
   }
   public static void main(String args[]) {
      Application.run(new example6());
   }
}

Comparing WFC and JFC

The development of Relay for JFC and WFC allowed me to compare the two GUI toolkits in terms of variety of components, performance, portability, extensibility and tool support.

Both JFC and WFC support a very wide variety of component types.  They provide nearly everything that you would expect from a GUI class library and more.   Currently, WFC has a slight edge in this category. I have already mentioned that JFC does not provide full support for common dialogs. Microsoft’s Internet Controls package that ships with WFC also provides number of useful controls that have no equivalent in JFC.

In GUI performance, WFC is the clear winner. Take a look at the Relay-JFC and Relay-WFC clients running side by side and you will immediately see the difference. Even with the new dynamic and native compilers, JFC may still have problems with GUI sluggishness because of the overhead of its look-and-feel emulation.

In the area of portability, JFC is the only choice. Since JFC is written in pure Java it will run on any of the numerous operating systems that support a Java JDK 1.1.2 or better VM. One of the design goals of JFC was to eliminate the subtle differences in GUI behavior of AWT. JFC seems to have met that goal. There are probably some platform specific incompatibilities, but I have worked with JFC under Windows 95, Windows NT and Linux and I have not encountered one.

JFC was designed with extensibility in mind and this is clear in almost every part of the library. For example, with JFC it easy to provide your own plug-in editors, renderers and data models for cells within table controls, items within list boxes and other controls. WFC provides adequate extensibility, but the individual controls do not provide quite as much flexibility as those of WFC. I prefer JFC in this area.

It appears that both JFC and WFC will have excellent tool support.  Microsoft will provide support for WFC through the Visual J++ IDE and GUI builder. Microsoft has also lined up numerous third party software vendors to provide WFC components and add-ons.  Borland, Symantec and others will be providing full support for JFC in their GUI builders and most Java Beans vendors are tooling-up to offer JFC versions of their products.  

Table 4 summarizes the advantages and disadvantages of JFC and WFC.

Conclusion

JFC and WFC both provide excellent toolkits for GUI development and both will have excellent tool support. Even though they seem to have divided the Java community, JFC and WFC are signs of Java’s growing maturity and suitability for real application development. So, which of these two Java class libraries is best for you? As you might expect, the answer is "it depends." JFC is well designed, full-featured and is a natural next step for developers familiar with AWT. If you have cross-platform requirements, or you suspect that you may have such requirements in the future, then JFC is your best choice. WFC is fast, full-featured and should be familiar territory to Windows developers of all stripes. If you are specifically targeting the Windows platform, then WFC is your best choice.

--

Endnotes

Footnotes

  1. Anders Hejlsberg tells how his team designed WFC, MSDN Online, http://www.microsoft.com/msdn/news/feature/031198/andersh.htm.
  2. Mixing Heavy and Light Components, Java Developers Connection, http://java.sun.com/products/jfc/swingdoc-current/mixing.html).