Writing a GUI Application Using the .NET Classes

I l @ ve RuBoard

If you have an existing AWT Java application you want to deploy under .NET, porting it is a sensible approach. But if you have an application built using Swing, you'll most likely want to redevelop the GUI using .NET classes, for three main reasons:

  • J# does not provide support for Swing.

  • You can take advantage of the wide variety of GUI components that .NET offers.

  • Applications built using .NET classes can run much faster than their Java peers.

In this section, we'll continue with the theme of the text editor. Specifically , rather than using JDK constructs, we'll build an equivalent application using the .NET Framework. As you walk through the code, pay particular attention to areas where .NET development differs from traditional Java development. In particular, notice the object model implemented by Windows Forms and the way in which events are raised and handled.

Creating the Windows Form

To create the Windows Form, you must first create a new solution in Visual Studio .NET: open the IDE, and click New Project. The New Project dialog box will appear. In the Project Types pane, select Visual J# Projects, and in the Templates pane, select Windows Application. Give the project a name (use Editor for this example), and then click OK.

When the application is created, Visual Studio .NET will also create a default form called Form.jsl and a file containing assembly and attribute information called AssemblyInfo.jsl (which you can ignore for now). The Design View window, will display a GUI representation of Form1. To the left of the Design View window is the Toolbox (Figure 4-6), which contains controls that you can drag-and-drop onto the form.

Figure 4-6. The Toolbox and Design View in Visual Studio .NET

Before you start adding controls to the form, look at the code behind Form1. To view the code, choose Code from the View menu. The code should look like that shown on the facing page. (You might need to expand some of the contracted code to see everything.)

Form1.jsl
 packageEditor; importSystem.Drawing.*; importSystem.Collections.*; importSystem.ComponentModel.*; importSystem.Windows.Forms.*; importSystem.Data.*; /** *SummarydescriptionforForm1. */ publicclassForm1extendsSystem.Windows.Forms.Form { /** *Requireddesignervariable. */ privateSystem.ComponentModel.Containercomponents=null; publicForm1() { // //RequiredforWindowsFormDesignersupport // InitializeComponent(); // //TODO:AddanyconstructorcodeafterInitializeComponentcall // } /** *Cleanupanyresourcesbeingused. */ protectedvoidDispose(booleandisposing) { if(disposing) { if(components!=null) { components.Dispose(); } } super.Dispose(disposing); } #regionWindowsFormDesignergeneratedcode /** *RequiredmethodforDesignersupport-donotmodify *thecontentsofthismethodwiththecodeeditor. */ privatevoidInitializeComponent() { this.components=newSystem.ComponentModel.Container(); this.set_Size(newSystem.Drawing.Size(300,300)); this.set_Text("Form1"); } #endregion /** *Themainentrypointfortheapplication. */ /**@attributeSystem.STAThread()*/ publicstaticvoidmain(String[]args) { Application.Run(newForm1()); } } 

This is actually a complete working application. You can build and run it, and it will display an empty form! Your task as a developer is to add functionality to make it useful.

Before we examine this code in detail, we need to change the filename and form name to something more meaningful.

Note

In a difference from the JDK, the name of a class or form in J# does not have to be the same as the filename in which the form or class is defined. Furthermore, a single J# (JSL) file can contain the definitions of multiple public classes. However, this means that if you change the name of a file to something meaningful, you should also change the names of all forms or classes in that file to something equally meaningful because it will not happen automatically!


To change the filename, right-click on Form1.jsl in Solution Explorer (normally found on the right side of the Design View window). Choose Solution Explorer from the View menu if it is not displayed. Select Rename, and change the filename to Editor.jsl. To change the form name, switch back to the Design View window (choose Designer from the View menu), click anywhere on the form, and then change the value of the (Name) property to Editor in the Properties window. (The Properties window is normally displayed below Solution Explorer; you can display it if it is not visible by choosing Properties from the View menu.) You can change the caption of the form by setting the Text property to Editor as well.

Returning to Code View, the program commences by declaring the package name to which this form belongs, and then it imports a number of namespaces that the application uses. Although Visual Studio imports all these namespaces, you might not necessarily use classes from all of the namespaces. For example, in this application you won't use any of the classes or interfaces that the System.Data namespace contains. Table 4-1 lists the imported namespaces and describes their contents.

Table 4-1. Visual Studio .NET Default Imports for a Windows Form

Namespace

Description

System.Drawing

Provides basic graphics functionality through GDI+. It is similar in functionality to the graphics classes located in the java.awt package in the JDK.

System.Collections

Provides collection classes and interfaces, such as arrays, queues, and hash tables. The nearest equivalent in the JDK is the java.util package.

System.ComponentModel

Provides classes and interfaces that define the runtime and design-time behavior of controls and components.

System.Windows.Forms

Provides the essential classes for creating Windows-based applications.

System.Data

Provides the classes that constitute the ADO.NET architecture. (You'll learn about ADO.NET in Chapter 7).

After the import statements, the code defines the Editor class. This class represents a form ”that is, a window displayed in an application. A standalone form, the parent form of an application, and a dialog box (such as a file dialog box) are all examples of forms as defined by the .NET Framework. This class, like all other Windows Forms, extends the System.Windows.Forms.Form class in much the same way that an AWT frame extends java.awt.Frame or a Swing frame extends javax.swing.JFrame .

The default constructor for this class simply calls the InitializeComponent method, which on the surface performs a similar role to the initComponents method in the AWT application shown earlier. One thing that might strike you about this method is that there is no reference to a layout manager. This is because both forms and individual controls are positioned and sized using coordinates, or absolute sizes. This means you can catch up on all that sleep you lost when you were working out how to fit all those Java components into a GridBag layout! We'll take another look at this method when you start adding controls to the form.

The Dispose method frees any resources associated with the particular form. You do not need to call this method explicitly because it is called when you invoke the form's Close method.

Finally, the main method executes the static Run method of the Application class to start the application. The Application class exposes methods and properties that you can use to control the current application. For a complete list of the Application class's members , see the .NET Framework Class Library documentation. The Run method has three overloads, all of which run an application in the current thread:

  • Application.Run()

    This method starts the application but does not display a form.

  • Application.Run(Form mainForm)

    This method starts the application and makes the specified form visible.

  • Application.Run(ApplicationContext context)

    This method starts the application within the specified context. You can use an ApplicationContext to change the circumstances that cause the program to exit. By default, it waits for the Closed event on the main form (Form1 in this case) of the application. For more information, see the .NET Framework Class Library documentation.

    Caution

    Much of the time, if you use the Properties window or Solution Explorer to change the properties of forms and controls, Visual Studio .NET will update the corresponding statements in the code behind the form and keep everything synchronized. (This applies only to code that the Visual Studio .NET generates, not to code that you write yourself.) There is one major exception to this rule. If you change the name of a form, the Application.Run statement will not change ”it will still attempt to execute Form1. For this reason, if you change the name of a form, you should change the Application.Run statement as well (as shown below); otherwise , your application will not compile.

     Application.Run(new Editor()); 

Adding Controls to the Form

Once you've defined a form, you can add controls to it. In the Design View window, you can drag controls from the Toolbox onto the form. You'll notice that most of the controls in the Toolbox offer similar functionality to many of the Java Swing components and have similar names. To start building the Editor application, drag a MainMenu component and drop it anywhere on the form. A MainMenu control called mainMenu1 will appear in the area below the form, and a menu bar will be added to the form. You can rename this component, and any other controls, by selecting it and changing the (Name) property in the Properties window. In this case, change the name of the mainMenu1 control to editorMenu .

Creating menus , even complex ones, is a relatively simple procedure in Visual Studio .NET. Click where it says "Type Here" in the menu bar near the top of the form displayed in the Design View window, and type the text (the menu label , in AWT and Swing parlance) for the first menu item (File, in this case). The display will change to allow you to add this menu and additional menus, as shown in Figure 4-7.

Figure 4-7. Creating a menu in Visual Studio .NET

As you type the name of the menu item, the item will be created and added to the main menu automatically (as you'll see shortly, when we examine the code that is generated). You can use the same technique to add the remaining menu items to create the menus for the application. Table 4-2 lists the menu items you should create for this application. You should notice that unlike the AWT or Swing, the Windows.Forms library does not differentiate between menus and menu items; a menu (such as File ”or Edit, in this example) is a menu item that happens to contain child menu items (such as Open, Save, Exit, and so on). Note that to insert a separator, you can either right-click at the place where you want to place the separator and then choose Insert Separator from the shortcut menu, or you can type a "-" sign. As you add the menus items, also change the Name property of each item as shown in Table 4-2. (You must click away from the new menu item and select it again for the properties to be displayed in the Properties window.)

Table 4-2. Menus and Menu Items for the Editor Application

Top-Level Menu Item

Child Menu Item

Name Property Value

File

fileMenu

Open

openMenuItem

Save

saveMenuItem

Separator

separator1

Exit

exitMenuItem

Edit

editMenu

Cut

cutMenuItem

Copy

copyMenuItem

Paste

pasteMenuItem

Note

The menu built for the Editor application is very basic. If you look at the properties available for menu items, you'll see that you can add shortcuts for each menu item, and items can be rendered with check boxes to indicate on/off values. Menu items can also be disabled and hidden. You can add code to the application that selectively enables or disables, or shows or hides, menu items by dynamically modifying these properties.


If you look at the variables added to the Editor class and InializeComponent method for the form in the Code View window, you'll see the code that initializes the menu items you've just added to the form:

 publicclassEditorextendsSystem.Windows.Forms.Form { privateSystem.Windows.Forms.MainMenueditorMenu; privateSystem.Windows.Forms.MenuItemfileMenu; privateSystem.Windows.Forms.MenuItemopenMenuItem; privateSystem.Windows.Forms.MenuItemsaveMenuItem; privateSystem.Windows.Forms.MenuItemseperator1; privateSystem.Windows.Forms.MenuItemexitMenuItem; privateSystem.Windows.Forms.MenuItemeditMenu; privateSystem.Windows.Forms.MenuItemcutMenuItem; privateSystem.Windows.Forms.MenuItemcopyMenuItem; privateSystem.Windows.Forms.MenuItempasteMenuItem; ... privatevoidInitializeComponent() { this.editorMenu=newSystem.Windows.Forms.MainMenu(); this.fileMenu=newSystem.Windows.Forms.MenuItem(); this.openMenuItem=newSystem.Windows.Forms.MenuItem(); this.saveMenuItem=newSystem.Windows.Forms.MenuItem(); this.seperator1=newSystem.Windows.Forms.MenuItem(); this.exitMenuItem=newSystem.Windows.Forms.MenuItem(); this.editMenu=newSystem.Windows.Forms.MenuItem(); this.cutMenuItem=newSystem.Windows.Forms.MenuItem(); this.copyMenuItem=newSystem.Windows.Forms.MenuItem(); this.pasteMenuItem=newSystem.Windows.Forms.MenuItem(); ... } } 

The InitializeComponent method also sets the properties of each menu item using the values that you specified in the Properties window. You'll notice that the way in which menus are constructed at run time is not dissimilar to the procedure followed by the AWT or Swing. The main difference between a .NET menu and its Java equivalents is that in .NET menu items are added to menus, and menus are added to the menu bar, in the same way ”by calling the AddRange method. This method accepts an array of MenuItem objects, which are then associated with that particular menu or menu bar. For example, the following code fragment generated by Visual Studio .NET adds the File and Edit menus to the main menu bar:

 this.editMenu.get_MenuItems().AddRange (newSystem.Windows.Forms.MenuItem[]{this.fileMenu,this.editMenu}); 

The next task for you to perform is to add a control that the user can type text into. The AWT application used a TextArea . The Toolbox has two controls that at first glance look like they might provide the functionality the application requires, namely TextBox and RichTextBox . TextBox is typically used for single-line text entry, much like a Swing JTextField or an AWT TextField . However, setting its Multiline property to true and its Scrollbars property to Vertical allows it to display multiple lines of text in a way that is almost the same as a Swing JTextArea . The RichTextBox control provides richer functionality than the TextBox . For example, you can assign character and paragraph formatting. The RichTextBox control is probably overkill for this application.

In the Design View window, drag a TextBox control anywhere onto the form and then display its properties by choosing Properties from the View menu. Many of the TextBox properties are common to all controls because its parent class ( TextBoxBase ) inherits from System.Windows.Forms.Control , which is the base class for all controls that display information to a user, as Figure 4-8 shows.

Figure 4-8. The TextBoxBase Control class hierarchy

As you scroll through the list of properties, you'll see some that are familiar from an AWT and Swing perspective, but many others might be less so. You should consult the Visual Studio .NET documentation for a definitive list of all the TextBox properties. Table 4-3 shows a selection of TextBox properties that might be unfamiliar to Java developers and indicates the class from which each property originates in the Control hierarchy:

Table 4-3. TextBox Control Properties

Property

Inherited From

Description

AcceptsTab

TextBoxBase

A Boolean property that indicates whether pressing the Tab key will type a Tab character or move the focus to the next control in the tab order. Default value is false.

AccessibleDescription

Control

A string that provides a textual description of a control appearance. Typically used as an accessibility feature, for example, for users with poor vision. The default value is null.

AccessibleName

Control

A string that provides a short name for a control. Typically used as an accessibility feature. The default value is null.

AccessibleRole

Control

An AccessibleRole enum member that indicates the type of user interface element the control represents. For example, AccessibleRole members include Alert, Border, Caret, and DropList. The default value is Default.

AllowDrop

Control

A Boolean that indicates whether drag-and-drop is allowed. The default value is false.

Anchor

Control

A bitwise combination of AnchorStyles enum members that indicates which edges of a container the control is anchored to. The AnchorStyles enumeration has five members: Bottom, Left, None, Top, and Right. The default value is Top and Left.

AutoSize

TextBoxBase

A Boolean that indicates whether the size of the control will automatically adjust when the font changes. The default value is true.

CausesValidation

Control

A Boolean that indicates whether the control, on receiving focus, will cause the validation of other controls. The default value is true.

CharacterCasing

A CharacterCasing enum member that indicates whether the control will change the case of text as the user types it. The CharacterCasing enumeration contains three values: Lower, Normal, and Upper. The default value is Normal.

ContextMenu

Control

A ContextMenu item that indicates whether a shortcut menu will be associated with the control. See the product documentation for usage guidelines.

Dock

Control

A DockStyle enum member that indicates whether the control will be docked to its parent container, and if it is, which edges it will dock to. The DockStyle enum values are: Bottom, Fill, Left, None, Right, and Top. The default value is None.

ImeMode

Control

An ImeMode enum member that indicates the Input Method Editor (IME) mode of the control. An IME is typically a program that allows a user to enter characters that are not found on a standard keyboard. The values for the ImeMode enum are Alpha, AlphaFull, Disable, Hangul, HangulFull, Hirgana, Inherit, Katakana, KatakanaFull, NoControl, Off, and On. The default is Inherit. In this instance, the control will normally inherit from Form, which has a default value of NoControl.

Location

Control

A Point (a Point structure represents an integer x-y coordinate pair) that indicates the position of the top-left corner of the control relative to the top-left corner of its containing control.

For the Editor application, you should change the values of the TextBox properties shown in Table 4-4. Leave the remaining properties at their default values.

Tip

You might find it easier to locate each property if you select the Alphabetic toggle button in the toolbar directly above the Properties window. (It has an image showing an A above a Z alongside an arrow.) The properties will then be displayed in alphabetical order.


Table 4-4. TextBox Properties in the Editor Application

Property

Value

(Name)

textEditor

AcceptsTab

True

Dock

Fill (You can either type the word Fill or select the square in the middle of the diagram that appears when you click the drop-down arrow.)

MultiLine

True

ScrollBars

Both

Text

Leave blank

If you return to the Code window and examine the InitializeComponent method again, you can observe the changes that resulted from your adding the TextBox and setting its properties. As the following code shows, the two points where the .NET-generated code radically departs from the Java GUI development model are the set_Size and set_Dock accessor methods. These two methods, which were described in Table 4-3, further illustrate that the Windows GUI development model revolves around coordinate sizes (and positioning) and relative positioning.

 this.textEditor.set_AcceptsTab(true); this.textEditor.set_Dock(System.Windows.Forms.DockStyle.Fill); this.textEditor.set_Multiline(true); this.textEditor.set_Name("textEditor"); this.textEditor.set_ScrollBars(System.Windows.Forms.ScrollBars.Both); this.textEditor.set_Size(newSystem.Drawing.Size(292,266)); this.textEditor.set_TabIndex(0); this.textEditor.set_Text(""); 

Handling Events

The next step is to write code to handle the events that are triggered when the user chooses the various menu items.

To understand the event model used by Windows Forms, we'll examine the simplest of the events in the application ”the event associated with the Click event of the Exit menu item. To create the event handler method, use the Design View windows and choose the File menu item belonging to the Editor form. Click the Exit menu item when it appears. With the Exit menu item highlighted, switch to the Properties window (which should now be displaying the properties for the exitMenuItem control) and click the "lightning bolt" button (the Events button) on the toolbar. This action will display the events available for the menu item. Type the method name exitMenuItemClick in the slot adjacent to the Click event and press the Enter key. The display will automatically change to the Code View window, and a new method called exitMenuItemClick will be created to handle the Click event.

Following the conventions discussed in Chapter 3, the exitMenuItemClick method accepts two parameters rather than the single parameter you normally expect with Swing and AWT events:

 privatevoidexitMenuItemClick(System.Objectsender,System.EventArgse) 

The first parameter, sender , is a reference to the object that raised the event. The second parameter contains additional information specific to the event. For example, for some events it might contain information about which key the user pressed or the position of the cursor when the event fired . All events for Windows Forms components are passed this parameter, even if there's no useful additional information.

The click event is just one of the events supported by the menu item. All controls, and the form itself, expose other events. For a complete guide to the use of each of these events, see the Visual Studio .NET documentation.

When you work with an IDE, the actual mechanics of how events are connected to event handlers is often hidden from you. But sometimes you might need to explicitly wire events to their corresponding handlers. In a .NET Windows Forms application, you can think of an event as an action that you can code against. A user might trigger this action by moving a mouse, or code within your application or the system itself might invoke the action. The code that handles the event is the event handler .

As discussed in Chapter 3, a delegate performs the binding between an event and an event handler. Event handler methods are added to the delegate. When an event fires, the runtime will execute the delegate and cause all the associated event handlers to run. If you're using an IDE such as Visual Studio .NET, the binding of an event method to a delegate will occur automatically, but you can also write code to do it manually. This technique is useful if you want to exploit the event model in your own applications, but without using GUI components. The following code fragment, which is part of the InitializeComponent method in the Editor application, shows how Visual Studio .NET uses a delegate to bind the Click event of the exitMenuItem control to the exitMenuItem_Click method. The type of delegate used is System.EventHandler :

 this.exitMenuItem.add_Click (newSystem.EventHandler(this.exitMenuItemClick)); 

The menu item control inherits the method add_Click from its ancestor class, Control . For each type of event that a control or .NET class exposes, there is a corresponding add_XXX subscriber method and a corresponding remove_XXX unsubscriber method. The add_Click method accepts a single parameter, which is an instance of System.EventHandler that refers to the method to be executed when the delegate is invoked. (See Chapter 3 for more details about how events are raised.)

Note

In .NET, delegates support multicasting , in which multiple event handlers can be associated with a single delegate. For example, the following code fragment shows how to bind three event handlers to a button that fires when the button is resized:

 this.button1.add_Resize(new    System.EventHandler(this.firstB1_Resize()); this.button1.add_Resize(new    System.EventHandler(this.secondB1_Resize()); this.button1.add_Resize(new    System.EventHandler(this.thirdB1_Resize()); 

In the Editor application, the purpose of the exitMenuItemClick event handler method is to quit the application. You can implement this in three main ways. The first approach is to use the Application.Exit method, but this is rather draconian ”it terminates the form without giving it a chance to perform any tidying up or without saving any unsaved data. ( System.exit is the equivalent method in the JDK, and it has much the same effect.) A better solution is to use the Close method of the Form class. This method raises the Form.Closing and Form.Closed events, which can be intercepted by event handlers attached to the form and can be used to save any data or even give the user the option of vetoing the close operation. (The Form.Closing method can set a flag that prevents the form from terminating.)

The Exit event handler method should appear as follows :

 privatevoidexitMenuItem_Click(System.Objectsender,System.EventArgse) { this.Close(); } 

Tip

If you want to simply hide a form rather than dispose of it, you can call the form's Hide method (which is inherited from Control ). This method makes the form invisible but allows you to redisplay it later by calling its Show method. Note also that the Hide method, unlike the Close method, does not release the resources associated with the form.


Using File Dialog Boxes

The next task in creating the Editor application is to provide the code that allows a user to open and save files. Unlike Swing and the AWT, .NET provides dedicated file dialog boxes for both Save and Open operations. Figure 4-9 shows an example of the Open File dialog box. The user can navigate to a folder, select a file (or type in a name), and click OK. Clicking Cancel aborts the operation:

Figure 4-9. The Open File dialog box

Inheriting File Dialog Box Functionality

Both of the classes that represent the dialog boxes for Open and Save operations inherit from the System.Windows.Forms.FileDialog class. This is an abstract class that defines the common functionality of the Open and Save file dialog boxes. It defines just these two classes' functionality. You cannot directly inherit from the FileDialog class. If you want to create your own custom file dialog box, you must either inherit from the FileDialog class's parent class, CommonDialog , or from SaveFileDialog or OpenFileDialog . Figure 4-10 shows the class hierarchy of the most commonly used .NET Framework dialog boxes.

Figure 4-10. The FileDialog class hierarchy

To display a file dialog box, you call its ShowDialog method. There are two overloaded versions of this method. The first accepts no parameters, and the second accepts a single parameter that indicates the dialog box's parent form. If you pass the ShowDialog method a reference to a form, the dialog box will be modal; if you do not, it will be modeless.

Note

A modal dialog box is one that blocks the application until the user closes it. A modeless dialog box allows other forms in the same application to receive the focus.


You'll also notice that the ShowDialog method returns a member of the DialogResult enumeration, which indicates which button the user clicked to close the dialog box. In our sample application, the code tests to determine whether the value returned is equal to OK . The ShowDialog method returns the OK value when the user makes a positive choice ”for example, by clicking the OK button or double-clicking on a file. The values from this enumeration are used as the return values not just from the file dialog boxes but by other dialog boxes throughout the .NET Framework Class Library.

Table 4-5 describes the members of the enumeration.

Table 4-5. The DialogResult Enumeration Members

Value

Description

OK

Returned when the user clicks OK or double-clicks a file

Abort

Returned when the user clicks Abort

Cancel

Returned when the user clicks Cancel or when the user closes a dialog box without making a choice

Ignore

Returned when the user clicks Ignore

No

Returned when the user clicks No

None

The dialog box is still running (modeless), so nothing is returned

Retry

Returned when the user clicks Retry

Yes

Returned when the user clicks Yes

The following code shows the Open menu item event handler. You should add this method to the Editor form and connect it to the Click event of the Open menu item; in the Design View window, select the File menu item, click the Open menu item, and then use the Properties window to set the Click event of the Open menu item to the openMenuItemClick method. The code for the openMenuItemClick is shown here ”type it in:

 privatevoidopenMenuItemClick(System.Objectsender,System.EventArgse) { StringtheText= ""; //Opensafilethatauserselects OpenFileDialogofd=newOpenFileDialog(); ofd.set_Title("Pickafile"); //Passownertoensurethatitismodal DialogResultdr=ofd.ShowDialog(this); if(dr==dr.OK) { StringtheFile=ofd.get_FileName(); try { StreamReadersr=newStreamReader(theFile); theText=sr.ReadToEnd(); textEditor.set_Text(theText); } catch(System.Exceptionioe) { MessageBox.Show(this,ioe.get_Message()); } } } 

In the .NET Framework Class Library, as in Swing, the file dialog classes provide the functionality that allows the user to select a file, but they do not actually read or write to the file that has been selected. You must write the code to do this. You could use the objects in the java.io package of the JDK to read or write a file, but because the purpose of this chapter is to show you how to develop Windows Forms applications, the preceding openMenuItemClick method uses classes from .NET Framework Class Library to perform the I/O. As you can see, the I/O elements of the method use a StreamReader in much the same way that you'd use a Java FileReader . To use the StreamReader class (and all of the other .NET- related I/O classes), you must import the System. IO package at the start of the file:

 importSystem.IO.*; 

You can read data from a text input stream a line at a time using the Read method of the StreamReader class, but an alternative is to use the ReadToEnd method, which reads the entire contents of the file, including any carriage return characters. This is the approach taken by the openMenuItemClick method.

Like the event handler for the Open menu item, the event handler for the Save menu item uses .NET classes for both the dialog box and I/O. The following code shows the completed method; it also illustrates that the SaveFileDialog object is instantiated and used in the same way as the OpenFileDialog object. You should add this method to the Editor form and attach it to the Click event of the Save menu item. (You can select an existing method when you define an event handler as well as create a new one ”in this way, it is possible for two handlers to refer to the same method and execute the same code.)

 privatevoidsaveMenuItemClick(System.Objectsender,System.EventArgse) { //thesavefilemethod StringtheFile=null; StreamWritersw=null; //getthefilenametosaveas SaveFileDialogsfd=newSaveFileDialog(); DialogResultdr=sfd.ShowDialog(this); if(dr==dr.OK) { theFile=sfd.get_FileName(); try { sw=newStreamWriter(theFile); sw.Write(textEditor.get_Text()); } catch(System.Exceptionioe) { MessageBox.Show(this,ioe.get_Message()); } finally { try { sw.Flush(); sw.Close(); } catch(System.Exceptionex) { MessageBox.Show(ex.get_Message()); } } } } 

You might notice in both of the event handlers described in this section that a MessageBox is displayed when an exception is caught. We've included this because it is a really useful class that has similar functionality to the Swing JOptionPane class ”that is, it displays simple dialog-type boxes. The code in these examples calls the static Show method to display a message box. In both fragments , the code passes two parameters: a form to ensure that the box is modal, and a message. However, the Show method has 12 overloads, allowing you to specify parameters that indicate objects such as captions, buttons, icons, and default buttons . For a full guide to these overloads, see the Visual Studio .NET documentation.

Working with the System Clipboard

Working with the system clipboard is straightforward, and the code is almost identical to using the system clipboard with the JTextArea class in Swing. The .NET TextBox class inherits three system clipboard manipulation methods from its parent class, TextBoxBase : these are cut , copy , and paste . Implementing the event handler methods for the Cut, Copy, and Paste menu items is straightforward, as shown in the following code. These methods should be added to the Editor form and the methods attached to the Click event handlers of the appropriate menu items:

 privatevoidcopyMenuItemClick(System.Objectsender,System.EventArgse) { textEditor.Copy(); } privatevoidpasteMenuItemClick(System.Objectsender,System.EventArgse) { textEditor.Paste(); } privatevoidcutMenuItemClick(System.Objectsender,System.EventArgse) { textEditor.Cut(); } 

This is all the code that most applications will need to interact with the clipboard. However, you can also access the clipboard through the System.Windows.Forms.Clipboard class, which gives you much greater control. The Clipboard class has only two members, both of which are static methods: GetDataObject to retrieve from the clipboard, and SetDataObject to place items on the clipboard. The SetDataObject method has two overloads: You can either pass it an object or you can pass it an object and a Boolean flag to indicate whether the data should remain on the clipboard after the application exits. (Pass the value true to indicate this.) As the generic names of the methods suggest, you can copy and retrieve objects of different types to the clipboard.

In much the same that as you work with data flavors in the AWT, you can test the data types held on the clipboard within .NET. The following code fragment shows a modified Paste menu item event handler that tests whether the data on the clipboard is of the type Text :

 privatevoidpasteMenuItemClick(System.Objectsender,System.EventArgse) { StringtheText=""; DataObjectdobj=(DataObject)Clipboard.GetDataObject(); if(dobj.GetDataPresent(DataFormats.Text)) { theText=(System.String)dobj.GetData(DataFormats.Text); textEditor.set_SelectedText(theText); } } 

This event handler obtains the data from the clipboard by calling the GetDataObject method. The returned type is an object that implements the IDataObject interface. The object is cast to the DataObject type, which is the generic class used for storing data on the clipboard (all objects stored on the clipboard must descend from the DataObject class), and implements the IDataObject interface. The interface defines four methods, as listed in Table 4-6.

Table 4-6. Methods of the IDataObject Interface

Method

Description

GetDataPresent

Checks whether data held on the clipboard is of a given data format or can be converted to a given format. You should specify one of the formats from the DataFormat enumeration.

GetData

Retrieves data of a given data format. You should cast the return value appropriately.

GetFormats

Returns a list of all the data formats associated with the stored data. The stored data can be safely converted to any of the formats returned.

SetData

Stores data of a specified data format in the current instance.

In the preceding code fragment, if the data is of the type specified ( Text ), it is assigned to the local variable theText , and the data is then inserted into the text of the TextBox using the Textbox class's set_SelectedText method. Note that the code casts Text format data to a System.String because an unqualified String would be a java.lang.String , which would trip an invalid cast exception. In our example, the code specifically tests for a data type of Text ; however, the DataFormats class defines a large number of other data types, including Bitmap , HTML , RTF , Serializable , and WaveAudio . For a complete list of supported formats, see the Visual Studio .NET documentation.

Building and Running the Application

To compile the application, choose Build Solution from the Build menu. Any syntax errors will be displayed in the Output window. Once you have successfully compiled the application, you can run it from within Visual Studio .NET by choosing Start or Start Without Debugging from the Debug menu. Alternatively, you can run the program from the command line by navigating to the bin\Debug folder under the project folder and executing Editor.exe. Figure 4-11 shows the application running. (The form has been expanded slightly.)

Figure 4-11. The .NET Editor application running

A completed version of the Editor application is available in the Editor folder among the book's sample files.

I l @ ve RuBoard


Microsoft Visual J# .NET (Core Reference)
Microsoft Visual J# .NET (Core Reference) (Pro-Developer)
ISBN: 0735615500
EAN: 2147483647
Year: 2002
Pages: 128

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net