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