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