Using a Main Menu with Forms

Using a Main Menu with Forms

Menus are a common user interface element in Windows applications. With the exception of simple dialog box–based applications, practically all applications written for Windows provide a menu that enables users to interact with the application. The menu that often appears along the top edge of a Windows application is known as the application’s main menu, or menu bar. Menus that are displayed when you right-click a control are known as shortcut menus, or sometimes context menus.

Menus can be viewed as a series of parent-child relationships. An application’s main menu will usually consist of a number of top-level menu items. Typically, these top-level menu items have associated popup child windows that are displayed when the relevant top-level menu item is clicked. Further nesting is possible, as each menu item in a popup menu can have an associated child menu.

In general, the .NET Framework will automatically render your menus in a way that’s consistent with the Windows user interface guidelines. However, there are a few user interface conventions for menus that you should follow:

  • Menu items should be associated with a mnemonic character—that is, a character that’s part of the menu item name and that when pressed with the Alt key will invoke the menu item. For example, the top-level File menu is usually associated with F as a mnemonic character; pressing Alt+F on the keyboard typically invokes the File menu. The steps required to provide a mnemonic character are discussed later in this section.

  • Any top-level menu items that invoke commands and don’t create a popup child menu should have an exclamation mark after their name. This alerts the user to the fact that selecting the menu item will result in a command being invoked.

  • Popup menu items that will require additional input from the user, such as populating a dialog box, should have an ellipsis (...) after the menu item name.

The following subsections describe the steps required to create both top-level menus and shortcut menus and show you how to use menus to make your programs more user-friendly.

Creating a Main Menu

There are two basic approaches to creating menus for your applications. The first approach is to use the Visual C# .NET Menu Designer. This is the simplest and most straightforward way to add a basic menu to your application. We’ll also examine how to add menus programmatically, the second approach, so that you can gain a better understanding of the code the Menu Designer generates.

Creating a Menu Using the Menu Designer

To use the Menu Designer, drag a MainMenu control from the Toolbox to your form. The MainMenu control will automatically be attached to the top edge of your form and will also add an icon that represents the menu to an area under your form, as shown in Figure 13-2.

Figure 13-2.
The Forms Designer after a MainMenu control has been added.

As you can see, the menu initially includes a box labeled Type Here. To add a top-level menu item, click on the box, and enter the text for the menu item. After the new menu item has been added to the menu, two boxes will be labeled Type Here: one box on the top level of the menu, and one box that’s a child of the new menu item. You can continue adding menu items until the menu is populated with all the menu items you want. Figure 13-3 shows an example of a menu after several menu items have been added.

Figure 13-3.
Using the Menu Designer to create a main menu.

To add a mnemonic character to a menu item, prefix a letter in the menu item’s text caption with an ampersand (&). The mnemonic character must be unique within its parent menu, so it’s not always possible to use the first letter in each menu item. When the menu is displayed, pressing the Alt key and the mnemonic character will select the menu item.

A menu will often include a horizontal line known as a separator that serves to group related menu items. To add a separator to a menu, enter a dash (-) as the menu item’s Text property, or right-click a menu item and choose Add Separator from the shortcut menu. You also can rearrange the position of a separator or other menu items by dragging them to the desired position.

The Properties window enables you to manipulate the properties for each menu item. Simply click the menu item you want to manage, and the Properties window will be updated with the properties for that menu item. The most commonly used properties for menu items are listed below.

  • Checked  Specifies whether a check mark should be displayed next to the menu item. This property is discussed later in this chapter, in the section “Adding Check Marks to Menu Items.”

  • Enabled  Enables or disables a menu item. This property is discussed later in this chapter, in the section “Disabling Menu Items.”

  • Mnemonic  Returns the mnemonic character for the menu item, or returns 0 if no mnemonic character exists.

  • Name  Returns the identifier for the menu item.

  • Parent  Returns a reference to the Menu object that’s the current item’s parent.

  • RadioCheck  Specifies whether the check mark for a menu item should be replaced by a radio button.

  • Shortcut  Specifies a keyboard sequence that executes the associated event handler just as if the menu item were clicked.

  • Text  Specifies the text caption associated with the menu item.

  • Visible  Specifies whether the menu item is visible.

As with other controls in the .NET Framework, all properties that can be accessed through the Properties window can also be accessed through code. In the next section, you’ll learn how to create a menu directly in your code.

Creating a Menu Programmatically

Although you can easily create a menu using the Windows Forms Menu Designer, it’s a good idea to understand the steps required to create a menu programmatically. A menu bar is created in two steps: first you create MenuItem objects and assemble them into a collection of child menu items, and then you create an instance of the MainMenu class and add the MenuItem objects to it.

The following four classes are used to create menus in the System.Windows.Forms namespace:

  • Menu  Represents the base class for all other menu and menu item classes

  • MenuItem  Represents a menu item in a MainMenu or ContextMenu instance

  • MainMenu  Represents a main top-level menu for a form

  • ContextMenu  Represents a shortcut menu for a form or control

As mentioned, the first step in creating a menu is to create the menu item objects that will populate the menu. After the menu items have been created, they’re assembled to form a menu. Each menu item is the child of a MainMenu or ContextMenu object (for top-level items) or the child of another MenuItem object (for nested menus).

The MenuItem class, which has six constructors, is used to create menu items. The commonly used constructor shown here enables you to specify the menu item’s text caption, as well as the event handler that takes care of the Click event:

EventHandler fileOpenHandler = new System.EventHandler(fileOpen_Click);
MenuItem fileOpen = new MenuItem("&Open...",
                                  fileOpenHandler);

private void fileOpen_Click(object sender, System.EventArgs e)
{
    
    

}

Two other forms of the constructor create menu items that require additional work before they’re usable. The simplest of these constructors creates a menu item with an empty caption and no Click event handler, as shown here:

MenuItem fileOpen = new MenuItem(); 

The other simple MenuItem constructor enables you to pass just the menu item’s caption as a parameter, as shown here:

MenuItem fileOpen = new MenuItem("&Open..."); 

A slightly more complex version of the MenuItem constructor enables you to specify a shortcut in addition to the text caption and event handler, as follows:

EventHandler fileOpenHandler = new System.EventHandler(fileOpen_Click);
MenuItem fileOpen = new MenuItem("&Open...",
                                  fileOpenHandler,
                                  Shortcut.CtrlO); 

One MenuItem constructor allows you to specify a caption and an array of submenu items, as the following code shows:

// Create an array of menu items
MenuItem [] fileMenuItems = {
    new MenuItem("&Open"),
    new MenuItem("&Save"),
    new MenuItem("Save &As"),
    new MenuItem("E&xit")
};
// Create a menu item from the array of submenu items
MenuItem fileMenu= new MenuItem("&File", fileMenuItems);

The sixth constructor is used in Multiple Document Interface (MDI) applications, which aren’t discussed in this book, and allows you to specify how menu items should be merged.

After creating your menu items, you assemble them and add them to a MainMenu object. The MainMenu object is then assigned to your form’s Menu property. There are two constructors for the MainMenu class. The simplest version of the constructor creates a MainMenu object without associating it with any menu items, as shown here:

MainMenu mainMenu = new MainMenu();

The second version of the MainMenu constructor enables you to create a main menu that’s associated with an array of MenuItem children that are passed as a parameter to the constructor:

MenuItem fileMenuItem = new MenuItem("&File")
MenuItem viewMenuItem = new MenuItem("&View");
MenuItem editMenuItem = new MenuItem("&Edit");
MenuItem [] topLevelMenuItemArray = new MenuItem[] { fileMenuItem,
                                                     viewMenuItem,
                                                     editMenuItem };
MainMenu mainMenu = new MainMenu(topLevelMenuItemArray);

As you’ll recall, the Menu class serves as the base class for all other menu classes. One of the features implemented by the Menu class is its ability to store child menu items in an instance of MenuItemCollection. However, you’ll seldom need to create a reference to this class directly because it’s typically accessed through the MenuItems property, as shown here:

Menu.MenuItemCollection items = mainMenu.MenuItems;

A more common use of the MenuItemCollection class is simply to use the MenuItems property as an array of MenuItem objects, as shown here:

foreach(MenuItem item in mainMenu.MenuItems)
{
    item.Enabled = false;
} 

The Add method is used to add a menu item to the collection. There are five versions of the Add method. The simplest version just adds a menu item to the end of the current menu and returns the index position of the new item, as shown here:

MenuItem helpMenuItem = new MenuItem("&Help");
int position = mainMenu.MenuItems.Add(helpMenuItem); 

Another version of the Add method creates a new menu item at the end of the current menu, using the string passed as a parameter for the menu item’s Text property, and returns a reference to the new menu item:

MenuItem helpMenuItem = mainMenu.MenuItems.Add("&Help");

You can also use the Add method to create a new menu item that’s associated with a Click event handler, as shown here:

EventHandler indexHandler = new EventHandler(helpIndex_Click);
MenuItem indexItem = helpMenuItem.MenuItems.Add("&Index", indexHandler);

The fourth version of the Add method, shown in the following code, enables you to specify the index that the menu item will occupy. Any menu items currently in the menu will be shifted down if needed.

int position = helpMenuItem.MenuItems.Add(0, indexItem);

The fifth and final version of the Add method enables you to add a menu item that contains a submenu. For this version of the Add method, you pass the Text property for the new menu item and an array of menu items that will form the submenu.

EventHandler helpIndexHandler = new EventHandler(index_Click);
EventHandler helpContentsHandler = new EventHandler(contents_Click);
EventHandler helpSearchHandler = new EventHandler(search_Click);
MenuItem helpIndex = new MenuItem("&Index", helpIndexHandler);
MenuItem helpCont = new MenuItem("&Contents", helpContentsHandler);
MenuItem helpSearch = new MenuItem("&Search", helpSearchHandler);
MenuItem [] helpMenuArray = new MenuItem [] { helpIndex,
                                              helpCont,
                                              helpSearch };
MenuItem helpMenuItem = mainMenu.MenuItems.Add("&Help", helpMenuArray); 

The AddRange method is used to add an array of menu items to the collection. This method enables you to potentially add a large number of child menu items with one method call. The AddRange method is similar to the last version of the Add method discussed in the preceding paragraph, except that AddRange appends an array of menu items to the current menu, rather than creating a new submenu, as shown here:

MenuItem helpMenuItem = mainMenu.MenuItems.Add("&Help");
helpMenuItem.MenuItems.AddRange(helpMenuArray); 

The Count method is used to retrieve the number of menu items in the collection, as shown here:

int topLevelItemCount = Menu.MenuItems.Count;
int helpItemCount = helpMenuItem.MenuItems.Count;

To remove an item from the collection, pass a reference to the item to be deleted to the Remove method, as shown here:

editMenuItem.MenuItems.Remove(editWrap);

To remove an item at a specific index, you can use the RemoveAt method, as shown here:

editMenuItem.MenuItems.RemoveAt(1);

To remove all menu items from the collection, use the Clear method:

editMenuItem.MenuItems.Clear(); 

The following code creates a main menu that has three top-level menu items, each associated with a popup submenu:

// Top-level menu
MenuItem fileMenuItem = new MenuItem("&File");
MenuItem viewMenuItem = new MenuItem("&View");
MenuItem editMenuItem = new MenuItem("&Edit");
MainMenu mainMenu = new MainMenu( new MenuItem[] { fileMenuItem,
                                                   viewMenuItem,
                                                   editMenuItem });
// Event handlers for the File popup menu
EventHandler fileOpenHandler = new EventHandler(fileOpen_Click);
EventHandler fileSaveHandler = new EventHandler(fileSave_Click);
EventHandler fileSaveAsHandler = new EventHandler(fileSaveAs_Click);
EventHandler fileExitHandler = new EventHandler(fileExit_Click);
// File popup menu
MenuItem fileOpen = new MenuItem("&Open...", fileOpenHandler);
MenuItem fileSave = new MenuItem("&Save", fileSaveHandler);
MenuItem fileSaveAs = new MenuItem("Save &As...", fileSaveAsHandler);
MenuItem fileSeparator = new MenuItem("-");
MenuItem fileExit = new MenuItem("E&xit", fileExitHandler);
MenuItem [] fileMenuItemArray = new MenuItem [] { fileOpen,
                                                  fileSave,
                                                  fileSaveAs,
                                                  fileSeparator,
                                                  fileExit };
fileMenuItem.MenuItems.AddRange(fileMenuItemArray);
// Event handlers for the View popup menu
EventHandler viewHorizHandler = 
    new EventHandler(viewHorizontal_Click);
EventHandler viewVertHandler = new EventHandler(viewVertical_Click);
// View popup menu
MenuItem viewScroll = new MenuItem("&Scroll bars");
MenuItem viewHorizontal = 
    new MenuItem("&Horizontal", viewHorizHandler);
MenuItem viewVertical = new MenuItem("&Vertical", viewVertHandler);
MenuItem [] scrollMenuItemArray = new MenuItem [] { viewHorizontal,
                                                    viewVertical };
viewScroll.MenuItems.AddRange(scrollMenuItemArray);
viewMenuItem.MenuItems.Add(viewScroll);
// Event handlers for the Edit popup menu
EventHandler editClearHandler = new EventHandler(editClear_Click);
EventHandler editWrapHandler = new EventHandler(editWrap_Click);
// Edit popup menu
MenuItem editClear = new MenuItem("&Clear", editClearHandler);
MenuItem editWrap = new MenuItem("&Word Wrap", editWrapHandler);
MenuItem [] editMenuItemArray = new MenuItem [] { editClear,
                                                  editWrap };
editMenuItem.MenuItems.AddRange(editMenuItemArray);
// Assign new MainMenu object to the form's Menu property.
Menu = mainMenu;

This code begins by creating three menu items and adding them to a MainMenu object as its top-level menu items. Next event handler delegates are created for menu items in the File popup menu and are used in the construction of the individual menu items. After the File menu items have been created, they’re packed into a MenuItem array and added to the menu item collection of fileMenuItem. Because fileMenuItem is a top-level menu item, its child menu items will form the popup menu displayed when the menu item is clicked. This process is repeated for the View and Edit popup menus. After all menu items have been created and relationships have been established, the mainMenu object is assigned to the form’s Menu property.

note

The code generated by the Menu Designer is located in the same code region as the code generated by the Forms Designer. If you examine the code that the Menu Designer generates, you’ll see that it’s written in a slightly different style than the code presented here. In general, the Menu Designer creates MenuItem and MainMenu instances using the default constructor and then sets each property. This approach works well for the Menu Designer because the code is easy for the tool to update, but the code it creates is more verbose.

Handling Menu Events

There are two ways to handle events for menu items: programmatically and using the Forms Designer. As shown in the previous section, event handler delegates for the Click event can be attached to menu items programmatically during construction. In this section, you’ll learn how to write code to handle additional events from menu items. You’ll also learn how to use the Forms Designer to write the code required to handle menu-related events.

Three events are raised by MenuItem objects in response to user actions, as follows:

  • Popup  The menu item is about to be displayed. This event is useful when you need to update the status of a menu item dynamically, as will be done in the next section.

  • Select  The menu item has been highlighted as a selection but has not yet been clicked. This event is useful when help text is displayed for a potential menu choice, such as text displayed in a status bar. Using the Select event to update status bars is discussed later in this chapter, in the section “Using Status Bar Panels.”

  • Click  The menu item has been chosen by the user. Of all the menu-related events, this is the one that you’ll probably use the most, as it indicates that the user has clicked an item on the menu and expects the application to take some sort of action.

These menu item events tell you when individual menu items are selected or clicked; however, there’s no menu item event to tell you that a menu has been dismissed. Instead, the Form class raises two events that tell you when the menu is initially displayed and dismissed:

  • MenuStart  Raised when a menu initially receives the input focus

  • MenuComplete  Raised when the menu loses the input focus

The MenuStart event is typically used to manage the form’s user interface. When a menu item is selected, you might want to disable specific controls. Alternatively, you might want to enable controls that are used to provide feedback about the menu selection. For example, the MenuStart event can be used to display a help balloon control that describes the currently selected menu item.

The MenuComplete event is used to reverse the action taken during the MenuStart event. MenuComplete is raised when the user has finished using the menu, either because a menu item has been clicked or because the user has abandoned the menu and selected a different object.

Updating Menu Items

Menu items can be updated dynamically to reflect the current state of the application. You can enable and disable items, add check marks, and even add and remove menu items at run time. Updating menu items dynamically is an effective way to provide feedback to the user. For example, instead of displaying an error message if the user selects a menu item that’s not allowed in the current context, a more user-friendly approach is to simply disable the menu item.

Adding Check Marks to Menu Items

When a menu item is used to enable or disable a property or a feature of your application, a common user interface pattern is to supply a check mark to indicate that the item has been enabled. For example, applications that display a status bar typically provide a menu item that controls the status bar’s visibility. When the status bar is visible, the menu item is checked; when the status bar is hidden, the menu item is unchecked.

As mentioned, the MenuItem class exposes the Checked property, which is used to add a check mark to the left of the menu item text, as shown here:

statusBarMenuItem.Checked = statusBar.Visible; 

A common pattern is to set the state of menu items when the parent’s menu item raises the Popup event, as shown here:

private void editMenuItem_Popup(object sender, System.EventArgs e)
{
    editWordWrap.Checked = textBox.WordWrap;
} 

In this code, the check mark on the editWordWrap menu item is set or cleared depending on the status of the WordWrap property of the textBox object.

Disabling Menu Items

Menu items that aren’t available due to the current state of your application should be disabled to indicate that they can’t be selected. You can use the Enabled property provided by the MenuItem class to enable and disable menu items, as shown here:

private void fileMenuItem_Popup(object sender, System.EventArgs e)
{
    fileSave.Enabled = textBox.Modified;
}

In this code, the Save menu item is disabled if no changes have been made to the textBox object.

Using Multiple Menus

You’re not limited to using a single main menu for your application. When applications support multiple tasks or multiple document types, it’s common practice to provide multiple menus, with the proper menu displayed according to the context. Because a menu is associated with your main form through the form’s Menu property, switching to a new menu can be done simply by assigning a new menu to the Menu property, as shown here:

private void SwitchToBasicMenu()
{
    Menu = basicMainMenu;
}
private void SwitchToTextDocumentMenu()
{
    Menu = textDocumentMainMenu;
}

To add an additional menu for use by your application, drag a menu control from the Toolbox to your form. An additional menu icon will be added under your form in the Forms Designer. To work with a specific menu using the Menu Designer, click the appropriate icon in the Forms Designer.

Creating a Simple Editor

As an example of how menus are used in an application, the companion CD includes SimpleEdit, a small Notepad-like editor. Although this example is fairly small, it illustrates the various ways menus are used in a dynamic application.

The Basic SimpleEdit Application

The SimpleEdit project is a Windows Forms application with two controls on its main form, as follows:

  • A main menu control named mainMenu that provides access to application commands

  • A text box control named textBox that fills the form’s client area and serves as the container for edited text

The properties of the top-level menu items in the SimpleEdit project are listed in Table 13-10.

Table 13-10.  Top-Level Menu Item Properties in SimpleEdit 

Text

Name

&File

fileMenuItem

&View

viewMenuItem

&Edit

editMenuItem

Each of the three top-level menu items has an associated popup menu that’s displayed when the top-level menu item is selected. Table 13-11 lists the properties for the menu items on the File, View, and Edit popup menus.

Table 13-11.  Popup Menu Properties in SimpleEdit 

Parent Menu

Text

Name

File

&Open…

fileOpenMenuItem

&Save

fileSaveMenuItem

Save &As…

fileSaveAsMenuItem

Separator

N/A

E&xit

fileExitMenuItem

View

&Scroll Bars

viewScrollBarMenuItem

Scroll Bars

&Horizontal

hScrollMenuItem

&Vertical

vScrollMenuItem

Edit

&Clear

editClearMenuItem

&Word Wrap

editWrapMenuItem

The View menu has one child menu item, Scroll Bars; this menu item is in turn the parent of two submenus, Horizontal and Vertical, as shown in Figure 13-4.

Figure 13-4.
The Scroll Bars menu item, which has two child items.

The textBox control used by the SimpleEdit project is just a basic text box control from the Toolbox, with a few properties set to nondefault values, as listed in Table 13-12.

Table 13-12.  Text Box Control Properties in SimpleEdit 

Property

Value

Multiline

true

Name

textBox

Dock

DockStyle.Fill

The Dock property is used to attach a control to the edge of its container. By default, this property is set to DockStyle.None. Setting the property to DockStyle.Fill causes the control to expand to fill the entire container. Docking is simply a characteristic that specifies which edge of the current form the control will attach to. Values for the Dock property must be set to one of the DockStyle enumeration values, listed in Table 13-13. Many controls expose the Dock property; this feature will be discussed in more detail in Chapter 15. For now, it’s enough that you simply understand that the text box control uses the Dock property to completely fill the client area of the form.

Table 13-13.  DockStyle Enumeration Values 

Value

Description

Bottom

Docks to the bottom edge of the form

Fill

Docks to all edges, filling the client area of the form

Left

Docks to the left edge of the form

None

Not docked to any edge

Right

Docks to the right edge of the form

Top

Docks to the top of the form

Dynamically Updating SimpleEdit Menu Items

The File, View, and Edit popup menus each contain menu items that are updated dynamically as the menus are displayed. The Popup event for each top-level menu item is managed by the event handlers listed in Table 13-14.

Table 13-14.  Popup Event Handlers for SimpleEdit 

Menu Item

Event Handler

fileMenuItem

fileMenuItem_Popup

editMenuItem

editMenuItem_Popup

viewScrollBarMenuItem

viewScrollBarMenuItem_Popup

The event handler methods from Table 13-14 are shown in the following code:

private void fileMenuItem_Popup(object sender, System.EventArgs e)
{
    fileSaveMenuItem.Enabled = textBox.Modified;
}

private void editMenuItem_Popup(object sender, System.EventArgs e)
{
    editWordWrapMenuItem.Checked = textBox.WordWrap;
}

private void 
viewScrollBarMenuItem_Popup(object sender, System.EventArgs e)
{
    // Horizontal scrolling is n/a if the text box control
    // has word-wrapping enabled.
    hScrollMenuItem.Enabled = !textBox.WordWrap;
    switch(textBox.ScrollBars)
    {
        case ScrollBars.Both:
            hScrollMenuItem.Checked = true;
            vScrollMenuItem.Checked = true;
            break;
        case ScrollBars.Vertical:
            hScrollMenuItem.Checked = false;
            vScrollMenuItem.Checked = true;
            break;
        case ScrollBars.Horizontal:
            hScrollMenuItem.Checked = true;
            vScrollMenuItem.Checked = false;
            break;
        default:
            hScrollMenuItem.Checked = false;
            vScrollMenuItem.Checked = false;
            break;
    }
}

The handler for the File menu Popup event, fileMenuItem_Popup, enables or disables the Save menu item, depending on the state of the text box control. If the text box control’s Modified property returns true, changes have been made to the contents of the text box, and the Save menu item is enabled. If the Modified property returns false, the contents of the text box control haven’t been modified, and the Save item is disabled.

The editMenuItem_Popup method, the handler for the Edit menu’s Popup event, controls whether a check mark is placed next to the Word Wrap menu item. If the text box control has its WordWrap property enabled, the check mark is set. If the property is disabled, the check mark is cleared.

The final handler for a Popup event, viewScrollBarMenuItem_Popup, is more complex than the previous two handlers because it updates two menu items based on the value of the text box control’s ScrollBars property. The value of the ScrollBars property is tested to determine its value, and then check marks are either set or cleared for the vScrollMenuItem and hScrollMenuItem objects. In addition, the text box control doesn’t support horizontal scrolling if the WordWrap property is set to true, so the hScrollMenuItem object is enabled or disabled based on this property.

Handling Menu Commands

As you’ll recall, the Click event is raised when the user chooses a menu item. The SimpleEdit application exposes eight commands through its main menu, as listed in Table 13-15.

Most of the commands available to users of the SimpleEdit application are mapped directly to properties exposed by the text box control. The only exceptions are the menu items in the File popup menu, which are discussed in the next section. The following code includes the Click event handlers for menu items on the Edit and View popup menus:

private void 
editClearMenuItem_Click(object sender, System.EventArgs e)
{
    _fileName = "";
    _pathName = "";
    Text = _appName + " - " + "[Empty]";
    textBox.Clear();
}
private void 
editWordWrapMenuItem_Click(object sender, System.EventArgs e)
{
    textBox.WordWrap = !textBox.WordWrap;
}
private void 
hScrollMenuItem_Click(object sender, System.EventArgs e)
{
    if(hScrollMenuItem.Checked)
    {
        // Clear horizontal scroll bars.
        if(textBox.ScrollBars == ScrollBars.Both)
            textBox.ScrollBars = ScrollBars.Vertical;
        else
            textBox.ScrollBars = ScrollBars.None;
    }
    else
    {
        // Set horizontal scroll bars.
        if(textBox.ScrollBars == ScrollBars.Vertical)
            textBox.ScrollBars = ScrollBars.Both;
        else
            textBox.ScrollBars = ScrollBars.Horizontal;
    }
}
private void vScrollMenuItem_Click(object sender, System.EventArgs e)
{
    if(vScrollMenuItem.Checked)
    {
        // Clear vertical scroll bars.
        if(textBox.ScrollBars == ScrollBars.Both)
            textBox.ScrollBars = ScrollBars.Horizontal;
        else
            textBox.ScrollBars = ScrollBars.None;
    }
    else
    {
        // Set vertical scroll bars.
        if(textBox.ScrollBars == ScrollBars.Horizontal)
            textBox.ScrollBars = ScrollBars.Both;
        else
            textBox.ScrollBars = ScrollBars.Vertical;
    }
}

In the editClearMenuItem_Click method, the text box control’s Clear method is called to remove the contents of the control. In addition, member variables that track the current file name are reset, and the application’s caption is reset to indicate that no file is currently open. The editWord­Wrap­Menu­Item_Click method toggles word wrapping by simply inverting the property’s current value.

The hScrollMenuItem_Click and vScrollMenuItem_Click methods are used to manage the scroll bars attached to the text box control. The process of enabling and disabling scroll bars requires some additional code because the ScrollBars property is used to control the vertical and horizontal scroll bars.

Opening and Saving Text Files

The File popup menu is used primarily for opening and saving files, as well as for closing the application, which is traditionally included as an item on this menu. The following code includes the Click event handlers for the File menu:

private void fileOpenMenuItem_Click(object sender, 
                                    System.EventArgs e)
{
    OpenFileDialog dlg = new OpenFileDialog();
    dlg.Filter = _fileFilter;
    dlg.InitialDirectory = Application.CommonAppDataPath;
    dlg.CheckFileExists = false;
    if(dlg.ShowDialog() == DialogResult.OK)
    {
        string path = dlg.FileName;
        if(File.Exists(path) != true)
        {
            StreamWriter writer = File.CreateText(path);
            writer.Close();
        }
        _fileName = Path.GetFileName(path);
        _pathName = path;
        Text = _appName + " - " +  _fileName;
        StreamReader reader = new StreamReader(path);
        textBox.Text = reader.ReadToEnd();
        reader.Close();
    }
}
private void SaveTextToPath(string path)
{
    StreamWriter writer = new StreamWriter(path);
    writer.Write(textBox.Text);
    writer.Close();
    textBox.Modified = false;
}
private void SaveTextToNewPath()
{
    SaveFileDialog dlg = new SaveFileDialog();
    dlg.Filter = _fileFilter;
    dlg.InitialDirectory = Application.CommonAppDataPath;
    if(dlg.ShowDialog() == DialogResult.OK)
    {
        string path = dlg.FileName;
        if(File.Exists(path) == true)
        {
            DialogResult result = MessageBox.Show(_overwriteWarning,
                                         _appName,
                                         MessageBoxButtons.OKCancel,
                                         MessageBoxIcon.Question);
            if(result == DialogResult.Cancel)
                return;
        }
        _fileName = Path.GetFileName(path);
        _pathName = path;
        Text = _appName + " - " +  _fileName;
        SaveTextToPath(path);
    }
}
private void fileSaveMenuItem_Click(object sender, 
                                    System.EventArgs e)
{
    if(_fileName != null && _fileName != "")
        SaveTextToPath(_pathName);
    else
        SaveTextToNewPath();
}
private void fileSaveAsMenuItem_Click(object sender, 
                                      System.EventArgs e)
{
    SaveTextToNewPath();
}
private void fileExitMenuItem_Click(object sender, 
                                    System.EventArgs e)
{
    Close();
}

The SimpleEdit project uses common dialog boxes to enable users to select files used by the application. As discussed in Chapter 11, the Application.Common­AppData property is used as a starting location for all file operations; when a common file dialog box is displayed to the user, the initial directory is set to that path.

The fileOpenMenuItem_Click method creates an instance of OpenFile­Dialog, which enables the user to use a common file dialog box to select a file that’s to be loaded into the text box control. If the user selects a file and clicks OK, the file path is tested to determine whether the file exists by passing the path to the File.Exists method. If the file exists, an instance of StreamReader is created from the selected file’s path, and the ReadToEnd method is called to retrieve the file’s contents. If the file doesn’t exist, it’s assumed that the user wants to work with a new file, and the file is created.

The SaveTextToPath and SaveTextToNewPath methods are used by Click event handlers to save the contents of the text box. The SaveTextToPath method is used by SimpleEdit to write the contents of the text box to a disk file. The method accepts a destination file path as a parameter. It then creates an instance of StreamWriter using the path, which is used to write the contents of the text box to the destination file. After the file has been closed, the text box control’s Modified flag is reset to indicate that all changes have been saved. The SaveTextToNewPath method creates an instance of SaveFileDialog, which enables the use of a common file dialog box to select a destination for the text box control’s contents. After a destination file has been selected, the SaveTextToPath method is called to actually write the contents to the file.

The fileSaveMenuItem_Click event handler will call the SaveTextToPath method if a destination file has already been determined. This will be the case when a file has previously been opened for editing. If no file has been previously selected, the SaveTextToNewPath method will be called, and the user must select a destination path before the contents are saved. The file­Save­As­Menu­Item_Click method always calls the SaveTextToNewPath method.



Part III: Programming Windows Forms