http://www.developer.com/

Back to article

Creating a App using JScript .NET and Windows Forms


February 22, 2002

In the second article of this series, I briefly described how the JScript .NET and the .NET Framework allow JScript developers to create Windows-style applications using Windows Forms. Windows Forms is a part of the .NET Framework that enables developers to take advantage of Windows' graphical interface capabilities.

A Windows Form represents a traditional graphical interface through which users interact with your application. Windows Forms offers a cohesive programming model that makes it easy to build forms that have rich capabilities and a high degree of interactivity. This article walks you through a sample application written in JScript .NET that implements a Windows Forms application that features and uses these types of controls:

  • Main menu
  • Context menu
  • Tab strip control
  • Tree View control
  • Checked List Box control
  • Drop Down menu
  • Progress bar
  • Picture box control
  • Timer
  • Month calendar control

Figure 1 shows the completed application featured in this article.

Figure 1 - Sample Application

Understanding the Windows Forms Programming Model

As with most other aspects of the programming with the .NET Framework, a Windows Form is an object that derives from a base class, which implements most of the form's behavior. Your job is to add controls and customize the behavior of the form for your application. The Windows Forms package primarily resides in the System.Windows.Forms namespace; however, you'll also require access to members of the System.Drawing namespace, which supports accurate the Windows Forms package by allowing you to accurately specify control placement and size.

Your application's form acts as a container for the controls, like buttons and text boxes, that reside in it. In fact, you must programmatically add your controls to the form's Controls collection for them to be visible at runtime. Whenever a user clicks on a control, or interacts with it in some way, the control raises events, which give your application the opportunity to respond to the user's actions. When you create a control, you can specify what events a control responds to and can provide your implementation for the event. For example, the Stop button at the bottom of the form in Figure 1 stops the progress bar from updating - the code to implement that behavior resides in the buttons Click event handler.

To create a basic form, you need to import some namespaces and define a class that represents the form and contains its controls, as shown in the following listing:

import System;
import System.Drawing;
import System.Windows.Forms;

public class sampleForm extends System.Windows.Forms.Form {

  private var btnProgressControl : System.Windows.Forms.Button;

  public function sampleForm() {
    this.btnProgressControl = new System.Windows.Forms.Button();
    this.btnProgressControl.Text = " Stop ";
    this.btnProgressControl.add_Click(btnProgressControl_OnClick);
  }

  function btnProgressControl_OnClick( source:Object, e: EventArgs )
  {
    //...
  }
}

Application.Run(new sampleForm);

The sampleForm class extends the .NET Class Library's Form class, which provides basic forms support. The class's constructor initializes a Button object by setting its Text property (the button's caption) and defines the button's Click event handler. The event handler is a function that takes two arguments, as shown in the listing. You can name the event handler function anything you want, but the function's signature (the function's argument types) must appear as shown (you can use any names for the arguments but the types have to be Object and EventArgs). Associate the event handler with the button's click event by calling the Button object's add_Click method, passing it the name of the event handler function. The application starts executing through the Application object's static Run method, which takes an instance of Windows Form object, makes it visible, and processes system messages.

Working with Windows Forms Controls

The sample application includes a number of controls, all of which are easy to work with. When you start the sample application (you can download the sample's source code using the link at the end of this article), the tab strip control starts with the tree view control visible, as shown in Figure 1. The tree view control organizes information using a hierarchal structure - you can access lower-level members by clicking on the plus symbol next to the root nodes.

When you select a node and click the "Show Current Node" button, a message box pops (see Figure 2) describing the currently selected node and its full path. Here's the code that handles the button's click event:

// The following line appears elsewhere in the class...
private var treeView1 : System.Windows.Forms.TreeView;

// This function handles the "Show Current Node"
// button's click event
function btnCurrentNode_OnClick( source:Object, e: EventArgs )
{
  if(treeView1.SelectedNode==null) 
    MessageBox.Show("No currently selected node\n\nPlease 
      select a node and try again",
      "Note",
      MessageBoxButtons.OK,
      MessageBoxIcon.Exclamation,
      MessageBoxDefaultButton.Button1);
  else 
    MessageBox.Show("Selected \"" + treeView1.SelectedNode.Text + 
       "\"\nLocated at: \"" + treeView1.SelectedNode.FullPath+"\"",
      "Selected Node",
      MessageBoxButtons.OK,
      MessageBoxIcon.Information,
      MessageBoxDefaultButton.Button1);
}

Figure 2 - Displaying the currently selected node

If the user does not select a node, the tree view's SelectedNode is null, otherwise, it refers to TreeNode object that describes the currently selected node. The current node's Text property has the display text shown in the tree view control, while the FullPath property contains the complete path (starting form the root node) to the currently selected node. The FullPath property is useful when you want to, for example, perform an operation on a particular file in a directory listing. You can add nodes to the tree view control using its Add or AddRange methods - see the source code for details.

The tab strip control contains a Month Calendar control under the Month Calendar tab. The control allows users to select a date based on a calendar metaphor that's easy to use and understand. The control offers a number of customization features that you can access using the control's properties. For example, the month calendar control has a FirstDayOfWeek property that allows you to specify the first day of the week (the day of the week that appears on the left side of each week in the month) - the sample application sets the FirstDayOfWeek property to Monday.

The last control the tab strip control hosts is the Checked List Box control, which is similar to a regular List Box control except that each item has a check box next to it. When the user clicks the "Checked Items" button, the following event handler executes, which also causes a message box to pop up (see Figure 3):

function btnCheckedItems_OnClick( source:Object, e: EventArgs )
{
  var i:int;
  var msg:StringBuilder = 
    new StringBuilder("Checked items:\n");
  
  if(checkedListBox1.CheckedItems.Count==0)
    msg.Append("\nNone!");
  else
    for(i=0;i< checkedListBox1.CheckedItems.Count;i++)
    {
      msg.Append("* ");
      msg.Append(checkedListBox1.CheckedItems.Item(i).ToString());
      msg.Append("\n");
    }
  MessageBox.Show(msg.ToString(),
    "Checked Items",
    MessageBoxButtons.OK,
    MessageBoxIcon.Information,
    MessageBoxDefaultButton.Button1);
}

Figure 3 - Displaying checked items

The function uses a StringBuilder object to compose a string that contains a list of the currently selected items. A StringBuilder (from the System.Text namespace) is a dynamic string that's well suited for building strings, as this function does, at runtime and is recommended instead of the System.String object, which is considered to be an immutable object. The only thing you need to watch out for is that you call the StringBuilder object's ToString method when you want to refer to its String representation, otherwise you'll get a compile-time error about a type mismatch whenever a method (like the MessageBox's Show method) requires a String object as part of its list of parameters that it accepts.

The checked list box control represents checked items as a collection, which exposes a Count property making it easy to iterate over the items the user selects as shown in the listing.

Examining the PictureBox control

A PictureBox control allows you to display images on your form, as shown on the right side of Figure 1. The control supports various types of graphic formats, including GIF and JPG. Load an image into the PictureBox control based on the contents of an Image object, which (in this example) manages a Bitmap object. It sounds complicated, but it's actually straight-forward and logical once you see the code, which I'll present shortly.

The ComboBox control (drop down menu above the PcitureBox control) manages the contents of the PictureBox control at runtime, as shown in Figure 4.

Figure 4 - Selecting a picture to display

Whenever the user selects an item from the ComboBox, an event handler executes which checks to determine if an image file exists and then loads the image into the PictureBox control. Here's the code:

function cboPicture_OnChanged( source:Object, e: EventArgs ) {
  if(cboPicture.SelectedItem.ToString()=="") return;
  var fileName:String=new String(cboPicture.SelectedItem.ToString()+".jpg");
  if(!File.Exists(fileName))
  {
    MessageBox.Show("Cannot load picture...",
      "Error",
      MessageBoxButtons.OK,
      MessageBoxIcon.Exclamation,
      MessageBoxDefaultButton.Button1);
    return;
  }
  
  var pic : Bitmap = Bitmap(Image.FromFile(fileName));
  pictureBox1.Image = pic;
}

The function composes a file name based on the currently selected item's text and a ".jpg" extension. The function uses the File class's static Exists method to ensure that the image file the user selects exists in the same directory as the sample application. If the file exists, the listing creates an instance of a Bitmap object based on the contents of an Image object, which is based on the contents of an image file. The last action the code takes is to assign the Bitmap to the PictureBox's Image property, causing the currently selected image to be displayed.

The listing checks if the required image file exists before attempting to load it because the Image object throws an exception if it cannot load the file. An exception is a relatively (computationally) expensive operation, which you should try to avoid running into to make your code execute as fast as possible. There are many cases, like this one, where you can anticipate the exception and take steps to avoid the exception in the first place, often using less expensive operations.

Working with a Timer

A Timer control is a non-visual control that can raise events based on a time interval. When you run the sample application, it appears that the code executes in parallel since the progress bar at the bottom of Figure 1 updates every two seconds - even when you interact with other controls on the form.

The Timer class resides in the System.Timers namespace - set up a timer as shown in the following listing:

// Appears in the class's declaration...
private var localTimer : System.Timers.Timer ;

// Appears in the class's constructor and InitializeComponent function...
localTimer =  new System.Timers.Timer();
localTimer.Interval=2000;
localTimer.AutoReset=true
localTimer.add_Elapsed(OnTimer);
localTimer.Enabled=true;

// Elsewhere in the class...
function OnTimer( source:Object, e: ElapsedEventArgs )
{
  if(progressBar.Value>=100)
    progressBar.Value=0;
  progressBar.PerformStep();
}

The listing creates an instance of a new timer and sets its Interval property (the frequency at which the timer fires). The AutoReset property, when true, resets the timer to zero after each Interval - essentially setting up the timer to fire again. The timer does not start until you set its Enabled property to true; the OnTimer function handles the Elapsed event each time it fires (every 2000 milliseconds, or two seconds). The OnTimer function works with the ProgressBar control to update it every two seconds.

The ProgressBar control's PerformStep method increments the position of the bar's progress indicator, as defined by the bar's Step property. The ProgressBar control has a number of properties that you can set to customize its behavior and appearance - see the sample code for details.

Working with Menus

Most modern applications implement a system of menus and shortcut keys that make it easier for novice and advanced users to take advantage of the application's functionality. Menus make an application's functionality apparent to novice users, while shortcut keys appeal to more advanced users that prefer not to use a mouse for common tasks or frequently used application functionality.

The sample application features two types of menus: a main menu (a 'traditional' menu that runs across, just under the application's title bar) and a context menu (a menu that pops up when a user right-clicks on the form), as shown in figures 5 and 6.

Figure 5 - Main Menu

Figure 6 - Context Menu

Whether you're working on creating a context menu or a main menu, the tasks you perform are the same - the only thing that changes are the names of the objects. The following listing shows how code the application's main menu (under the application's title bar):

private var mnuMain : System.Windows.Forms.MainMenu;
private var mnuHelp : System.Windows.Forms.MenuItem;
private var mnuHelpAbout : System.Windows.Forms.MenuItem;

// mnuHelpAbout
mnuHelpAbout = new System.Windows.Forms.MenuItem
mnuHelpAbout.add_Click(mnuHelpAbout_Click)
mnuHelpAbout.Text = "&About"
mnuHelpAbout.Shortcut = Shortcut.CtrlA;
mnuHelpAbout.ShowShortcut = true;

// mnuHelp
mnuHelp = new System.Windows.Forms.MenuItem
mnuHelp.MenuItems.Add(mnuHelpAbout)
mnuHelp.Text = "&Help"
mnuHelp.ShowShortcut = false;

// mnuMainMenu
mnuMain = new System.Windows.Forms.MainMenu
mnuMain.MenuItems.Add(mnuHelp)
// *** Assign the main menu to the form...
this.Menu = mnuMain;

The listing creates three objects: a MainMenu, and two MenuItem objects - configure the MenuItem objects before you add them to the MainMenu object, as shown in the listing.

MenuItem objects have a number of properties that make it easy to configure application-wide shortcut keys for an option, in addition to making keyboard based navigation possible. For example, you can access the Help menu by pressing Alt+H, and then A; alternately, you can also directly invoke the About menu by pressing Ctrl-A. See the listing for details on how to configure the menu and the shortcut keys.

Resizing Controls when the Form's Size Changes

A trait that most advanced graphical applications have a professional finish that comes from a very basic feature of Windows applications - the ability to resize a Window. When you resize a Window, like Internet Explorer's window, there are some controls that resize to match the proportions of the new window (instead of having a fixed size or staying in place as you resize the window).

Resizing controls based on the size of a containing window was a problem in the past, which often required a lot of extra code. The problem is so pervasive the Windows Forms includes automatic support for resizing controls through control anchors. For example, if you start the sample application and drag it's bottom right corner out and down, you'll see the text box, progress bar, and their associated buttons move along with the form's border as you resize it.

You can anchor a control using its Anchor property, which can have any or all values in the AnchorStyles enumeration. For example, the following line anchors the ProgressBar to the bottom, left and right of the form:

progressBar.Anchor = AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left;

When you resize the form, the ProgressBar stays at the bottom of the form, and resizes along with changes in the forms left and right borders. The button beside the ProgressBar does not change size but moves with the form's right side border because it's anchored to the bottom right of the form, as shown:

btnProgressControl.Anchor = AnchorStyles.Bottom | AnchorStyles.Right ;

If you anchor the button to the Left as well, it ends up resizing along with the form, which is an unusual visual behavior for a button control. As a result, the button's anchor has it move only with the bottom and right borders such that it maintains the same relative position as the form resizes.

Working with the sample code

The sample code works with .NET version 1.0 only. Download the ZIP file, extract its contents into a new folder, and build the sample using the build command at a command prompt. You can run the application by typing controls at the command prompt while you're in the same directory that you build the application with.

Summary

The Windows Forms package offers a cohesive programming model that makes it easy to build forms that have rich capabilities and a high degree of interactivity. Most classes you'll require to build Windows Forms applications reside in the System.Windows.Forms and System.Drawing namespaces. Windows Forms controls can be customized by programmatically manipulating their properties and methods. The next article in this series goes through the process of creating a Web Service and consuming it from a Windows Forms application, ASP.NET application, and directly from Internet Explorer.


Essam Ahmed is the author of "< a href="http://books.internet.com/books/0764548689" target="new">JScript .NET Programming" (ISBN 0764548689, Published by Hungry Minds September 2001), many articles (including some at CodeGuru.com) and book reviews (also available at CodeGuru.com). Contact Essam at essam@designs2solutions.com, or at his Web site

# # #

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date