Как сделать меню в windows forms

Последнее обновление: 31.10.2015

Для создания меню в Windows Forms применяется элемент MenuStrip. Данный класс унаследован от ToolStrip и
поэтому наследует его функциональность.

Наиболее важные свойства компонента MenuStrip:

  • Dock: прикрепляет меню к одной из сторон формы

  • LayoutStyle: задает ориентацию панели меню на форме. Может также, как и с ToolStrip, принимать следующие значения

    • HorizontalStackWithOverflow: расположение по горизонтали с переполнением — если длина меню превышает длину контейнера,
      то новые элементы, выходящие за границы контейнера, не отображаются, то есть панель переполняется элементами

    • StackWithOverflow: элементы располагаются автоматически с переполнением

    • VerticalStackWithOverflow: элементы располагаются вертикально с переполнением

    • Flow: элементы размещаются автоматически, но без переполнения — если длина панели меню меньше длины контейнера, то
      выходящие за границы элементы переносятся

    • Table: элементы позиционируются в виде таблицы

  • ShowItemToolTips: указывает, будут ли отображаться всплывающие подсказки для отдельных элементов меню

  • Stretch: позволяет растянуть панель по всей длине контейнера

  • TextDirection: задает направление текста в пунктах меню

MenuStrip выступает своего рода контейнером для отдельных пунктов меню, которые представлены объектом ToolStripMenuItem.

Добавить новые элементы в меню можно в режиме дизайнера:

Для добавления доступно три вида элементов: MenuItem (объект ToolStripMenuItem), ComboBox и TextBox. Таким образом,
в меню мы можем использовать выпадающие списки и текстовые поля, однако, как правило, эти элементы применяются в основном на панели инструментов.
Меню же обычно содержит набор объектов ToolStripMenuItem.

Также мы можем добавить пункты меню в коде C#:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        ToolStripMenuItem fileItem = new ToolStripMenuItem("Файл");
            
        fileItem.DropDownItems.Add("Создать");
        fileItem.DropDownItems.Add(new ToolStripMenuItem("Сохранить"));

        menuStrip1.Items.Add(fileItem);

        ToolStripMenuItem aboutItem = new ToolStripMenuItem("О программе");
        aboutItem.Click += aboutItem_Click;
        menuStrip1.Items.Add(aboutItem);
    }

    void aboutItem_Click(object sender, EventArgs e)
    {
        MessageBox.Show("О программе");
    }
}

ToolStripMenuItem в конструкторе принимает текстовую метку, которая будет использоваться в качестве текста меню. Каждый подобный объект
имеет коллекцию DropDownItems, которая хранит дочерние объекты ToolStripMenuItem. То есть один элемент ToolStripMenuItem
может содержать набор других объектов ToolStripMenuItem. И таким образом, образуется иерархическое меню или структура в виде дерева.

Если передать при добавление строку текста, то для нее неявным образом будет создан объект ToolStripMenuItem: fileItem.DropDownItems.Add("Создать")

Назначив обработчики для события Click, мы можем обработать нажатия на пункты меню: aboutItem.Click += aboutItem_Click

Отметки пунктов меню

Свойство CheckOnClick при значении true позволяет на клику отметить пункт меню. А с помощью свойства Checked
можно установить, будет ли пункт меню отмечен при запуске программы.

Еще одно свойство CheckState возвращает состояние пункта меню — отмечен он или нет. Оно может принимать три значения:
Checked (отмечен), Unchecked (неотмечен) и Indeterminate (в неопределенном состоянии)

Например, создадим ряд отмеченных пунктов меню и обработаем событие установки / снятия отметки:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        ToolStripMenuItem fileItem = new ToolStripMenuItem("Файл");

        ToolStripMenuItem newItem = new ToolStripMenuItem("Создать") { Checked = true, CheckOnClick = true };
        fileItem.DropDownItems.Add(newItem);


        ToolStripMenuItem saveItem = new ToolStripMenuItem("Сохранить") { Checked = true, CheckOnClick = true };
        saveItem.CheckedChanged += menuItem_CheckedChanged;

        fileItem.DropDownItems.Add(saveItem);

        menuStrip1.Items.Add(fileItem);
    }

    void menuItem_CheckedChanged(object sender, EventArgs e)
    {
        ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
        if (menuItem.CheckState == CheckState.Checked)
            MessageBox.Show("Отмечен");
        else if (menuItem.CheckState == CheckState.Unchecked)
            MessageBox.Show("Отметка снята");
    }
}

Клавиши быстрого доступа

Если нам надо быстро обратиться к какому-то пункту меню, то мы можем использовать клавиши быстрого доступа. Для задания клавиш быстрого доступа
используется свойство ShortcutKeys:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        ToolStripMenuItem fileItem = new ToolStripMenuItem("Файл");
         
		ToolStripMenuItem saveItem = new ToolStripMenuItem("Сохранить") { Checked = true, CheckOnClick = true };
        saveItem.Click+=saveItem_Click;
        saveItem.ShortcutKeys = Keys.Control | Keys.P;
        
		fileItem.DropDownItems.Add(saveItem);
		menuStrip1.Items.Add(fileItem);
    }

    void saveItem_Click(object sender, EventArgs e)
    {
        MessageBox.Show("Сохранение");
    }
}

Клавиши задаются с помощью перечисления Keys. В данном случае по нажатию на комбинацию клавиш Ctrl + P, будет срабатывать нажатие на пункт
меню «Сохранить».

С помощью изображений мы можем разнообразить внешний вид пунктов меню. Для этого мы можем использовать следующие свойства:

  • DisplayStyle: определяет, будет ли отображаться на элементе текст, или изображение, или и то и другое.

  • Image: указывает на само изображение

  • ImageAlign: устанавливает выравнивание изображения относительно элемента

  • ImageScaling: указывает, будет ли изображение растягиваться, чтобы заполнить все пространство элемента

  • ImageTransparentColor: указывает, будет ли цвет изображения прозрачным

Если изображение для пункта меню устанавливает в режиме дизайнера, то нам надо выбрать в окне свойство пункт Image, после чего откроется
окно для импорта ресурса изображения в проект

Чтобы указать, как разместить изображение, у свойства DisplayStyle надо установить значение Image. Если мы хотим, чтобы
кнопка отображала только текст, то надо указать значение Text, либо можно комбинировать два значения с помощью другого
значения ImageAndText. По умолчанию изображение размещается слева от текста:

Также можно установить изображение динамически в коде:

fileToolStripMenuItem.Image = Image.FromFile(@"D:\Icons\0023\block32.png");

The MenuStrip class is the foundation of menus functionality in Windows Forms. If you have worked with menus in .NET 1.0 and 2.0, you must be familiar with the MainMenu control. In .NET 3.5 and 4.0, the MainMenu control is replaced with the MenuStrip control.

Creating a MenuStrip

We can create a MenuStrip control using a Forms designer at design-time or using the MenuStrip class in code at run-time or dynamically.

To create a MenuStrip control at design-time, you simply drag and drop a MenuStrip control from Toolbox to a Form in Visual Studio. After you drag and drop a MenuStrip on a Form, the MenuStrip1 is added to the Form and looks like Figure 1. Once a MenuStrip is on the Form, you can add menu items and set its properties and events.

MenuStrip


Figure 1

Creating a MenuStrip control at run-time is merely a work of creating an instance of MenuStrip class, setting its properties and adding MenuStrip class to the Form controls.

The first step to create a dynamic MenuStrip is to create an instance of MenuStrip class. The following code snippet creates a MenuStrip control object.

C# Code

  1. MenuStrip MainMenu = new MenuStrip();  

VB.NET Code

  1. Dim MainMenu AsNewMenuStrip()  

In the next step, you may set properties of a MenuStrip control. The following code snippet sets background color, foreground color, Text, Name, and Font properties of a MenuStrip.

C# Code

  1. MainMenu.BackColor = Color.OrangeRed;  
  2. MainMenu.ForeColor = Color.Black;  
  3. MainMenu.Text = «File Menu»;  
  4. MainMenu.Font = newFont(«Georgia», 16);  

VB.NET Code

  1. MainMenu.BackColor = Color.OrangeRed  
  2. MainMenu.ForeColor = Color.Black  
  3. MainMenu.Text = «File Menu»  
  4. MainMenu.Font = NewFont(«Georgia», 16)  

Once the MenuStrip control is ready with its properties, the next step is to add the MenuStrip to a Form. To do so, first we set MainMenuStrip property and then use Form.Controls.Add method that adds MenuStrip control to the Form controls and displays on the Form based on the location and size of the control. The following code snippet adds a MenuStrip control to the current Form.

C# Code

  1. this.MainMenuStrip = MainMenu;  
  2. Controls.Add(MainMenu);  

VB.NET Code

  1. Me.MainMenuStrip = MainMenu  
  2. Controls.Add(MainMenu)  

Setting MenuStrip Properties

After you place a MenuStrip control on a Form, the next step is to set properties.

The easiest way to set properties is from the Properties Window. You can open Properties window by pressing F4 or right clicking on a control and selecting Properties menu item. The Properties window looks like Figure 2.


Figure 2

Name

Name property represents a unique name of a MenuStrip control. It is used to access the control in the code. The following code snippet sets and gets the name and text of a MenuStrip control.

C# Code

  1. MainMenu.Name = «MainMenu»;  

VB.NET Code

  1. MainMenu.Name = «MailMenu»  

Positioning a MenuStrip

The Dock property is used to set the position of a MenuStrip. It is of type DockStyle that can have values Top, Bottom, Left, Right, and Fill. The following code snippet sets Location, Width, and Height properties of a MenuStrip control.

C# Code

  1. MainMenu.Dock = DockStyle.Left;  

VB.NET Code

  1. MainMenu.Dock = DockStyle.Left  

Font

Font property represents the font of text of a MenuStrip control. If you click on the Font property in Properties window, you will see Font name, size and other font options. The following code snippet sets Font property at run-time.

C# Code

  1. MainMenu.Font = newFont(«Georgia», 16);  

VB.NET Code

  1. MainMenu.Font = newFont(«Georgia», 16)  

Background and Foreground

BackColor and ForeColor properties are used to set background and foreground color of a MenuStrip respectively. If you click on these properties in Properties window, the Color Dialog pops up.

Alternatively, you can set background and foreground colors at run-time. The following code snippet sets BackColor and ForeColor properties.

C# Code

  1. MainMenu.BackColor = Color.OrangeRed;  
  2. MainMenu.ForeColor = Color.Black;  

VB.NET Code

  1. MainMenu.BackColor = Color.OrangeRed  
  2. MainMenu.ForeColor = Color.Black  

The new MenuStrip with background and foreground looks like Figure 3.

MenuStrip with background and foreground


Figure 3

MenuStrip Items

A Menu control is nothing without menu items. The Items property is used to add and work with items in a MenuStrip. We can add items to a MenuStrip at design-time from Properties Window by clicking on Items Collection as you can see in Figure 4.


Figure 4

When you click on the Collections, the String Collection Editor window will pop up where you can type strings. Each line added to this collection will become a MenuStrip item. I add four items as you can see from Figure 5.

MenuStrip Items


Figure 5

A ToolStripMenuItem represents a menu items. The following code snippet creates a menu item and sets its properties.

C# Code

  1.   
  2. ToolStripMenuItem FileMenu = newToolStripMenuItem(«File»);  
  3. FileMenu.BackColor = Color.OrangeRed;  
  4. FileMenu.ForeColor = Color.Black;  
  5. FileMenu.Text = «File Menu»;  
  6. FileMenu.Font = newFont(«Georgia», 16);  
  7. FileMenu.TextAlign = ContentAlignment.BottomRight;  
  8. FileMenu.ToolTipText = «Click Me»;  

VB.NET Code

  1. Dim FileMenu AsNewToolStripMenuItem(«File»)  
  2. FileMenu.BackColor = Color.OrangeRed  
  3. FileMenu.ForeColor = Color.Black  
  4. FileMenu.Text = «File Menu»  
  5. FileMenu.Font = NewFont(«Georgia», 16)  
  6. FileMenu.TextAlign = ContentAlignment.BottomRight  
  7. FileMenu.TextDirection = ToolStripTextDirection.Vertical90  
  8. FileMenu.ToolTipText = «Click Me»  

Once a menu item is created, we can add it to the main menu by using MenuStrip.Items.Add method. The following code snippet adds FileMenu item to the MainMenu.

C# Code

  1. MainMenu.Items.Add(FileMenu);  

VB.NET Code

  1. MainMenu.Items.Add(FileMenu)  

Adding Menu Item Click Event Handler

The main purpose of a menu item is to add a click event handler and write code that we need to execute on the menu item click event handler. For example, on File >> New menu item click event handler, we may want to create a new file.

To add an event handler, you go to Events window and double click on Click and other as you can see in Figure 6.


Figure 6

We can also define and implement an event handler dynamically. The following code snippet defines and implements these events and their respective event handlers.

C# Code

  1. FileMenu.Click += new System.EventHandler(this.FileMenuItemClick);  
  2. privatevoid FileMenuItemClick(object sender, EventArgs e) {  
  3.     MessageBox.Show(«File menu item clicked»);  
  4. }  

VB.NET Code

  1. Dim FileMenuItem AsNewToolStripMenuItem(«File», Nothing, _  
  2. NewEventHandler(AddressOf FileMenuItemClick))  
  3. PrivateSub FileMenuItemClick (ByVal sender AsObject, ByVal e AsEventArgs)  
  4. MessageBox.Show(«File menu item clicked!»)  
  5. EndSub  

Summary

In this article, we discussed how to create menus using the MenuStrip control. First we discussed how to create menus at design-time and run-time. After that we saw how to set menu properties and click event handlers.

Menu bars can be seen in almost every application out there. Menus are used to contain different commands that the user can use. You can add menu bars to a form by using the MenuStrip control (System.Windows.Forms.MenuStrip). The MenuStrip is the container of the different menus. To add a menu bar to your form, drag a MenuStrip control from the Toolbox. It is located in the Menus & Toolbars Section.

You can also see the MenuStrip control in a section located at the bottom of the Designer. Located here are some non-visual components such as a Timer control and many more.

The MenuStrip control has the following useful properties.

Property Description
Dock Determines which location to dock the menu bar. The default is Top.
GripStyle Allows you to show the grip which is used to repositions items in the menu strip.
Items A collection of top-level menus.
Stretch Specifies whether the MenuStrip stretches from end to end in its container.

Figure 1 – MenuStrip Properties

Adding Standard Menus


Visual Studio offers you a way to automatically add standard items to the MenuStrip control. To do this, open the smart tag, which can be done by pressing the arrow button at the upper right side of the MenuStrip.

Then choose Insert Standard Items.

Visual Studio will fill the MenuStrip with standard menus such as Creating New files, Saving Files, Loading and many more.

Adding Your Own Menus

The standard menus might not suit what you specifically need. We can create our own custom menu bar using Visual Studio’s tools. You can either undo the placement of the standard menu by hitting Ctrl+Z or you can simply delete the MenuStrip and add a new one. To add a menu item, click the box labeled “Type Here” and type the name of the menu.

You will notice that as you type, another box will appear beside and below it so you can add more items. As an example, type File, Edit, and Help in the MenuStrip.

To add submenus, click a menu item it will open up a container for all its submenus. You will also see a box that will allow you to insert sub menus.

If you want to add a separator, type the  character.

You can even create submenus of submenus. You can nest and nest menus if you want to. A submenu which contains another submenu can be identified by the arrow at its right side.

Each text menu item you add is represented by ToolStripMenuItem. The separator is represented by ToolStripSeparator. The ToolStripSeparator is only used to separate related menus. Each ToolStripMenuItem is added to the MenuStrip‘s Item property and each submenu is added to the ToolStripMenuItem‘s DropDownItems property. Visual Studio automatically adds a name to each ToolStripMenuItem control which is based on the text you specified. Let’s discuss the properties of the ToolStripMenuItem.

Property Description
Checked Tells whether the item is checked.
CheckOnClick Tells whether an item will be checked or unchecked when it is clicked.
CheckState Tells whether the item is checked or unchecked.
DropDownItems A collection of submenus of the current item.
Enabled Enables of disables this item.
Image An optional image or icon that is shown at the left of the label.
ShortcutKeys The shortcut keys associated with the ToolStripMenuItem.
ShowShortcutKeys Tells whether the shortcut keys for an item is shown in the menu.
Text The text or label of the menu item.
ToolTipText The text inside the tooltip that appears when you hover your mouse on the menu item.

Figure 2 – ToolStripMenuItem Properties

Checking and Unchecking Menu Items


The CheckOnClick property allows a menu item to be checked or unchecked. For example, you can have a menu that shows a list of windows that will be shown. You can turn their CheckOnClick properties to true. When a menu item is checked, you will see a check in its left side.

You can use the Checked or CheckState property of the menu item to tell whether it is checked or unchecked.

Adding Shortcut Keys to a Menu Item


You can add shortcut keys to a menu item. The easiest one is by using the & (ampersand) character when typing the name of the menu item.

The letter following the & character will be considered as the shortcut key. For example, &Save will have S as the shortcut key and E&xitwill have the X as the shortcut key. At the designer, the shortcut key will be underlined. The underline will not be shown during runtime unless you hit the Alt key. Using this type of shortcut key requires the use of the Alt key. To activate a menu item, you hit the Alt + Shortcut Key. For example, if the shortcut Key of the File menu is F, then you use Alt + F combination to activate the menu item.

If you want more complex shortcut combinations, then we have to use the ShortcutKeys property. Select a menu item and then go to the Properties Window and find the ShortcutKeys property. Click the drop down arrow to show you a small window that allows you to choose the shortcut keys for the current menu item.

Once you specified a shortcut combination, it will now appear beside the label of a menu item provided that the ShowShortcutKeysproperty of the menu item is set to True.

Adding Icons to Menu Items


You can add images or icons at the left side of a menu item as seen in the standard menus.

We use the Image property to do just that. Click the drop down button of the Image property in the Property Window. Choose which resource to use and then browse for the appropriate image. If the image is too large, then it will automatically be scaled based on the value of the ImageScaling property.

Adding Functionality to the Menu Items


Menu items have the Click event which occurs when the menu item is clicked. We can handle this event to give functionality to menu items. To add a Click event to a menu item, in the Designer, double-click the menu item and Visual Studio will automatically create an event handler for you. For menu items that have CheckOnClick properties set to true, you can handle the CheckedChange or CheckStateChange events. As an example create another form and create a menu as seen below.

Double click the Exit menu item. Visual Studio will create an event handler for its Click event. Add the following code.

private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
    Application.Exit();
}

The static method Exit of the Application class simply closes the application.

Использование ToolStrip для организации меню на главной форме приложения

Для организации меню на главной форме приложения возможно использование ToolStrip.

Внимание! Версия Visual Studio должна быть 2005 или выше.

В основном проекте приложения в скобках программиста необходимо указать три метода:

AddToolStripMenu
ChangeToolStripMenuItem
CopyMenuToStrip

// *** Start programmer edit section *** (ThreatsModelDesktop CustomMembers)
        static void AddToolStripMenu(ToolStrip ts, int menuindex, int submenuindex, 
            string Text, Image image, EventHandler e, System.Windows.Forms.Keys shortkeys)
        {
            ((ToolStripMenuItem)ts.Items[menuindex]).DropDownItems.Insert(submenuindex, new ToolStripMenuItem(Text, null, e));
            ChangeToolStripMenuItem(ts, menuindex, submenuindex, image, shortkeys);
        }
        static void ChangeToolStripMenuItem(ToolStrip ts, int menuindex, int submenuindex, Image image, System.Windows.Forms.Keys shortkeys)
        {
            ToolStripMenuItem tsmi = ((ToolStripMenuItem)((ToolStripMenuItem)ts.Items[menuindex]).DropDownItems[submenuindex]);
            tsmi.ShortcutKeys = shortkeys;
            tsmi.ShowShortcutKeys = true;
            tsmi.ImageTransparentColor = Color.Magenta;
            tsmi.Image = image; 
        }
        static void CopyMenuToStrip(MenuItem mi, ToolStrip ts, ToolStripMenuItem tsmi)
        {
            if (!mi.Visible) return;
            ToolStripItem mtddb = null;
            if (mi.Text == "-")
            {
                mtddb = new ToolStripSeparator();
                tsmi.DropDownItems.Add(mtddb);  
            }
            else
            {
                mtddb = new ToolStripMenuItem(mi.Text, null, mtsb_Click);
                if (tsmi == null) ts.Items.Add(mtddb);  
                else tsmi.DropDownItems.Add(mtddb);  
            }
            mtddb.Tag = mi;
            for (int i = 0; i < mi.MenuItems.Count; i++)
            {
                CopyMenuToStrip(mi.MenuItems[i], ts, (ToolStripMenuItem)mtddb);
            }
        }

В методе main в скобках программиста изменяются настройки меню:

//Уберем Properties
desktop.menuItem1.MenuItems[0].Visible = false;
//Вставим обработчик на Help
desktop.menuItem4.Click += new EventHandler(menuItem4_Click);
//Скопируем существующее меню в ToolStrip
ToolStrip ts = new ToolStrip();
ts.Parent = desktop;
for (int i = 0; i < desktop.mainMenu1.MenuItems.Count; i++)
{
CopyMenuToStrip(desktop.mainMenu1.MenuItems[i],ts,null);
desktop.mainMenu1.MenuItems[i].Visible = false; //скрываем меню..
}
//Добавить новые элементы меню с картинками и горячими клавишами
AddToolStripMenu(ts, 0, 0, "Создать модель ИСПДн", (System.Drawing.Image)global::IIS.Product.Properties.Resources.new16,new EventHandler(WorkMenu_Click),Keys.Alt | Keys.N);
AddToolStripMenu(ts, 0, 1, "Открыть модель ИСПДн...",(System.Drawing.Image)global::IIS.Product.Properties.Resources.Open16, new EventHandler(WorkMenu_Click), Keys.Alt | Keys.O);
ts.Items.Insert(1,new ToolStripMenuItem("Справочники"));
AddToolStripMenu(ts, 1, 0, "Угрозы...", null, new EventHandler(WorkMenu_Click), Keys.Alt | Keys.U);
AddToolStripMenu(ts, 1, 1, "Меры противодействия...", null, new EventHandler(WorkMenu_Click), Keys.Alt | Keys.M);
AddToolStripMenu(ts, 2, 1, "Лицензионное соглашение...", (System.Drawing.Image)global::IIS.Product.Properties.Resources.Attrib16,  new EventHandler(menuItem4_Click), Keys.Alt | Keys.F10);
//Для меню "Выход", вставляем иконку и горячую клавишу
ChangeToolStripMenuItem(ts, 0, 3,(System.Drawing.Image)global::IIS.Product.Properties.Resources.Exit16, Keys.Alt | Keys.X);
//Для меню "Помощь..." , вставляем иконку и горячую клавишу
ChangeToolStripMenuItem(ts, 2, 0,(System.Drawing.Image)global::IIS.Product.Properties.Resources.Help16,  Keys.F1);

В результате компиляции обновляется интерфейс:

Интерфейс

Меню «Файл»:

Меню «Справочники»:

Меню «?»:

Меню Подробнее

В результате решено несколько проблем:

  1. Меню выглядит более консервативно.
  2. Усилен визуальный эффект от меню за счет иконок.
  3. Расширена возможность горячих клавиш.

Меню в главной форме приложения

В приложении в методе Main необходимо найти переменную desktop, у которой в свойствах находятся все элементы существующего меню.

Пример

  1. Убрать видимость элемента меню “Свойства”.
  2. Добавить элемент меню “Сменить пользователя” с обработчиком.
  3. Добавить обработчик на элемент меню “Помощь”.
[STAThread()]
static void Main()
{
//...
      БорьбаDesktop desktop = new БорьбаDesktop();
      //...
      desktop.menuItem1.MenuItems[0].Visible = false;                 //Свойства;
      desktop.menuItem1.MenuItems.Add(0,new MenuItem("Сменить пользователя...",new EventHandler(БорьбаDesktop_Click)));
      desktop.menuItem4.Click += new EventHandler(menuItem4_Click);   //Помощь;
      //...
}

Chapter 4. Menus and Toolbars

Menus are often the only practical way to present a rich array of

functionality without cluttering up the user interface. Whether

appearing at the top of the window, or as a context menu accessed

through the righthand mouse button, menus allow an application to

show concisely which operations are available. An

application’s usability can be further enhanced by

making the most important operations available through toolbar

buttons as well as menus.

The Windows Forms framework provides support for both menus and

toolbars. Despite the fact that these two technologies serve similar

roles—toolbar buttons often correspond directly to menu

items—they are presented through two largely unrelated parts of

the class library. However, as we will see later, it is possible to

unify the way you handle events from them in your application.

In this chapter, we will first examine the support for menus. Then we

will see how to create toolbars. Finally, we will see how events from

both can be dealt with by a single set of event handlers.

Menus

The

Windows Forms framework provides support for adding menus to your

applications. It uses a single programming model both for normal

window menus and for context menus. The model allows menus to be

modified dynamically, or even combined, providing flexibility at

runtime, and supports the ability to reuse and extend menu

definitions.

We will start by examining the object model used for constructing

menus. Then we will see how to attach them to a form. Next, we will

look at how to add context menus. Finally, we will see how to reuse

and extend your menu definitions by merging items from one menu into

another, both in the context of MDI applications, and also when

reusing forms through inheritance.

The Object Model

For your application to use menus, you must provide Windows Forms

with a description of their structure and contents. You do this by

building hierarchies of objects that represent menus and the items

they contain. Although you will typically get Visual Studio .NET to

do this for you, a sound understanding of the object model it uses is

important to use menus effectively in your applications.

This object model revolves around the

Menu class, which is arguably misnamed, because it

represents a more abstract concept than its title suggests. It can

correspond to any element of a menu structure, and it is the base

class of all the other types in the menu object model. So while a

Menu object might represent a menu, it could just

represent a single item of a menu. (Perhaps

MenuElement would have been a more descriptive

name.) Representing menus and menu items with the same base type

seems a little strange at first, but it makes sense when you consider

that menus can be hierarchical. A menu item might well be a nested

menu, in which case, it makes sense for that menu item to be

represented by an object whose class derives from

Menu.

The main job of the Menu class is to represent the

structure of a menu. You can find out whether a particular

Menu object is a leaf item or an item with

children by examining its

IsParent property. If

IsParent is true, its child

items will be in a collection on the object’s

MenuItems property.

You will never use the Menu class directly. Its

constructor is protected, which means that to obtain a reference to a

Menu, you must instead create one of its

derivatives: MainMenu,

ContextMenu, or MenuItem.

The

MainMenu class

represents a form’s main menu, and

ContextMenu

represents a pop-up context menu. Every menu structure has one or the

other of these at its root, and you’ll see more

about how to use them later on. But everything else in the menu is

represented by

MenuItem objects.

Every line that the user sees in a menu (and every top-level menu in

a form’s main menu) is represented by a

MenuItem. A leaf item (i.e., a menu item that does

not lead to a submenu) is indicated by the fact that it has no

children. If the item leads to a submenu, the same object represents

both the item and the submenu.

Figure 4-1 shows an example application with

a main menu. As you can see, a single MenuItem

object represents both the Edit caption and the menu associated with

it. Each entry in the menu (Undo, Redo, etc.) has its own

MenuItem object. The object that corresponds to

the Find and Replace item also represents the submenu (although the

entries in that submenu all have their own

MenuItem objects).

Figure 4-1. Menus and their objects

We’ll now look at how to go about building such

hierarchies of objects to add menus to an application.

Building menus

The easiest way to create a menu is to

use the Visual Studio .NET Forms Designer. It provides two

menu-related controls in the tool box: MainMenu

and ContextMenu. Each of these provides a visual

interface for editing the contents of a menu. Somewhat confusingly,

Visual Studio uses the same interface for both. This is a little

strange, because it means that the editor makes context menus look

like the form’s main menu. But this is just a

design-time anomaly—context menus are displayed correctly at

runtime.

As we have seen in previous chapters, anything done in the Forms

Designer simply ends up generating code. Menus are no exception, and

regardless of which kind of menu you create, the Forms Designer

generates the same kind of code. It will create a top-level menu

(either a MainMenu or a

ContextMenu), and then one

MenuItem

for each element of each menu. The C# code appears as follows:

this.mainMenu = new System.Windows.Forms.MainMenu();

this.menuFile = new System.Windows.Forms.MenuItem();

this.menuFileNew = new System.Windows.Forms.MenuItem();

this.menuFileOpen = new System.Windows.Forms.MenuItem();

this.menuFileClose = new System.Windows.Forms.MenuItem();

this.menuFileExit = new System.Windows.Forms.MenuItem();

The corresponding VB code is:

Me.mainMenu = New System.Windows.Forms.MainMenu()

Me.menuFile = New System.Windows.Forms.MenuItem()

Me.menuFileNew = New System.Windows.Forms.MenuItem()

Me.menuFileOpen = New System.Windows.Forms.MenuItem()

Me.menuFileClose = New System.Windows.Forms.MenuItem()

Me.menuFileExit = New System.Windows.Forms.MenuItem()

Tip

By default, the Designer will choose unhelpful names for the menu

items, such as menuItem1,

menuItem2, etc. If you want your code to be

readable, it is a good idea to change each menu

item’s Name property to something

more meaningful in the Designer, as has been done in this example.

(The Name property is in the Design category of

the Properties window.)

Of course, creating a few menu items is

not enough to describe the menu fully—with the code as it

stands, Windows Forms will have no idea that

menuFile is an item of

mainMenu, or that menuFileNew,

menuFileOpen, and menuFileClose

are members of menuFile. So the designer also

generates code to establish the menu hierarchy.[15] In C#, the code looks

like this:

// 

// mainMenu

// 

this.mainMenu.MenuItems.AddRange(

    new System.Windows.Forms.MenuItem[] {

        this.menuFile,

        this.menuEdit,

        this.menuView,

        this.menuHelp});

// 

// menuFile

// 

this.menuFile.Index = 0;

this.menuFile.MenuItems.AddRange(

    new System.Windows.Forms.MenuItem[] {

        this.menuFileNew,

        this.menuFileOpen,

        this.menuFileClose,

        this.menuFileExit});

In VB:

'mainMenu

'

Me.mainMenu.MenuItems.AddRange( _

    New System.Windows.Forms.MenuItem() _

        {Me.menuFile, _

         Me.menuEdit, _

         Me.menuView,

         Me.menuHelp})

'

'menuFile

'

Me.menuFile.Index = 0

Me.menuFile.MenuItems.AddRange( _

    New System.Windows.Forms.MenuItem() _

        {Me.menuFileNew, _

         Me.menuFileOpen, _

         Me.menuFileClose, _

         Me.menuFileExit})

Note that the designer uses the same code for adding items to the

main menu as for adding items to the File submenu. This illustrates

why all the various menu classes derive from the

Menu base class—Menu

supplies the functionality common to all menu elements, such as the

ability to contain menu items.

A menu’s items are stored in the

MenuItems property,

whose type is the special-purpose collection class

Menu.MenuItemCollection. The code uses this

collection’s AddRange method to

add a list of MenuItem objects. Of course, because

each MenuItem inherits from

Menu, it has a MenuItems

property too, and can have further subitems added—this is how

nested menu structures are built.

The order in which you add menu items to a parent menu has no bearing

on the order in which they appear on screen. Their order is

controlled by the

Index property. This

property is an int or Integer,

and it is used to number child items sequentially starting from 0.

(The Designer does this automatically, and adjusts the

Index properties when you reorder items visually.)

The framework will also need to know what text should be displayed

for each menu item, and whether it has any keyboard shortcut

associated with it. So for each item, the Designer generates code

like this in C#:

this.menuFileNew.Index = 0;

this.menuFileNew.Shortcut = System.Windows.Forms.Shortcut.CtrlN;

this.menuFileNew.Text = "&New...";

And code like this in VB:

Me.menuFileNew.Index = 0

Me.menuFileNew.Shortcut = System.Windows.Forms.Shortcut.CtrlN

Me.menuFileNew.Text = "&New..."

As we have already seen, the Index property

determines the order in which menu items appear. The

Text property determines what text should be

displayed. (If you set this to a hyphen, the menu item will appear as a

separator—a dividing line between

menu items.) The

ampersand denotes something called an

accelerator; both this and the

Shortcut

property allow experienced users to use menus much more quickly than

would otherwise be possible.

Accelerators and shortcut keys

Most Windows applications can be

controlled from the keyboard as well as with the mouse. In fact, this

is a requirement for earning the Designed for Windows logo.

Accelerator keys and shortcut keys are two long-established

mechanisms for making menus easier to use from the keyboard.

Menus can be navigated with the arrow keys, but with large menu

structures, this rapidly becomes tiresome, so accelerator keys are also supported. These are keys that can be

pressed to select a particular menu item without having to use the

mouse or arrow keys. Each item in a menu can have a letter associated

with it, and if the user presses that key while the menu is visible,

the effect is the same as clicking on the item.

The previous code fragments illustrate how to choose an accelerator

key: in the Text property, we simply place an

ampersand in front of the relevant letter. So in this example, if the

user presses the N key while the File menu is open, the New menu item

will be selected. The user can find out what accelerator keys are

available by pressing the Alt key while the menu is open: the

accelerators will be underlined, as shown in Figure 4-2. (Older versions of Windows show the

accelerators at all times, even when Alt has not been pressed.)

Figure 4-2. Menu with accelerators and shortcuts

Menu accelerators can make it easy for experienced users to use menus

quickly without taking their hands off the keyboard. However, for

very frequently used operations, keyboard shortcuts provide a more

direct form of access.

Unlike menu accelerators, which can only be used while the relevant

menu is visible, a

shortcut

key can be used at any time. In this example, the New menu

item’s shortcut key is Ctrl-N. The user can press

Ctrl-N without a menu visible, and it will be as if he had selected

the New item from the File menu.

Shortcuts are assigned with the

Shortcut property on

the MenuItem class, and its value must be one of

the key combinations enumerated in the Shortcut

enumeration. This is a subset of all possible key presses; it

includes the function keys, with various combinations of modifier

keys (e.g., F12 or

CtrlShiftF3), and the alphanumeric keys with Ctrl

or Ctrl and Shift, (e.g., CtrlA,

CtrlShiftQ, Ctrl3). By default,

the shortcut will be displayed in the menu, as shown in Figure 4-2, although this can be disabled by

setting the ShowShortcut property to

false.

Shortcut keys only work because the Form class

knows about menus—when handling key presses, a form will offer

keys to the both the main menu and the context menu for that form.

This means that shortcuts only work properly for menus that have been

attached to a form (as described later on). You would usually not use

them on context menus that have been attached to specific controls.

So we know how to create hierarchical menu structures, and how to

assign text, accelerators, and shortcut keys to menu items. But for

all this to be of any use, we need to know when the user clicks on

one of our menu items. So we will now look at the events raised by

menus.

Event Handling

The entire point of adding menus to an

application is so that users can ask the application to do something,

such as save a file or perform a search. So as developers, we want

our code to be notified whenever the user chooses an item from a

menu. Menus therefore provide events to inform us of user input.

The most important menu event is

Click. This is

very similar to the Control

class’s Click event[16]—a MenuItem raises this event

when the user clicks on the menu item or performs an equivalent key

press (using either an accelerator or a shortcut key). It even has

the same signature as Control.Click:

EventHandler. If you double-click on a menu item

in the Designer, Visual Studio .NET will add a new method and attach

it to the menu item’s Click

event, as shown in C# in Example 4-1 and in VB in

Example 4-2.

Example 4-1. Menu Click handler in C#

private void InitializeComponent()

{

    . . .

    this.menuFileNew.Click +=

        new System.EventHandler(this.menuFileNew_Click);

    . . .

}



private void menuFileNew_Click(object sender, System.EventArgs e)

{

    . . . handle click here

}

Example 4-2. Menu click handler in VB

Friend WithEvents menuFileNew As System.Windows.Forms.MenuItem



Private Sub menuFileNew_Click(ByVal sender As System.Object, _

  ByVal e As System.EventArgs) Handles menuFileNew.Click

    . . . handle click here

End Sub

The handler method’s first parameter is, as always,

the source of the event (the MenuItem object, in

this case). The second parameter is the usual placeholder and will

normally be EventArgs.Empty.

The MenuItem class also provides a

Popup event, which is

fired whenever a menu is about to be displayed. This provides a

useful opportunity to make sure that the state of all the items is up

to date (e.g., you can place ticks by certain menu items, as

described in the next section). The event occurs on the

MenuItem that represents the menu that is about to

appear. Its parent is not notified, and neither are the individual

items that make up the menu. For example, when the File menu in the

preceding examples is about to be displayed, the

Popup event would occur on the

menuFile object, not on the main menu, and not on

any of the File menu’s items.

You can also be notified when an item has been highlighted (i.e.,

when the mouse moves over it). That item’s

Select event is raised

when this happens. This event also occurs when the item is selected

with the arrow keys. The name Select is slightly

misleading. Selecting a menu item sounds like a fairly positive

operation by the user, but it typically indicates that the mouse has

simply moved over the item. The Click event is the

one raised when a user actively chooses an item.

If you are familiar with the old C++ MFC Library, you might be

expecting to see events for handling menu item state. In that

library, every time a menu item was displayed, an event was raised

asking whether the item should be enabled and whether it should have

a tick by it. In .NET, things are a little different—Windows

Forms exposes these features as properties on the

MenuItem object.

Menu Item State

You will often want a menu

item’s appearance to change according to the

application’s state. For example, a menu item that

turns something on or off (such as a status bar) can have a tick

beside it to indicate that the feature is currently on. Some menu

items may sometimes be unavailable and should be grayed out or even

hidden. We will now see how to modify the appearance and behavior of

menus at runtime to achieve this.

Each MenuItem has an

Enabled property. By

default, it is set to true, but when it is

false, the item will be grayed out and will not be

clickable. More drastically, you can set the

Visible property to false,

which will prevent the item from appearing at all. The

MenuItem class also provides a

Checked property. When

this is set, a tick will be displayed next to the menu item.

Tip

If you preferred the old MFC approach, in which you decided which

items should be ticked or disabled at the last minute, you can still

do this. Simply supply a Popup handler for the

menu and set the flags for each menu item in it. This approach can be

useful, because it guarantees that menu items are always up to date,

but an event-driven approach is no longer mandatory, so you can use

whichever is simpler for your particular application. Remember that

the Popup event is raised for the menu, not for

each of its items, so your code will not look quite the same as it

did with MFC—you will have a single handler setting the state

of all necessary items, rather than one handler per item.

So,

we now know how to create menus, how to handle the events they

generate, and how to modify the appearance of individual items. All

that remains is to make sure these menus appear when and where we

want them, which is the subject of the next section.

Attaching Menus

There are two ways in which a menu can

appear. It can either be permanently visible at the top of a form, or

it can be a so-called Context Menu that pops up when the

user clicks the right (or alternate) mouse button. In either case, we

simply associate a hierarchy of menu items with a form or a control.

The menu that appears at the top of a window is determined by the

Form class’s

Menu property. You can set this property

to a

MainMenu object

representing the root of a hierarchy of MenuItem

objects. The Forms Designer does this automatically when you add a

main menu to a form.

Setting a context menu is very similar, except context menus may be

assigned to any control, not just a form. This means that you can

provide different context menus for each control in a window. This is

done by setting the

ContextMenu property of

the control or form.

Remember that the Form class derives from

Control, so it is possible to set a context menu

for the whole form. Be aware though that when you add a context menu

to a form with the designer, Visual Studio .NET does not presume that

the menu should be attached to the form—for all it knows, you

might be planning to associate it with a control, so it leaves it

unattached. You must explicitly attach the menu either to the form or

to a control by setting the relevant object’s

ContextMenu property.

Be aware that keyboard

shortcuts for a context menu will only

work if the control that owns the menu is able to process keys. For a

context menu attached to a form, this means that the shortcuts will

work so long as the form is active, but for menus attached to a

particular control on a form, the shortcuts will only work when that

control has the focus. So it is not always useful to put shortcut

keys on a context menu attached to a control, because the whole point

of shortcut keys is that they can be used from any context. (The

exception would be if your control can receive the focus and presents

a nontrivial interactive user interface. For example, a text box

provides clipboard shortcuts such as Ctrl-C for copy. In this case,

it makes sense for the shortcuts only to be available when the

control has the focus.)

Sometimes it is useful to know if a menu is currently being displayed

to avoid distracting or interrupting the user—it can be

annoying if an application pops up a notification dialog while you

are using a menu, because this causes the menu to be closed. If you

want to disable or defer certain operations while a menu is open, you

simply need to observe the Form

class’s

MenuStart and

MenuComplete events. These are fired just before a

menu receives the focus and just after it disappears. These events

are fired for the form’s context menu as well as for

its main menu. Unfortunately, the form does not raise these events

for a control-specific context menu, and although you could handle

such a menu’s Popup event, there

is unfortunately no corresponding event to tell you when it goes

away.

Menu Merging

Many

applications present several forms that all have similar but slightly

different menus. This is particularly common when forms inheritance

is in use (see Chapter 6). MDI applications often

have a related requirement: a form may make subtle changes to its

menu structure depending on which child window (if any) is active.

Unfortunately, we cannot exploit inheritance here as we would for

building a group of similar forms: whereas the structure and behavior

of a form is represented by a class definition, the structure and

behavior of a menu is defined by an object graph constructed at

runtime. All menus are made from a collection of objects that are

always of the same types (several MenuItem objects

and either one MainMenu or one

ContextMenu), so inheritance cannot help us here.

The good news is that the Menu class provides a

solution. It provides a method called

MergeMenu

that allows us to take an existing menu structure and extend or

modify it to create a new menu. The resulting menu will be the

combination of the two menus, as illustrated in Figure 4-3.

Figure 4-3. Merged menus

Figure 4-3 illustrates the simplest way of using

menu merging—two menus are combined, and the result is a menu

containing all the items from each. The MergeMenu

method does a deep copy, so any submenus will also be duplicated.

This merging is easy to do—the following C# code (the VB code

is almost identical) shows how to create a new context menu by

merging the items from two other menus:

ContextMenu mergedMenu = new ContextMenu();

mergedMenu.MergeMenu(menuFirst);

mergedMenu.MergeMenu(menuSecond);

But even this simple example raises an interesting question: how does

the framework decide the order in which to place the items in the

created menu? While it has not reordered the items from each

individual menu in Figure 4-3, it has decided to

insert the items from the second menu halfway through those of the

first menu. The framework determines how to interleave the

menus’ contents by looking at the

MergeOrder property on

each MenuItem. This property is an

int or Integer, and the

framework guarantees that when combining menus, it will merge items

in ascending MergeOrder order. So the reason the

framework decides to insert the second menu’s

contents halfway down becomes clear when we see the

MergeOrder property settings on the original

menus:[17]

this.menuHello.MergeOrder = 5; 

this.menuWorld.MergeOrder = 5; 

this.menuSeparator1.MergeOrder = 10;

this.menuFoo.MergeOrder = 100;

this.menuBar.MergeOrder = 100;



this.menuMore.MergeOrder = 20;

this.menuStuff.MergeOrder = 20;

The More and Stuff items in the second menu have a merge order of 20,

which means that they appear between the separator and the Foo entry,

which have orders of 10 and 100, respectively. You can choose

whatever values you like for a merge order, but using 0 for the items

you want to appear first, 100 for those you want to appear last, and

more or less evenly spaced values for those in between is a popular

choice. (The default MergeOrder is 0.)

Advanced merging

The merging technique shown above is sufficient for many purposes,

but you might need to do something a little more complex. For

example, sometimes it is not enough simply to add new items to a

menu—you may wish to remove items. Also, if you want to insert

new items into a submenu instead of the top level menu, the

naïve approach is insufficient: by default you will end up

with two identically named submenus.

To support these more subtle merging techniques, the

MenuItem class provides a

MergeType property,

which controls the way an item is treated when it is merged. By

default, its value is MenuMerge.Add, meaning that

all items in the menu being merged will be added as new items.

You can set MergeType to

MenuMerge.Remove, which causes the corresponding

entry not to appear. You would use this value if the menu you are

modifying contains an entry you would like to remove.

Tip

You must set the MergeType property on both source

menus for this technique to work. If the new menu being merged into

the original menu attempts to remove an item, that attempt will be

ignored unless the original menu’s corresponding

item (the item with the same MergeOrder) is also

marked as MenuMerge.Remove.

So the

MergeType property really has two

meanings—on the original item it indicates the allowable

operations, and on the new item it indicates the operation being

requested. Unfortunately, not only is this overloading slightly

confusing, it is somewhat restrictive— there is no way to

create a menu item that allows both the MergeItem

and the Remove operations. Conflicts are also

dealt with a little inconsistently—if the original is

Add and the new is Remove, the

new item is ignored; if the original is Remove and

the new is MergeItem, the item is replaced!

Setting MergeType to

MenuMerge.Replace is similar to remove, except it

replaces the original item with the one in the menu being merged.

Finally, MergeType can be set to

MenuMerge.MergeItems. This value is used when you

wish to modify the contents of a submenu. Although you could modify a

submenu by replacing it entirely, MergeItems is

useful when you want to make only minor modifications. You use this

by supplying a MergeItems item corresponding to

the submenu item in the original menu, and then put child items

underneath it, using Add,

Remove, Replace, or

MergeItems as appropriate. For example, if you

wanted to add an item to a main menu’s File menu,

you would not replace the entire main menu, or even the entire File

menu. The following code (in C#) would suffice:

MenuItem mergingFileMenu = new MenuItem();

mergingFileMenu.MergeType = MenuMerge.MergeItems;

mergingFileMenu.Text = "&File";



MenuItem menuExtraFileItem = new MenuItem();

menuExtraFileItem.Text = "Ext&ra";

menuExtraFileItem.MergeOrder = 10;

MenuItem menuExtraFileSeparator = new MenuItem();

menuExtraFileSeparator.Text = "-";

menuExtraFileSeparator.MergeOrder = 10;



mergingFileMenu.MenuItems.AddRange(new MenuItem[] {

    menuExtraFileItem,

    menuExtraFileSeparator});



MainMenu mergingMainMenu = new MainMenu(new MenuItem[] {

    mergingFileMenu });

mainMenu.MergeMenu(mergingMainMenu);

This adds an item labeled Extra to the File menu, followed by a

separator. So even though we are merging two main menus together, we

are able to modify one of its submenus without having to replace that

submenu wholesale. And although this may look like a lot of code to

add a single item, it is rather less than would be required to

reconstruct the whole menu. Furthermore, building a modified File

menu from scratch presents code maintenance issues—if you want

to change the basic File menu, you would also need to change all the

places where you build a modified version. But if you use menu

merging, any changes to the basic menu will automatically propagate

to the modified versions.

Remember that this will only work if the original main

menu’s File menu item also has its

MergeType set to

MenuMerge.MergeItems; if it were set to the

default of MenuMerge.Add, you would end up with

two File submenus in the main menu.

Merging and MDI applications

If you build an MDI application, the

framework can automatically take advantage of menu merging. The menus

in such applications typically consist of two types of items: those

that are associated with a document window and those that are a part

of the main application frame. The set of available menu items is

determined by whether a document window is active (and if there is

more than one kind of document window, it will depend on which one is

active).

The application-level menu items are those that should always be

present and that make sense even if there are no open documents, such

as items for file opening, document creation, or application

configuration. Document-level menu items are those that only make

sense in the context of a document, such as items for file closing or

saving, editing operations, or view settings. MDI applications

usually present just a single menu bar as part of the main

application frame, but its contents change between being just the

application-level items or the complete set, according to whether a

document is active.

This seems like an ideal opportunity to use menu merging—the

application-level items could be placed into one

MainMenu, the document level items into a second

MainMenu, and these could be merged to create a

third. All that would need to be done would be to swap in the

appropriate merged or unmerged version according to whether a child

window is active. Indeed, this is exactly how MDI applications

usually work in .NET, but it turns out that Windows Forms can do the

menu merging automatically.

If you use the framework’s built-in support for MDI

applications (i.e., you establish the parent/child relationship with

the Form class’s

MdiParent or MdiChildren

properties), it will assume that the parent form’s

MainMenu contains the application-level menu

items, and that any child form’s

MainMenu contains document-level items. Whenever

an MDI child form is activated, the framework will automatically

merge its menu into the parent form’s menu. If all

MDI child windows are closed, it reverts to the original parent

form’s menu.

So using menu merging in MDI applications requires almost no effort.

The only thing you need to be careful about is setting the correct

MergeType—very

often a child window will want to add entries (such as for adding a

save and a close entry to the File menu) into an existing menu in the

parent. Both the parent and the child form’s main

menus will need to contain File submenus, which must both have the

same MergeOrder, and they must both have a

MergeType of

MenuMerge.MergeItems.

Merging and forms inheritance

It is possible to define a form that

derives from another form. We will be looking at the use of

inheritance in detail in Chapter 6, but we will

quickly examine the inheritance-related aspects of menu merging here.

When building an inherited form, the derived class will automatically

acquire the base class’s menu. However, if you try

to edit the menu in the derived class, the Forms Designer will

prevent you, complaining that the menu is defined on the base class.

To modify a menu on the derived form, you must use menu merging.

To modify the menu in the derived class, you must add a new

MainMenu component to the form. Place whatever

modifications you require in this menu. To merge this menu in with

the main menu requires a little code. Add the following C# code to

your constructor:

MainMenu mainMenu = new MainMenu();

mainMenu.MergeMenu(this.Menu);

mainMenu.MergeMenu(mainMenuDerived);

this.Menu = mainMenu;

The equivalent VB code is:

Dim mainMenu As New MainMenu()

mainMenu.MergeMenu(Me.Menu)

mainMenu.MergeMenu(mainMenuDerived)

Me.Menu = mainMenu

This builds a new MainMenu object, which takes the

original menu and merges in mainMenuDerived (or

whatever you choose to call the MainMenu that you

added to your derived form). It then sets this merged menu as the new

main menu of the form.

Owner-Drawn Menus

The

standard appearance provided by the Windows Forms framework for menu

items is pretty basic—you get simple text, and you can

optionally annotate menu items with a tick (just set the

Checked property). If you want to draw your own

annotations, or otherwise provide richer visual information, the

framework lets you draw your own menu items. The use of GDI+ to

perform custom drawing is discussed in detail in Chapter 7, but here we will look at the menu-specific

aspects of owner drawing.

You can decide to do your own drawing on a per-item basis by setting

the MenuItem object’s

OwnerDraw property to

true. Unfortunately, it is an all or nothing

decision: if you ask the framework to let you draw a particular item,

you are required to manage the whole drawing process. First, you must

tell the framework the size of your menu item by

handling the

MeasureItem event;

otherwise, the item will default to having a height of 0 pixels. And

you must also handle the

DrawItem event, in

which you are responsible for drawing everything, including the text

of the menu. (Turning on OwnerDraw will prevent

Windows from drawing anything other than the menu background.)

Tip

Because owner drawing is a per-item decision, it is possible to have

a single menu with a mixture of owner-drawn and system-drawn items.

You will receive the DrawItem event every

time the item needs redrawing. This happens when the menu appears,

directly after the MeasureItem event, but it also

happens again every time the mouse moves on or off the menu item, so

that you can highlight your item like Windows does for normal items.

Your event handler will be passed a

DrawItemEventArgs object, whose

State member indicates how the item should be

drawn. This field is of type DrawItemState, which

is a bit field, so it may indicate multiple styles simultaneously.

The flags that may be set are Selected (indicating

that the mouse is currently over the item),

NoAccelerator (indicating that accelerator keys

should not be displayed; this will normally be set unless the user is

operating the menu through the keyboard), and

Checked (indicating that a tick should be drawn by

the item).

Examples Example 4-3 and

Example 4-4 show a pair of event handlers for a very

simple owner-drawn menu. The item is always the same size because the

MeasureItem handler always returns the same width

and height. The DrawItem handler simply draws an

ellipse as the menu item, but it illustrates an important technique:

it checks the item’s state to see if it is selected,

and if so, it draws the menu background in the normal selected menu

item color, and draws the ellipse in the same color as selected text

in a menu would be drawn. Note the use of the

DrawBackground method of the

DrawItemEventArgs object to fill in the menu

background—it draws the background in the appropriate color

(i.e., SystemColors.Menu, unless the item is

selected, in which case it uses

SystemColors.Highlight). We call this whether the

item is in the selected state or not. You might think that this is

unnecessary because, as mentioned above, the framework draws the

background for us. Unfortunately it only does that when the menu is

first opened, so if we change the background when our item is

selected, we are required to put it back again when it is deselected.

Example 4-3. Simple owner-drawn menu item in C#

private void menuItem_MeasureItem(object sender,

    System.Windows.Forms.MeasureItemEventArgs e)

{

    e.ItemHeight = 17;

    e.ItemWidth = 100;

}



private void menuItem_DrawItem(object sender,

    System.Windows.Forms.DrawItemEventArgs e)

{

    Graphics g = e.Graphics;

    bool selected = (e.State & DrawItemState.Selected) != 0;

    Brush b = selected ?

        SystemBrushes.HighlightText : Brushes.Blue;

    e.DrawBackground();

    g.FillEllipse(b, e.Bounds);

}

Example 4-4. Simple owner-drawn menu item in VB

Private Sub menuItem_MeasureItem(sender As Object, _

  e As System.Windows.Forms.MeasureItemEventArgs) _

  Handles menuItem.MeasureItem

    e.ItemHeight = 17

    e.ItemWidth = 100

End Sub



Private Sub menuItem_DrawItem(sender As Object, _

  e As System.Windows.Forms.DrawItemEventArgs) _

  Handles menuItem.DrawItem

    Dim g As Graphics = e.Graphics

    Dim selected As Boolean = (e.State & DrawItemState.Selected) <> 0

    Dim b As Brush

    If selected Then

        b = SystemBrushes.HighlightText

    Else

        b = Brushes.Blue

    End If  

    e.DrawBackground()

    g.FillEllipse(b, e.Bounds)

End Sub

Now that we have seen how to create menus, let us see how we can

provide expert users with more direct access to the most frequently

used operations with toolbars.

Get .NET Windows Forms in a Nutshell now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как сделать снимок части экрана на компьютере windows 10
  • Syncthing как служба windows
  • Подборке программ от cuta system software for windows
  • Автопоиск драйверов windows 10 nvidia
  • Rootkits subverting the windows kernel subverting the windows kernel