Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 28:

Стандартные меню приложений

Упражнение 5. Контекстное меню

Контекстное меню вызывается по щелчку правой кнопки мыши на соответствующем элементе управления и зависит от конкретного состояния этого элемента. Контекстное меню представлено классом System.Windows.Forms.ContextMenuStrip. Любой визуальный элемент управления, производный от класса Control, наследует общедоступное виртуальное свойство ContextMenuStrip, к которому можно привязать объект контекстного меню.

Элементами контекстного меню ContextMenuStrip, как и в случае с основным меню, порожденным классом MenuStrip, являются объекты класса ToolStripMenuItem. Эти объекты добавляются в коллекцию Items, являющуюся свойством класса контекстного меню ContextMenuStrip. Каждый из объектов-элементов контекстного меню имеет свойство-коллекцию DropDownItems, в которую помещают элементы вложенного меню.

Мы можем заготовить несколько различных объектов контекстного меню и привязать каждый из них к определенному элементу управления. Но эти же несколько объектов контекстного меню можно поочередно привязывать к одному и тому же элементу управления в динамическом режиме в зависимости от текущего состояния контекста.

Рассмотрим Упражнение 5, в котором будем использовать объекты контекстного меню. Мы создадим несколько контекстных меню, часть динамическим способом, часть декларативным способом. Объекты контекстных меню будем присоединять к различным элементам управления через свойство этих элементов ContextMenuStrip.

Создание формы для Упражнения 5

  • В панели Solution Explorer выделите корневой узел проекта StripControls и командой меню оболочки Project/Add Windows Form добавьте к проекту файл Form5.cs
  • Откройте файл Start.cs в режиме View Designer, выделите объект listBoxChoice и добавьте через панель Properties в свойство Items ссылку на Упражнение 5
  • Откройте файл Start.cs в режиме View Em, найдите обработчик listBoxChoice_SelectedIndexChanged() и добавьте в него код запуска формы Form5
Form frm;   // Объявили поле
        private void listBoxChoice_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Привязали номер упражнения к индексу списка
            ListBox list = (ListBox)sender;
            string formName = "Form" + (list.SelectedIndex + 1);
    
            ///////////////////////////////////////////////////
            // Следующий код взят из лабораторной работы №47
            ///////////////////////////////////////////////////
            // Читаем коллекцию запущенных форм
            FormCollection fc = Application.OpenForms;
            bool IsRun = false;
            // Перебираем коллекцию
            foreach (Form form in fc)
            {
                // Имя очередной запущенной формы сравниваем с закрепленным за кнопкой
                if (form.Name == formName)
                {
                    IsRun = true;   // Нашли, что форма запущена, поднимаем флаг
                    frm = form;     // Сохраняем ссылку на запущенную форму для фокуса
                    break;          // Выходим из цикла
                }
            }
    
            // Если форма не запущена - запускаем
            if (!IsRun)
            {
                switch (formName)
                {
                    case "Form1":
                        frm = new Form1();
                        break;
                    case "Form2":
                        frm = new Form2();
                        break;
                    case "Form3":
                        frm = new Form3();
                        break;
                    case "Form4":
                        frm = new Form4();
                        break;
                    case "Form5":
                        frm = new Form5();
                        break;
                }
    
                this.AddOwnedForm(frm);     // Сделать новую frm подчиненной Start
                // frm.Owner = this;        // Алтернативный способ назначить владельца
                frm.ShowInTaskbar = false;  // Не отображать метку окна в панели задач
                frm.Show();                 // Показать новую форму
            }
            else
                frm.Focus();                // Передать фокус запущенной форме        
        }
Листинг 28.39. Код запуска Form5 из обработчика формы Start
  • Поместите на форму Form5 компонент SplitContainer, присвойте его экземпляру имя splitContainer и установите свойства: Orientation= Horizontal, BorderStyle=Fixed3D, Dock=Fill

Создание контекстного меню динамическим способом

  • Откройте файл Form5.cs в режиме View Code и откорректируйте код, чтобы он стал таким
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
    
namespace StripControls
{
    public partial class Form5 : Form
    {
        // Поле для сохранения выбранного элемента меню
        ToolStripMenuItem itemChecked = new ToolStripMenuItem();
    
        // Конструктор
        public Form5()
        {
            InitializeComponent();
    
            splitContainer.Panel1.BackColor = Color.White;// Начальный цвет верхней панели
    
            // Разделение цветов на алфавитные группы, представленные подменю
            //             A, B, C, D, E, F, G, H, I, J, K, L, M,           
            int[] iMenu ={ 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 4,  
                5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6 };
            //  N, O, P, Q, R, S, T, U, V, W, X, Y, Z
    
            // Создание контекстного меню и присоединение к объекту формы
            System.Windows.Forms.ContextMenuStrip menu =
                new System.Windows.Forms.ContextMenuStrip();
            // Будет вызываться для верхней панели
            splitContainer.Panel1.ContextMenuStrip = menu;
    
            // Формируем пункты подменю в контекстном меню
            for (int i = 0; i <= 6; i++)
            {
                // Индекс первого вхождения образца соответствует коду ASCII для буквы 
                char first = Convert.ToChar(Array.IndexOf(iMenu, i) + 'A');   // Позиция в массиве
                // Индекс последнего вхождения образца соответствует ASCII латинской буквы 
                char last = Convert.ToChar(Array.LastIndexOf(iMenu, i) + 'A');// Позиция в массиве
    
                // Создали, настроили и добавили в коллекцию элемент меню
                ToolStripMenuItem item = new ToolStripMenuItem();
                item.Text = String.Format("Цвета от {0} до {1}", first, last);
                menu.Items.Add(item);
                // Включили в подменю столбец с флажками в 
                // дополнение к столбцу с изображениями
                ((ToolStripDropDownMenu)item.DropDown).ShowCheckMargin = true;
            }
    
            // Применяем рефлексию к структуре, представляющей цвета
            PropertyInfo[] api = typeof(Color).GetProperties();
            // Перебор цветов
            foreach (PropertyInfo pi in api)
            {
                if (pi.CanRead && pi.PropertyType == typeof(Color)
                    && pi.Name!="Transparent")
                {
                    // Извлекаем очередной цвет
                    Color color = (Color)pi.GetValue(null, null);
    
                    // Создаем очередной элемент подменю
                    ToolStripMenuItem item = new ToolStripMenuItem();
                    // По букве цвета определяем подменю, в котором разместим цвет
                    int i = iMenu[color.Name[0] - 'A'];// Любой букве - своя позиция
                    ((ToolStripMenuItem)menu.Items[i]).DropDownItems.Add(item);
    
                    // Продолжаем настраивать команду
                    item.Text = InsertSpaceOut(color.Name);// Пробелы перед заглавными буквами цвета
                    item.Name = color.Name;// Сохраняем название цвета для обработчика ColorOnClick
                    item.Image = CreateBitmap(color);// Тонкой ссылке присваиваем толстый объект
                    item.Click += new EventHandler(ColorOnClick);// Регистрация обработчика
                    if (color.Equals(this.BackColor))// Отметить флажком текущий цвет и сохранить ссылку
                        (itemChecked = item).Checked = true;
                }
            }
        }
    
        void ColorOnClick(object sender, EventArgs e)
        {
            // Для выбранного устанавливаем новый флажок
            // и меняем цвет клиентской области формы
            ToolStripMenuItem item = (ToolStripMenuItem)sender;
            itemChecked.Checked = false;// Сбрасываем старое
            (itemChecked = item).Checked = true;// Устанавливаем флажок и запоминаем элемент
            splitContainer.Panel1.BackColor = Color.FromName(item.Name);// Красим верхнюю панель
        }
    
        Bitmap CreateBitmap(Color color)
        {
            // Генерируем изображение в виде цветного прямоугольника
            Bitmap bm = new Bitmap(16, 16);
            Graphics gr = Graphics.FromImage(bm);
            gr.FillRectangle(new SolidBrush(color), 0, 0, 16, 16);
            gr.Dispose();   // Освобождаем контекст устройства
            return bm;
        }
    
        string InsertSpaceOut(string str)
        {
            // Вставляем пробелы перед заглавными буквами названия цвета
            for (int i = str.Length - 1; i > 1; i--)
                if (Char.IsUpper(str[i]))
                    str = str.Insert(i, " ");
            return str;
        }
    }
}
Листинг 28.40. Код файла Form5.cs создания контекстного меню динамическим способом
  • Запустите приложение и для верхней панели сплиттера испытайте работу контекстного меню

Должно получиться нечто, похожее показанному на рисунке с изменением выбранного цвета фона на верхней панели сплиттера


Создание линейки контекстных меню декларативным способом

Теперь посмотрим, как присоединяются поочередно контекстные меню к одному и тому же элементу управления в зависимости от некоторых условий.

  • Поместите на нижнюю панель объекта splitContainer еще один экземпляр компонента SplitContainer и оставьте его имя по умолчанию splitContainer1
  • Установите настройки объекта splitContainer1: Dock=Fill, BorderStyle=Fixed3D
  • Поместите на левую нижнюю панель splitContainer1.Panel1 три экземпляра компонента радиокнопки RadioButton с именами radioButton1, radioButton2, radioButton3 и измените их свойство Text на ContextMenu1, ContextMenu2, ContextMenu1
  • Установите для правой нижней панели splitContainer1.Panel2 свойство BackColor=Lime

После проведения указанных действий интерфейс формы Form5 должен стать таким


  • Создайте три экземпляра компонента ContextMenuStrip с именами по умолчанию: contextMenuStrip1, contextMenuStrip2, contextMenuStrip3
  • Последовательно выделите каждый объект из только что добавленных контекстных меню и создайте в нем декларативно заготовки элементов MenuItem


В результате должны получиться три объекта-заготовки контекстных меню со следующим интерфейсом

contextMenuStrip1 contextMenuStrip2 contextMenuStrip3



  • Выделите на форме Form5 все три радиокнопки одновременно и через панель Properties в режиме Events для события CheckedChanged создайте обработчик с именем radioButton_CheckedChanged(), который заполните следующим кодом
private void radioButton_CheckedChanged(object sender, EventArgs e)
        {
                if (radioButton1.Checked)
                    splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip1;
                else if (radioButton2.Checked)
                    splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip2;
                else
                    splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip3;
        }
Листинг 28.41. Код обработчика смены контекстных меню

При смене активной радиокнопки в обработчике происходит присоединение к правой нижней панели соответствующего объекта контекстного меню. Вызов контекстного меню выполняется щелчком правой кнопки мыши на правой нижней панели, представленной элементом splitContainer1.Panel2. Для обеспечения подобного механизма работы при первом вызове контекстного меню необходимо задействовать для этого элемента событие BindingContextChanged.

  • Выделите элемент splitContainer1.Panel2 и через панель Properties в режиме Events создайте для события BindingContextChanged обработчик splitContainer1_Panel2_BindingContextChanged(), который заполните так
private void splitContainer1_Panel2_BindingContextChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked)
                splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip1;
            else if (radioButton2.Checked)
                splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip2;
            else
                splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip3;
        }
Листинг 28.42. Код обработчика splitContainer1_Panel2_BindingContextChanged()
  • Запустите приложение и убедитесь в работоспособности всех 4-х контекстных меню, первое из которых создано динамически, а три остальных - декларативно


Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ