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 | < Назад | Оглавление | Далее > |