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

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

Чтобы облегчить чтение и сопровождение программы, следует создавать меню в том же порядке, в котором они видны пользователю. Таким образом, конструктор вначале должен создавать первый пункт меню верхнего уровня (обычно File), затем все пункты подменю File (обычно New, Open, Save и т.д.), затем следующий пункт меню верхнего уровня (обычно Edit), затем пункты в подменю Edit и в самом конце — пункт About подменю Help.

Экспериментируя с созданием пунктов меню верхнего уровня с помощью метода Add класса Menu.MenuItemCollection, вы заметите, что разные версии метода Add неодинаковы. Некоторые полезней других. Вы можете выработать собственный стиль создания главного меню, используя только подмножество конструкторов MenuItem и методов Add. Я предлагаю задействовать только одну форму конструктора MenuItem и одну форму метода Add. Вы вполне можете пренебречь этим предложением. Я и сам пренебрегаю им в дальнейших главах. Но мне бы хотелось исследовать некоторые моменты, связанные с созданием меню.

Вначале рассмотрим создание некоего меню верхнего уровня без обработчиков и быстрых клавиш. Код выглядит примерно так:

  Menu = new MainMenu();
  Menu.MenuItems.Add("&File");
  Menu.MenuItems[0].MenuItems.Add("&Open...");
  Menu.MenuItems[0].MenuItems.Add("&Save...");
  ...

  Menu.MenuItems.Add("&Edit");
  Menu.MenuItems[1].MenuItems.Add("Cu&t");
  Menu.MenuItems[1].MenuItems.Add("&Copy");
  ...

Этот код хорошо структурирован (если не сказать, что он замечательный). Пункты меню верхнего уровня (File и Edit) добавляются к объекту MainMenu методом Add свойства MenuItems. Выражение Menu.MenuItems[0] относится к пункту File, а Menu.MenuItems[1] — к пункту Edit. Каждый из этих пунктов имеет собственное свойство MenuItems, представляющее набор пунктов подменю. Пункты типа Open, Save, Cut, Сору и т.д. добавляются методом Add этого свойства.

Кроме взаимоисключающих, большинство пунктов меню связано с собственными обработчиками события Click. Чтобы меню могло делать что-то полезное, придется преобразовать предыдущие выражения в примерно такие:

  Menu.MenuItems[0].MenuItems.Add("&Open...",
               new EventHandler(MenuFileOpenOnClick));

Но пункт меню Open обычно использует быструю клавишу Ctrl+O, а метода Add, принимающего в качестве аргумента быструю клавишу, нет. Вам придется вставить дополнительно выражение вроде:

  Menu.MenuItems[0].MenuItems[0].Shortcut = Shortcut.CtrlO;

для задания свойства Shortcut пункта меню Open. При этом, если позже вы измените свой код и вставите пункт New перед Open, выражение придется изменить, так как индекс изменится:

  Menu.MenuItems[0].MenuItems[1].Shortcut = Shortcut.CtrlO;

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

Возможно, гораздо лучше было бы вначале определить MenuItem примерно так:

  miFileOpen = new MenuItem{"&Open",
                    new EventHandler(MenuFileOpenOnClick),
                    Shortcut.CtrlO);

а затем добавить этот пункт меню в набор MenuItems:

  Menu.MenuItems[0].MenuItems.Add(miFileOpen);

Так как создание miFileOpen разбивается на несколько строк кода, возможно, проще будет использовать простой конструктор и явно присвоить свойства MenuItem:

  miFileOpen = new Menultem("&Open");
  miFileOpen.Click += new EventHandler(MenuFileOpenOnClick);
  miFileOpen.Shortcut = Shortcut.CtrlO;
  Menu.MenuItems[0].MenuItems.Add(miFileOpen);

А есть ли вообще смысл использовать версию Add, принимающую единственный аргумент типа string? Она может быть полезна при добавлении горизонтальной разделительной строки в меню:

  Menu.MenuItems[0].MenuItems.Add("-");

А еще — при добавлении пунктов меню верхнего уровня:

  Menu.MenuItems.Add("F&ormat");

При этом многие пункты меню верхнего уровня требуют наличия обработчиков события Popup, в которых можно было бы блокировать/разблокировать пункты подменю. И снова имеет смысл вначале создать объект MenuItem, а затем добавить созданный пункт к меню:

  MenuItem mi = new MenuItem("&File");
  mi.Popup += new EventHandler(MenuFileOnPopup);
  Menu.MenuItems.Add(mi);

Чтобы быть абсолютно последовательным при создании меню, понадобится всего одна форма конструктора MenuItem (версия с одним параметром типа siring) и одна версия метода Add (с параметром типа MenuItem).

Как вы могли заметить, я предпочитаю сводить количество переменных-полей класса к минимуму. Menu Designer из Visual Studio .NET делает каждый объект MenuItem полем. Это абсолютно не нужно! Так как большинство пунктов меню имеют собственные обработчики события Click, вовсе не обязательно сохранять все объекты MenuItem. Исключение составляют пункты, которые должны быть блокированы/разблокированы при обработке события Popup. Эти пункты могут храниться в полях.

Наконец, я хочу обратить ваше внимание на маленькую переменную типа int, которую я называю index. Каждый раз, когда вы добавляете пункт меню верхнего уровня, используя выражение вроде:

  Menu.MenuItems.Add(mi);

вы можете вычислить новое значение переменной index:

  index = Menu.MenuItems.Count - 1;

Эту переменную можно использовать для добавления пунктов к подменю, например так:

  Menu.MenuItems[index].MenuItems.Add(miFileOpen);

Для меню File использовать эту переменную вместо явного 0 едва ли необходимо: File будет первым пунктом главного меню отныне и вовеки веков. Но для других пунктов эта переменная может быть весьма полезна, особенно если однажды вы захотите изменить конструктор и вставить новое подменю.

Следующая программа демонстрирует мой подход к созданию стандартных меню последовательно сверху вниз с помощью единственной формы конструктора MenuItem и единственной формы метода Add.

StandardMenu.cs

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

  class StandardMenu: Form
  {
      MenuItem miFileOpen, miFileSave;
      MenuItem miEditCut, miEditCopy, miEditPaste;

      // Экспериментальные переменные для кода Popup

      bool bDocumentPresent  = true;
      bool bNonNullSelection = true;
      bool bStuffInClipboard = false;

      public static void Main()
      {
          Application.Run(new StandardMenu());
      }
      public StandardMenu()
      {
          Text = "Standard Menu";
          Menu = new MainMenu();

          // File

          MenuItem mi = new MenuItem("&File");
          mi.Popup += new EventHandler(MenuFileOnPopup);
          Menu.MenuItems.Add(mi);
          int index = Menu.MenuItems.Count - 1;

          // File - Open

          miFileOpen = new MenuItem("&Open...");
          miFileOpen.Click += new EventHandler(MenuFileOpenOnClick);
          miFileOpen.Shortcut = Shortcut.CtrlO;
          Menu.MenuItems[index].MenuItems.Add(miFileOpen);

          // File - Save

          miFileSave  = new MenuItem("&Save");
          miFileSave.Click += new EventHandler(MenuFileSaveOnClick);
          miFileSave.Shortcut = Shortcut.CtrlS;
          Menu.MenuItems[index].MenuItems.Add(miFileSave);

          // Горизонтальная линия

          mi = new MenuItem("-");
          Menu.MenuItems[index].MenuItems.Add(mi);

          // File - Exit

          mi = new MenuItem("E&xit");
          mi.Click += new EventHandler(MenuFileExitOnClick);
          Menu.MenuItems[index].MenuItems.Add(mi);

          // Edit

          mi = new MenuItem("&Edit");
          mi.Popup += new EventHandler(MenuEditOnPopup);
          Menu.MenuItems.Add(mi);
          index = Menu.MenuItems.Count - 1;

          // Edit - Cut

          miEditCut = new MenuItem("Cu&t");
          miEditCut.Click += new EventHandler(MenuEditCutOnClick);
          miEditCut.Shortcut = Shortcut.CtrlX;
          Menu.MenuItems[index].MenuItems.Add(miEditCut);

          // Edit - Copy

          miEditCopy = new MenuItem("&Copy");
          miEditCopy.Click += new EventHandler(MenuEditCopyOnClick);
          miEditCopy.Shortcut = Shortcut.CtrlC;
          Menu.MenuItems[index].MenuItems.Add(miEditCopy);

          // Edit - Paste

          miEditPaste = new MenuItem("&Paste");
          miEditPaste.Click += new EventHandler(MenuEditCopyOnClick);
          miEditPaste.Shortcut = Shortcut.CtrlV;
          Menu.MenuItems[index].MenuItems.Add(miEditPaste);

          // Help

          mi = new MenuItem("&Help");
          Menu.MenuItems.Add(mi);
          index = Menu.MenuItems.Count - 1;

          // Help - About

          mi = new MenuItem("&About StandardMenu...");
          mi.Click += new EventHandler(MenuHelpAboutOnClick);
          Menu.MenuItems[index].MenuItems.Add(mi);
      }
      void MenuFileOnPopup(object obj, EventArgs ea)
      {
          miFileSave.Enabled = bDocumentPresent;
      }
      void MenuEditOnPopup(object obj, EventArgs ea)
      {
          miEditCut.Enabled = bNonNullSelection;
          miEditCopy.Enabled = bNonNullSelection;
          miEditPaste.Enabled = bStuffInClipboard;
      }
      void MenuFileOpenOnClick(object obj, EventArgs ea)
      {
          MessageBox.Show("This should be a File Open dialog box!", Text);
      }
      void MenuFileSaveOnClick(object obj, EventArgs ea)
      {
          MessageBox.Show("This should be a File Save dialog box!", Text);
      }
      void MenuFileExitOnClick(object obj, EventArgs ea)
      {
          Close();
      }
      void MenuEditCutOnClick(object obj, EventArgs ea)
      {
          // Копирование выбранного фрагмента в буфер обмена
          // и удаление его из документа
      }
      void MenuEditCopyOnClick(object obj, EventArgs ea)
      {
          // Копирование выбранного фрагмента в буфер обмена
      }
      void MenuEditPasteOnClick(object obj, EventArgs ea)
      {
          // Копирование данных из буфера обмена в документ
      }
      void MenuHelpAboutOnClick(object obj, EventArgs ea)
      {
          MessageBox.Show("StandardMenu (C) 2001 by Charles Petzold", Text);
      }
  }

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


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

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