Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Comparing WFC and JFC


Feb99: Comparing WFC and JFC

David is a software developer with Virtual Corp. and can be contacted at [email protected] virtual-online.com.


According to Microsoft, Visual J++ 6.0 and the Windows Foundation Classes (WFC) provide Java developers with a complete framework for creating full-featured and high-quality Windows applications with Java. The fact that WFC applications will run only under Windows is not a limitation; instead, it allows programs to take full 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, does Java have what it takes or is WFC the only answer for Java development under Windows? In this article, I'll compare JFC and WFC by discussing how each was used in the development of a real application -- an Internet Relay Chat (IRC) chat client called "Relay." For the purposes of this article, I define a real application as a program that serves a useful function and provides a modern GUI complete with toolbars, tool tips, accelerator keys, menus with icons, tabbed dialogs, and a Multiple Document Interface (MDI). Source code for the sample programs presented in this article is available electronically from DDJ (see "Resource Center," page 5).The complete source code for both the Relay-JFC and Relay-WFC chat-client programs is available electronically from http://relayirc.netpedia.net/.

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 messages being sent to those channels, view 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 more detail.

Chat Engine Architecture

My first step in the development of Relay was the development of a GUI-independent chat engine to be reused in both the JFC and WFC implementations of the chat client. Figure 1 illustrates 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 serve as views for each chat channel. The core chat engine provides implementations for the IChatEngine and IChatViewManager interfaces. The JFC and WFC versions each provide their own implementations of the IChatApp and IChatView interfaces.

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 also 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 rich set of GUI components, including table controls, tree controls, image buttons, internal frames, and a Rich Text and HTML-capable text editor framework.

JFC 1.1 includes the Swing GUI components (see "Java Q&A," by Mukul Sood, DDJ, October 1998), an accessibility API and a pluggable look-and-feel architecture. This architecture enables applications to dynamically select the Windows, Motif, or the new Java look-and-feel at run time. 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 with the release of the JDK 1.2. 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 straightforward, but fulfilling these requirements with AWT would have been 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 Relay-JFC running under Windows NT. 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.

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 must be derived from the JFC JInternalFrame class.

The JFC JDesktopPane class serves as a 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. Listing One and Figure 3 present a JFC desktop pane application that creates a new internal frame every time you press the Add Internal Frame button.

Actions

Relay-JFC supports a set of user actions such as connect, disconnect, join, and setup. Users 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 tool-tip 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, which 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. Listing Two and Figure 4 present a JFC application that uses AbstractAction classes to provide two menu items and two corresponding toolbar buttons.

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 color different than that of the chat conversation. To accomplish this, Relay-JFC uses the JFC JTextPane class. JTextPane is somewhat complex because it is part of a JFC framework for developing viewers and editors for HTML, RTF, and similar formats. Listing Three and Figure 5 present a JFC JTextPane application that displays red text and blue text when you press the corresponding buttons.

JFC Limitations

Although I found the design of JFC to be excellent, I also ran into some serious limitations. The most alarming problem was probably 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 be aware of another problem. Heavyweight AWT components, like those derived from the Panel and Canvas classes, do not mix well with JFC (for more details, see "Mixing Heavy and Light Components," Java Developers Connection, http://java.sun.com/products jfc/ swingdoc-current/ mixing.html).

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 lets a Java program 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," by Andrew Wilson, DDJ, January 1998.) WFC also wraps the HTML object model, so you can program HTML forms in the same way that they program standard GUI forms.

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

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 lists the components that are used in each part of Relay-WFC, and Figure 6 is Relay-WFC running under Windows 95.

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 an 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. Listing Four and Figure 7 illustrate the use of Forms in an MDI-style interface. They show a WFC MDI application that will create a new MDI frame every time you press the Add New MDI Client Frame button.

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 (see http:// msdn.microsoft.com/developer/news/ feature/031198/andersh.htm). 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 hand- ling in WFC, Microsoft introduced the delegate keyword to Java. A delegate is a function pointer that is to be used as a callback. WFC lets you 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. Listing Five and Figure 8 present a WFC application that uses delegates to implement simple actions for two menu items and two corresponding toolbar buttons.

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 that will meet the needs. This control, RichEdit, certainly meets the needs of Relay-WFC. Listing Six and Figure 9 present a WFC RichEdit application that displays red text and blue text when you hit the corresponding buttons.

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 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 a 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. Because JFC is written in pure Java, it will run on any of the numerous operating systems that support a Java JDK 1.1 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 any.

JFC was designed with extensibility in mind, and this is clear in almost every part of the library. For example, with JFC it's easy to provide your own plug-in editors, renderers, and data models for cells within table controls, items within listboxes, and other controls. WFC provides adequate extensibility, but the individual controls do not provide as much flexibility as those of JFC. 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. Inprise, Symantec, and others will be providing full support for JFC in their GUI builders, and most JavaBean vendors are tooling-up to offer JFC versions of their products.

Conclusion

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 a natural next step for developers familiar with AWT. If you have cross-platform requirements, or 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.

DDJ

Listing One

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");


</p>
   public example1() {
      super("example1");


</p>
      // Add button in the north and desktop pane in center
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(_button,"North");
      getContentPane().add(_desktop,"Center");


</p>
      // 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);
   }
}

Back to Article

Listing Two

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();


</p>
   public example2() {
      super("example2");
      getContentPane().setLayout(new BorderLayout());


</p>
      // Create menu and add actions
      JMenu menu = new JMenu("Menu",false);
      menu.add(_action1);
      menu.add(_action2);
      _menubar.add(menu);
      setJMenuBar(_menubar);


</p>
      // Create toolbar and add actions
      _toolbar.add(_action1);
      _toolbar.add(_action2);
      getContentPane().add(_toolbar,"North");


</p>
      // 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();
   }
}

Back to Article

Listing Three

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();


</p>
   public example3() {
      super("example3");


</p>
      // 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");


</p>
      // 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();
   }
}

Back to Article

Listing Four

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();


</p>
   public example4() {
      setSize(new Point(300, 300));
      setText("example4");
      setIsMDIContainer(true);
      button1.setDock(ControlDock.TOP);
      button1.setText("Add MDI Client Frame");


</p>
      // Add event handler to button
      button1.addOnClick(new EventHandler(button1_click));


</p>
      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();


</p>
   public mdiframe() {
      button1.setDock(ControlDock.FILL);
      button1.setText("MDI Client Frame");
      this.setNewControls(new Control[] { button1 });
   }
}

Back to Article

Listing Five

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();


</p>
   public example5() {
      setText("example5");
      setSize(new Point(300, 300));


</p>
      // 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);


</p>
      // 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));


</p>
      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());
   }
}

Back to Article

Listing Six

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();


</p>
   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);
      richEdit1.setSelColor(col);
      richEdit1.setSelText(str);


</p>
      int end = richEdit1.getText().length();
      richEdit1.select(end,end);
   }
   private void initForm() {
      this.setText("example6");
      this.setAutoScaleBaseSize(new Point(5, 13));
      this.setBorderStyle(FormBorderStyle.FIXED_DIALOG);
      this.setClientSize(new Point(328, 211));


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


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


</p>
      richEdit1.setLocation(new Point(8, 40));
      richEdit1.setSize(new Point(312, 160));


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

Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.