netlib.narod.ru< Назад | Оглавление | Далее >

Коллекция пунктов меню

В программе FirstMainMenu меню строилось снизу вверх. Сначала создавались объекты MenuItem для самых последних пунктов (типа Open, Save и т.д.). Затем они объединялись в массивы для создания пунктов верхнего уровня (File, Edit и т.д.). А пункты верхнего уровня собирались вместе в объект MainMenu.

Программа была бы стройнее и легче в сопровождении, если бы меню создавалось сверху вниз, начиная с объекта MainMenu, затем добавлялись бы объекты MenuItem, соответствующие пунктам меню верхнего уровня (File, Edit и т.д.), и, наконец, добавлялись бы пункты в подменю (Open, Save и т.д.).

Пара важных свойств, определенных в классе Menu, делает такой подход возможным. Как я уже говорил, классы MenuItem, MainMenu и ContextMenu порождены Menu и наследуют эти свойства:


Свойства Menu (выборочно)



Тип Свойство Доступ

bool IsParent Чтение
Menu.MenuItemCollection MenuItems Чтение


Вам ничего не напоминает Menu.MenuItemCottection? Этот класс очень похож на ImageList.ImageCollection из главы 11 и Control.ControlCollection из главы 12. Как и в них, в Menu.MenuItemCollection реализованы интерфейсы IList, ICollection и IEnumerable. К свойству MenuItems можно обращаться по индексу, как если бы это был массив объектов MenuItem. Можно вызывать методы Add, Remove и Clear.

Свойство IsParent указывает, является ли пункт меню родительским по отношению к другим пунктам и, следовательно, допустимо ли обращение к свойству MenuItems.

Кроме того, класс MenuItem содержит такие связанные свойства:


Свойства MenuItem (выборочно)



Тип Свойство Доступ

Menu Parent Чтение
int Index Чтение/запись


Свойство Parent указывает на родительское меню конкретного пункта меню, свойство Index (доступное для записи) указывает на индекс (начиная с 0) объекта MenuItem внутри конкретного подменю.

В классе Menu.MenuItemCollection реализованы методы, позволяющие добавлять дочерние пункты меню к главному или контекстному меню либо другому пункту:


Методы Menu.MenuItemCollection (выборочно)



MenuItem Add(string strText)
MenuItem Add(string strText, EventHandler ehClick)
MenuItem Add(string strText, MenuItem[] ami)
int Add(MenuItem mi)
int Add(int index, MenuItem mi)
void AddRange(MenuItem[] ami)


Для определения количества пунктов меню в коллекции и доступа к ним можно использовать следующие свойства (второе из которых индексатор):


Свойства Menu.MenuItemCollection



Тип Свойство Доступ

int Count Чтение
MenuItem [] Чтение


Пункты меню можно искать и удалять:


Методы Menu.MenuItemCollection (выборочно)



bool Contains(MenuItem mi)
int IndexOf(MenuItem mi)
void Remove(MenuItem mi)
void RemoveAt(int index)
void Clear()


В следующей версии программы ContextMenuDemo — ContextMenuAdd — для создания меню применяются метод Add и возможность индексирования в свойстве MenuItem. Вначале создается объект ContextMenu, и к нему добавляются пункты меню.

ContextMenuAdd.cs

  //-----------------------------------------------
  // ContextMenuAdd.cs (C) 2001 by Charles Petzold
  //-----------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class ContextMenuAdd: Form
  {
      MenuItem miColor;

      public static void Main()
      {
          Application.Run(new ContextMenuAdd());
      }
      public ContextMenuAdd()
      {
          Text = "Context Menu Using Add";

          ContextMenu  cm = new ContextMenu();
          EventHandler eh = new EventHandler(MenuColorOnClick);

          cm.MenuItems.Add("Black",   eh);
          cm.MenuItems.Add("Blue",    eh);
          cm.MenuItems.Add("Green",   eh);
          cm.MenuItems.Add("Cyan",    eh);
          cm.MenuItems.Add("Red",     eh);
          cm.MenuItems.Add("Magenta", eh);
          cm.MenuItems.Add("Yellow",  eh);
          cm.MenuItems.Add("White",   eh);

          foreach (MenuItem mi in cm.MenuItems)
              mi.RadioCheck = true;

          miColor = cm.MenuItems[3];
          miColor.Checked = true;
          BackColor = Color.FromName(miColor.Text);

          ContextMenu = cm;
      }
      void MenuColorOnClick(object obj, EventArgs ea)
      {
          miColor.Checked = false;
          miColor = (MenuItem) obj;
          miColor.Checked = true;

          BackColor = Color.FromName(miColor.Text);
      }
  }

Можно было бы уменьшить размер исходного кода программы, присвоив свойству ContextMenu формы новый объект ContextMenu:

  ContextMenu = new ContextMenu();

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

  ContextMenu.MenuItems.Add("Black", eh);

Ранее я преобразовал ContextMenuDemo в программу BetterContextMenu, определив класс MenuItemColor, являющийся наследником MenuItem, но хранящий объект Color. Как же преобразовать ContextMenuAdd, чтобы в ней задействовать объекты MenuItemColor?

Очевидно, что нельзя применять такой же метод Add, что и в ContextMenuAdd. Вызов

  cm.MenuItems.Add("Black", eh);

неявно создает новый объект типа MenuItem и добавляет его в набор пунктов меню. Результат следующего выражений тот же, но объект создается в явном виде:

  cm.MenuItems.Add(new MenuItem("Black", eh));

Чтобы преобразовать программу с использованием класса MenuItemColor, нужно выполнять примерно такие вызовы:

  cm.MenuItems.Add(new MenuItemColor(Color.Black, "Black", eh));

Следующая программа содержит единственный пункт меню верхнего уровня Facename и во время обработки события Popup добавляет в это меню все доступные названия шрифтов. Теоретически это лучше, чем создавать меню при запуске программы, так как тогда появляется возможность выводить имена шрифтов, добавленных после запуска программы.

FontMenu.cs

  //-----------------------------------------
  // FontMenu.cs (C) 2001 by Charles Petzold
  //-----------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class FontMenu: Form
  {
      const int iPointSize = 24;
      string    strFacename;

      public static void Main()
      {
          Application.Run(new FontMenu());
      }
      public FontMenu()
      {
          Text = "Font Menu";

          strFacename = Font.Name;

          Menu = new MainMenu();

          MenuItem mi = new MenuItem("&Facename");
          mi.Popup += new EventHandler(MenuFacenameOnPopup);
          mi.MenuItems.Add(" "); // Необходимо для события Popup
          Menu.MenuItems.Add(mi);
      }
      void MenuFacenameOnPopup(object obj, EventArgs ea)
      {
          MenuItem     miFacename = (MenuItem)obj;
          FontFamily[] aff        = FontFamily.Families;
          EventHandler ehClick    = new EventHandler(MenuFacenameOnClick);
          MenuItem[]   ami        = new MenuItem[aff.Length];

          for (int i = 0; i < aff.Length; i++)
          {
              ami[i] = new MenuItem(aff[i].Name);
              ami[i].Click += ehClick;

              if (aff[i].Name == strFacename)
                  ami[i].Checked = true;
          }
          miFacename.MenuItems.Clear();
          miFacename.MenuItems.AddRange(ami);
      }
      void MenuFacenameOnClick(object obj, EventArgs ea)
      {
          MenuItem mi = (MenuItem)obj;
          strFacename = mi.Text;
          Invalidate();
      }
      protected override void OnPaint(PaintEventArgs pea)
      {
          Graphics grfx = pea.Graphics;
          Font     font = new Font(strFacename, iPointSize);

          StringFormat strfmt  = new StringFormat();
          strfmt.Alignment     = StringAlignment.Center;
          strfmt.LineAlignment = StringAlignment.Center;

          grfx.DrawString("Sample Text", font, new SolidBrush(ForeColor), 
                          ClientRectangle, strfmt);
      }
  }

При определении меню в конструкторе в подменю добавляется один пустой пункт. Для вызова события Popup требуется наличие в подменю хотя бы одного пункта.

Обработчик события MenuFacenameOnPopup начинается с получения пункта меню верхнего уровня:

  MenuItem miFacename = (MenuItem)obj;

Затем метод получает массив доступных названий шрифтов с помощью статического метода FontFamily.Families. Затем определяется массив MenuItem необходимого размера и инициализируются его элементы, попутно устанавливая свойство Check пункта, соответствующего названию шрифта из поля strFacename.

После инициализации массива MenuItem метод завершается очисткой всех элементов всплывающего меню и добавлением нового массива пунктов:

  miFacename.MenuItems.Clear();
  miFacename.MenuItems.AddRange(ami);

С целью уменьшения размера исходного кода в программе не обрабатывается ситуация, возникающая, если будет выбран шрифт, к которому неприменим стиль FontStyle.Regular (например, Aharoni). Если выбрать такой шрифт, конструктор Font в методе OnPaint сгенерирует исключение, и программа завершится. (Подробнее об этом и о работе со шрифтами см. главу 9.)

Но настоящая проблема в этой программе — размер подменю. Вполне вероятно, что высота списка будет больше высоты дисплея. Очевидно, для выбора шрифта больше подходят диалоговые окна, и я это покажу в главе 16.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz