Файлы к лабораторной работе Вы можете скачать здесь.
В библиотеке .NET Framework 2.0 появились
три новых класса для организации пользовательского интерфейса
управления приложением ( -
полоса):
System.Windows.Forms.MenuStrip - главное
менюSystem.Windows.Forms.ToolStrip - панель инструментовSystem.Windows.Forms.StatusStrip - панель состоянияНаряду с этими новыми элементами существуют и поддерживаются
прежние аналогичные классы, существующие
в версиях .NET Framework 1.x:
System.Windows.Forms.MainMenu System.Windows.Forms.ToolBar System.Windows.Forms.StatusBar По умолчанию эти старые классы не подключены к панели Toolbox,
поэтому не видны в ней, хотя и находятся в GAC (

Обе группы классов, старые и новые, обладают почти равной функциональностью, но стиль редактирования в режиме дизайнера и возможности несколько отличаются, как и наименования некоторых свойств и методов. Кроме того, новые классы являются более совершенными. Они поддерживают не только кнопки с растрами, но и текстовые поля, списки и выпадающие меню.
В данной работе мы будем рассматривать вопросы, касающиеся способов построения различных видов меню. Для демонстрации упражнений с элементами меню создадим заготовку приложения со стартовой формой.
Мы еще не знаем, сколько упражнений получится в этой работе,
поэтому применим список ListBox, в который
можно добавлять сколько угодно опций вызова упражнений. При
этом будем использовать рассмотренный ранее прием предотвращения
запуска уже запущенной формы.
Выполните
команду File/New/Project и заполните мастер создания приложения
в соответствии с рисунком
Через
панель Solution Explorer переименуйте файл Form1.cs в Start.cs,
а заголовку окна формы присвойте значение Text =" Lab48
- Имя студента "
Поместите
на форму библиотечный компонент ListBox и
настройте его в соответствии с таблицей свойств| Свойство | Значение |
|---|---|
Name |
listBoxChoice |
Items |
Упражнение 1. |
| Упражнение 2. | |
| Упражнение 3. | |
|
Через
панель Properties создайте обработчик для
события SelectedIndexChanged объекта listBoxChoice,
который заполните такForm frm; // Объявили поле
private void listBoxChoice_SelectedIndexChanged(object sender, EventArgs e)
{
// Привязали номер упражнения к индексу списка
ListBox list = (ListBox)sender;
string formName = "Form" + (list.SelectedIndex + 1);
///////////////////////////////////////////////////
// Следующий код взят из лабораторной работы №47
///////////////////////////////////////////////////
// Читаем коллекцию запущенных форм
FormCollection fc = Application.OpenForms;
bool IsRun = false;
// Перебираем коллекцию
foreach (Form form in fc)
{
// Имя очередной запущенной формы сравниваем с закрепленным за кнопкой
if (form.Name == formName)
{
IsRun = true; // Нашли, что форма запущена, поднимаем флаг
frm = form; // Сохраняем ссылку на запущенную форму для фокуса
break; // Выходим из цикла
}
}
// Если форма не запущена - запускаем
if (!IsRun)
{
switch (formName)
{
case "Form1":
frm = new Form1();
break;
case "Form2":
frm = new Form2();
break;
case "Form3":
frm = new Form3();
break;
}
this.AddOwnedForm(frm); // Сделать новую frm подчиненной Start
// frm.Owner = this; // Алтернативный способ назначить владельца
frm.ShowInTaskbar = false; // Не отображать метку окна в панели задач
frm.Show(); // Показать новую форму
}
else
frm.Focus(); // Передать фокус запущенной форме
}
Недостаток данного кода в том, что мы привязали вызов формы упражнения к индексу списка.
Командой
меню оболочки Project/Add Windows Form добавьте
в проект еще три формы с именами Form1, Form2, Form3
Запустите
приложение и убедитесь в работоспособности механизма вызова
отдельных упражненийРассмотрим простую программу, имитирующую работу меню.
Откройте
файл Form1.cs в режиме View Code и добавьте к конструктору
класса код создания простого менюpublic Form1()
{
InitializeComponent();
this.Text = "Простое меню";
// Главное меню
MenuStrip menu = new MenuStrip();
this.Controls.Add(menu);
// Раздел File
ToolStripMenuItem itemFile = new ToolStripMenuItem();
itemFile.Text = "File";
menu.Items.Add(itemFile);// Элемент верхнего уровня
// Раздел Help
ToolStripMenuItem itemHelp = new ToolStripMenuItem();
itemHelp.Text = "Help";
menu.Items.Add(itemHelp);
// Заполнение раздела File
// Команда Open...
ToolStripMenuItem itemOpen = new ToolStripMenuItem();
itemOpen.Text = "Open...";
itemOpen.ShortcutKeys = Keys.Control | Keys.O; // Ctrl+O
itemOpen.ShowShortcutKeys = true;// Отображать в команде
// Регистрировать вручную!!!!!!!!!!!!!!
itemOpen.Click += new EventHandler(itemOpen_Click);
itemFile.DropDownItems.Add(itemOpen);
// itemOpen.ShowShortcutKeys = false;// Скрыть комбинацию
// Разделитель
ToolStripSeparator itemSep = new ToolStripSeparator();
itemFile.DropDownItems.Add(itemSep);
// Команда Exit
ToolStripMenuItem itemExit = new ToolStripMenuItem();
itemExit.Text = "Exit";
// Регистрировать вручную!!!!!!!!!!!!!!
itemExit.Click += new EventHandler(itemExit_Click);
itemFile.DropDownItems.Add(itemExit);
// Заполнение раздела Help
// Команда About...
ToolStripMenuItem itemAbout = new ToolStripMenuItem();
itemAbout.Text = "About...";
itemAbout.ShortcutKeys = Keys.Control | Keys.A; // Ctrl+A
itemAbout.ShortcutKeyDisplayString = "Ctrl^A";// Свое отображение
itemAbout.ShowShortcutKeys = true;// Отображать в команде
// Регистрировать вручную!!!!!!!!!!!!!!
itemAbout.Click += new EventHandler(itemAbout_Click);
itemHelp.DropDownItems.Add(itemAbout);
}
Перепишите
выделенные строки кода регистрации обработчиков события Click
вручную, чтобы правильно создать заготовки обработчиков,
и заполните обработчики следующим кодомvoid itemAbout_Click(object sender, EventArgs e)
{
MessageBox.Show("Заготовка опции \"About...\"", this.Text);
}
void itemExit_Click(object sender, EventArgs e)
{
this.Close();
}
void itemOpen_Click(object sender, EventArgs e)
{
MessageBox.Show("Заготовка опции \"Open\"", this.Text);
}
}
В программе мы создаем объект меню, как экземпляр класса MenuStrip,
и привязали его к объекту формы .
У класса MenuStrip есть свойство Items,
которое является коллекцией элементов меню верхнего уровня.
Мы создаем два таких объекта itemFile и itemHelp типа ToolStripMenuItem и
добавляем их в свойство Items. У каждого объекта
типа ToolStripMenuItem есть
свойство DropDownItems, готовое хранить коллекцию
команд и разделителей, в том числе вложенных для построения
меню сложной иерархии. В эти свойства элементов верхнего уровня
мы добавляем объекты команд.
Схематично такая процедура построения меню примера изображена на рисунке

Символ амперсанда в значении свойства Text наименования
команды ассоциируется со знаком подчеркивания следующей за
ним буквы и позволяет после нажатия клавиши Alt (не
удерживая) вызывать команды простым нажатием соответствующих
клавиш. Знак подчеркивания в работающей
программе появляется только после щелчка на клавише Alt.
Свойство ShortcutKeys объекта команды ожидает
побитовую комбинацию элементов перечисления System.Windows.Forms. Keys,
задающую клавиши быстрого вызова этой команды. Логическое свойство ShowShortcutKeys объекта команды включает
или выключает отображение заданной комбинации быстрого вызова
справа от команды. По умолчанию задано значение true и
комбинация клавиш отображается в том случае, если она назначена
в свойстве ShortcutKeys. Свойство ShortcutKeyDisplayString позволяет
задать иную строку отображения установленной комбинации, чем
принято по умолчанию (например, вместо Ctrl+A по умолчанию можно задать Ctrl^A ).
Событие Click объекта
команды возникает при выборе соответствующей команды и вызывает
выполнение закрепленного обработчика.
Следует иметь ввиду, что как в коллекции Items меню
верхнего уровня, так и в коллекциях DropDownItems выпадающих
меню элементы отображаются в порядке их добавления в коллекцию.
Свойства Items и DropDownItems являются
экземплярами класса ToolStripItemCollection,
который имеет метод Add(). Именно этим методом
мы добавляем в ToolStripMenuItem.
Однако можно
использовать и метод коллекции AddRange(),
который ожидает ссылку на массив объектов команд. Так, например,
пусть объекты itemFile, itemOpen, itemSep, itemExit уже
созданы. Тогда к объекту верхнего уровня можно присоединить
сразу все подчиненные объекты команд такой строкой кода:
itemFile.DropDownItems.AddRange(new ToolStripMenuItem[]{
itemOpen, itemSep, itemExit});
Add() позволяют добавить команду
меню вместе с текстом, текстом и изображением, текстом + изображением
+ именем обработчика события Click.
В коде упражнения есть один недостаток. Ссылки на команды
меню объявляются внутри конструктора как
В современных меню слева от команд находится столбец, в котором могут отображаться изображения или флажки. В этом упражнении рассмотрим управление флажками.
Появление флажка слева от команды включается булевым свойством Checked объекта команды. В качестве альтернативы
можно установить свойство CheckState в одно из значений
одноименного перечисления. Например для объекта itemOpen это
можно определить так
itemOpen.CheckState = CheckState.Indeterminate ;itemOpen.CheckState = CheckState.Checked ;itemOpen.CheckState = CheckState.Unchecked ;Опция позволяет отобразить неопределенное состояние,
когда на месте флажка помещается точка.
Булево свойство CheckOnClick делает флажок
самоуправляемым, когда его состояние меняется по щелчку автоматически.
Но если группа флажков используется как радиокнопки для выбора
одного из нескольких взаимоисключающих элементов, то управление
их состоянием нужно организовывать вручную.
В этом упражнении мы создадим меню Font, которое будет выводить
список шрифтов, поддерживаемых компьютером. Обычно у современных
операционных систем этот список большой и не помещается на
экране, поэтому будем использовать объект класса ToolStripDropDownMenu.
Этот класс отвечает за появление стрелок, позволяющих перемещаться
по меню, не вмещающему команды. Здесь мы используем создание
и настройку меню частично в декларативном, частично в программном
режиме. Ну уж а управление логикой работы меню можно выполнить
только в программном режиме.
Откройте
форму Form2 в режиме View Designer и
поместите из
панели Toolbox на форму экземпляр компонента MenuStrip с
именем menu
При
выделенном элементе меню в панели Properties раскройте свойство Items
В окне
редактора меню выберите из раскрывающегося списка элемент MenuItem и щелчком на кнопке Add добавьте
в коллекцию Items
Присвойте
добавленному элементу через таблицу свойств редактора меню Name = itemFormat, Text = Format и
щелчком на кнопке OK завершите редактирование
При
выделенном элементе ItemFormat в панели Properties раскройте
свойство DropDownItems
В
окне редактора меню выберите из раскрывающегося списка элемент MenuItem и
щелчком на кнопке Add добавьте в коллекцию DropDownItems объект
команды меню
Присвойте
добавленному элементу через таблицу свойств редактора меню Name = itemFont, Text = Font и
щелчком на кнопке OK завершите редактирование
Выполните
приложение и проверьте работоспособность Form2 на данном
этапе
Поместите
из панели Toolbox экземпляр элемента Panel и настройте его
свойства так:Name = panel, Dock = Fill
Выделите
объект panel и через панель Properties в режиме Events зарегистрируйте
для события Paint обработчик и именем PanelOnPaintНа этом декларативная часть работы закончена. Далее займемся программной частью.
Перейдите
в режим View Code формы Form2 и добавьте перед конструктором
класса Form2 объявление ссылки на элемент менюpublic partial class Form2 : Form
{
// Объявили поле-ссылку на объект команды меню
ToolStripMenuItem itemSelectedFont;
public Form2()
{
InitializeComponent();
}
private void PanelOnPaint(object sender, PaintEventArgs e)
{
}
}
Добавьте
в Form2 после вызова функции инициализации
компонентов код динамического заполнения списка команд в
коллекцию команды Fontpublic Form2()
{
InitializeComponent();
// Заполнение коллекции объекта itemFont
// списком доступных шрифтов стиля "Обычный"
Graphics gr = CreateGraphics(); // Ссылка на контекст устройства
// Читаем все шрифты
FontFamily[] fonts = FontFamily.GetFamilies(gr);
// Перебираем шрифты и формируем команды меню Font
foreach (FontFamily font in fonts)
{
// Выбираем шрифты одного стиля "Обычный"
if (font.IsStyleAvailable(FontStyle.Regular))
{
// Добавляем в коллекцию команды Font
ToolStripMenuItem item =
(ToolStripMenuItem)itemFont.DropDownItems.Add(font.Name);
// Если встретился текущий шрифт формы,
// запоминаем эту команду и отмечаем флажком
if (font.Name == this.Font.Name)
{
itemSelectedFont = item;
itemSelectedFont.Checked = true;
}
}
}
gr.Dispose(); // Освобождаем ограниченный ресурс
}
Запустите
приложение и удостоверьтесь, что при раскрытии команды меню Font она уже заполнена элементами названий шрифтовТеперь обеспечим нужную функциональность выбора шрифтов и
отображения их названия на клиентской области панели panel.
Подпишите
добавленный в коллекцию команды Font элемент item на событие Click. Для этого наберите вручную!!!, чтобы правильно создать
обработчик, следующий код в конце условия ifpublic Form2()
{
....................................................
// Перебираем шрифты и формируем команды меню Font
foreach (FontFamily font in fonts)
{
// Выбираем шрифты одного стиля "Обычный"
if (font.IsStyleAvailable(FontStyle.Regular))
{
...............................................
// Следующую строчку введите вручную!!!
item.Click += new EventHandler(item_Click);
}
}
gr.Dispose(); // Освобождаем ограниченный ресурс
}
Заполните
обработчик item_Click() следующим
кодом, который будет сбрасывать флажок с выбранного ранее
шрифта и устанавливать его на новом выбранном шрифтеvoid item_Click(object sender, EventArgs e)
{
// Повышаем полномочия ссылки для адресации к выделенной команде
ToolStripMenuItem item = (ToolStripMenuItem)sender;
itemSelectedFont.Checked = false; // Сбрасываем предыдущий флажок
item.Checked = true; // Поднимаем новый флажок
itemSelectedFont = item;// Запоминаем отмеченную команду
panel.Invalidate(); // Перерисовываем панель с новым шрифтом
}
Найдите
созданный ранее обработчик события Paint панели и заполните
его кодом перерисовки панели с новым шрифтомprivate void PanelOnPaint(object sender, PaintEventArgs e)
{
Graphics gr = e.Graphics; // Получаем контекст устройства
// Создаем новый шрифт по выбранному в меню с заведомо
// большим размером, чтобы потом подогнать уменьшением
float emSize = 100.0F;
System.Drawing.Font font =
new Font(itemSelectedFont.Text.Trim(), emSize);
String str = font.Name; // Строка для рисования
// Подгоняем размер текстового блока под ширину формы
float widthText = gr.MeasureString(str,
font = new Font(itemSelectedFont.Text, emSize)).Width;
while ((panel.ClientSize.Width - widthText) < 0.0F)
{
emSize -= 0.5F;
widthText = gr.MeasureString(str,
font = new Font(itemSelectedFont.Text, emSize)).Width;
}
// Настроим точку привязки текстового блока
System.Drawing.StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center; // По горизонтали
fmt.LineAlignment = StringAlignment.Center; // По вертикали
// Настроим точку привязки клиентской области панели
PointF point = new PointF(panel.ClientSize.Width / 2,
panel.ClientSize.Height / 2);
// Рисуем на панели название выбранного шрифта текущим цветом формы
gr.DrawString(str, font,
new SolidBrush(this.ForeColor), point, fmt);
}
В классе Form2 переопределите унаследованный OnResize() (ввод начните с ключевого слова override ) и заполните
его следующим кодомprotected override void OnResize(EventArgs e)
{
panel.Invalidate();
base.OnResize(e);
}
Этот метод нужен для перерисовки панели внутри формы при изменении пользователем ее размеров.
Запустите Form2 и удостовертесь в ее работоспособности,
результат будет таким
Обратите внимание, что мы обеспечили центрирование текста на панели и автоматическую его подгонку под размеры формы.
Хотя информативность названий команд меню может оказаться достаточной, в современных меню применяются еще и смысловые пиктограммы. Это нужно для того, чтобы постепенно приучить пользователя к изображениям команд и безболезненно перевести его на более быстрый способ использования кнопок панели инструментов с теми же самыми изображениями.
Visual
Studio 2005 Standard Edition входит архив с большим набором
готовых пиктограмм, который после установки на компьютере
находится в каталоге C:\Program Files\Microsoft Visual
Studio 8\Common7\VS2005ImageLibraryЕсли распаковать этот архив, то используемые в меню изображения
хранятся в папке bitmaps/commands, рассортированные
по разной глубине цвета. Если
в Вашей системе нет этих пиктограмм, то их можно найти в прилагаемом
к данной лабораторной работе каталоге Source.
Служебные изображения в программе удобнее использовать как
ресурсы, внедренные в исполнимую сборку и Project/Add Existing Item, после чего оболочка
физически скопирует его в каталог размещения проекта и он появится
в панели Solution Explorer.
Далее можно использовать
один из способов превращения этого файла в ресурс приложения:
выделить этот файл и через панель Properties установить
для него опцию Build равной . Это заставит оболочку при компиляции
внедрять указанный файл как ресурс в сборку приложения. Внутри
самого кода приложения мы можем адресоваться к этому файлу.
Пусть, например, мы хотим использовать пиктограмму Open. и
добавили этот файл в проект. Доступ к изображению из
Bitmap bmCut = new Bitmap(this.GetType(), "path.Open.bmp");
Первый аргумент должен ссылаться на класс приложения, в нашем случае это порождающий класс текущего объекта. Второй аргумент, это имя файла изображения с путем относительно корневой папки проекта
Присоединить изображение к элементу меню можно присвоением
ссылки свойству Image соответствующей команды,
например
itemCut.Image = bmCut;
После таких действий пиктограмма элемента будет отображаться слева от текста меню на месте отображения флажка.
В данном упражнении разработаем простой редактор изображений, в котором можно будет загружать и сохранять файлы изображений, использовать буфер обмена и отображать рисунки несколькими способами. При этом задействуем ряд стандартных диалоговых окон. Для тренировки рассмотрим несколько способов загрузки изображений в элементы меню.
Откройте
файл Form3.cs в режиме View Designer и задайте свойству Text формы значение " Простой редактор изображений "
Поместите
на форму компонент PictureBox и настройте его так: Name = pictureBox, Dock = Fill В дальнейшем экземпляр элемента управления PictuteBox будет
служить контейнером при просмотре изображений в нашем законченном
упражнении.
Меню для нашего упражнения мы создадим, чередуя декларативный
и Designer.cs. По большому
счету разницы нет, как создавать код: с помощью графического
инструмента или непосредственно вручную. Главное знать, что
писать и как позиционировать объекты. При создании меню проблемы
с
Поместите
на форму компонент MenuStrip с именем menu и
настройте его декларативно в соответствии с приведенным ниже
деревом свойствСоздавать декларативным
способом новые элементы меню любого уровня удобнее, если
навести курсор справа от заготовки команды и раскрыть список
предлагаемых типов, затем выделенный элемент меню можно настроить
через панель Properties.


На схеме свойство ( Name ) взято в скобки,
поскольку это не свойство элемента меню, а имя ссылки, адресующейся
к объекту. Хотя у элементов управления все же есть свойство Name типа String, но его чаще всего используют как метку элемента
для его узнаваемости в коллекции, но не как адресную ссылку.
Свойство ImageTransparentColor устанавливает
цвет в изображении пиктограммы, который система должна считать
прозрачным. В нашем случае каждая загружаемая пиктограмма
имеет рамку цвета , и если не установить
этот цвет в свойстве ImageTransparentColor,
то рамка будет видна на пиктограмме в меню.
Когда снимают
фильм со спецэффектами, передний план тоже снимают на фоне
экрана, выкрашенного в однотонный редко встречающийся цвет.
При монтаже цвет фона легко убирают и подсовывают вместо него
любой нужный материал. Этот редкий экзотический цвет и можно
считать ImageTransparentColor,
который система считает прозрачным.
Мы создали декларативным способом объекты команд всплывающего меню только для двух элементов верхнего уровня. Для третьего элемента объекты команд создадим программно чуть позже. Но сначала нужно в проект загрузить пиктограммы декларативно.
Изображения мы планируем добавить к следующим объектам команд:
| Элемент меню | Файл с пиктограммой |
|---|---|
itemOpen |
Open. |
itemCut |
|
itemCopy |
Copy. |
itemPaste |
Paste. |
itemDelete |
Delete. |
| itemSaveAs | Save. |
Большинство современных компьютеров поддерживают глубину цвета 24 бита, поэтому готовые рисунки будем копировать из соответствующей папки с именем 24bitcolor, которую можно найти в прилагаемом каталоге Source (или в архиве C:\Program Files\Microsoft Visual Studio 8\Common7\VS2005ImageLibrary\VS2005ImageLibrary.zip ).
Пиктограммы в исполнимую сборку программы, для тренировки, будем загружать несколькими способами:
Embedded Resource ),
чтобы компилятор оболочки автоматически включил их код в
исполнимую сборку (файл Paste.bmp )Image элемента
через стандартный файл глобальных ресурсов, используя графические
средства оболочки (файл Delete.bmp )Во всех случаях после компиляции код рисунков должен оказаться в сборке и мы во время исполнения будем присоединять его к элементам меню по мере необходимости, правда, немного по разному.
В панели Solution Explorer двойным щелчком на локальном XML -файле разметки Form3.resx вызовите
редактор ресурсов и через первый узел меню установите режим Images
Выполните
команду Add Resource/Add Existing File меню
редактора ресурсов и, удерживая клавишу Ctrl,
добавьте в ресурс файлы Cut.bmp и Copy.bmp глубиной 24bitcolor из прилагаемого к работе каталога Source
Выполните
команду View Details меню View редактора
ресурсов, чтобы отобразить представление ресурсов в виде
таблицы
Откройте
панель Solution Explorer и убедитесь, что
оболочка создала каталог Resources, в который
скопировала отмеченные файлы
Оболочка автоматически присвоила ресурсам подходящие имена,
которые мы можем изменить в панели Properties,
выделяя соответствующие ресурсы. Здесь же мы можем указать,
где физически до компиляции будут располагаться рисунки: в XML -XML -файле
разметки будут помещены только ссылки.

Пока у нас рисунки имеют состояние Linked at ,
что означает, что при компиляции их код будет внедрен в исполнимую
сборку прямо из BMP -файлов по ссылкам, помещенным
в XML -файл Form3.resx. Поэтому
присутствие BMP -файлов в проекте обязательно
при каждой компиляции. При установке для любого рисунка опции оболочка
уже на
Чтобы убедиться в сказанном, выполните следующее:
В панели Solution Explorer вызовите контекстное меню
для узла Form3.resx и
выполните команду View Em, чтобы открыть XML -
Убедитесь,
что в конце XML -файла прописаны только ссылки
на добавленные в проект файлы рисунков<data name="Copy" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Copy.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Cut" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Cut.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
В панели Solution Explorer вызовите контекстное меню
для узла Form3.resx и выполните команду View
Designer, чтобы открыть
Выделите
ресурс Cut и через панель Properties установите
для него свойство Persistence в значение Embedded in
.resx
Вновь
откройте Form3.resx в режиме View
Em и убедитесь, что вместо
ссылки на файл Cut.bmp оболочка включила
в ресурс сам код этого рисунка<data name="Copy" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Copy.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Cut" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
Qk02AwAAAAAAADYAAAAoAAAAEAAAABAAAAABABgAAAAAAAAAAAASCwAAEgsAAAAAAAAAAAAA/wD//wD/
/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/u6GUl19FY0Iy
oo2C/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/zYZjq2I/ilAzYkU3vb2+uZ+SZkM0
Xj8wqJGG/wD//wD//wD//wD//wD//wD//wD/3YNVw7KquJyOhU4zwMDAxYJhqmI+YzwpLyUj/wD//wD/
/wD//wD//wD//wD//wD/4YVXxbOqva6ojFI1vLi21HxSwrKqrpSGa0It/wD//wD//wD//wD//wD//wD/
/wD/2ph25YZWsGZBj1U4nHJdyXVNwbm1up6Qj1U2/wD//wD//wD//wD//wD//wD//wD/yLWs645d24JW
n19AlGBHvnRP1XtPuWtFrYd0/wD//wD//wD//wD//wD//wD//wD//wD/yLau3pl234RWxo1wzHhN0nxR
xpF3vaec/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/iHNmcWBWhmdVq56Z/wD//wD//wD//wD/
/wD//wD//wD//wD//wD//wD//wD/uLSxjoB0fm5jaV1VdnJu/wD//wD//wD//wD//wD//wD//wD//wD/
/wD//wD//wD/urCqopSLqKCZjoF3Y1tV/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/vrGq
oZSLyMbEzcXAa19Wra2u/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/qZuSsqijw8PD6OTifG9m
qKmq/wD//wD//wD//wD//wD//wD//wD//wD//wD/u7i2o5aMvLq5/wD/3dzcvLKrpqGe/wD//wD//wD/
/wD//wD//wD//wD//wD//wD/v7izs6ie/wD//wD/y8vL4t3aoZeQ/wD//wD//wD//wD//wD//wD//wD/
/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/
</value>
</data>
Выделите
ресурс Cut и через панель Properties верните
свойство Persistence в прежнее значение Linked
at compile time Добавим к проекту файл рисунка Paste.bmp и сообщим компилятору о необходимости включать его код в исполнимую сборку.
В панели Solution Explorer вызовите контекстное меню
для папки Resources,
в которой уже должны находиться файлы Cut.bmp и Copy.bmp с
глубиной цвета 24bitcolor, и командой Add/Existing
Item добавьте к проекту из каталога Source файл Paste.bmp с
такой же глубиной цвета, предварительно установив для диалогового
окна соответствующий фильтр
Выделите
файл Paste.bmp и в панели Properties установите для него Build Action = Embedded Resource При такой настройке при компиляции код файла будет копироваться в исполнимую сборку.
Присоединим к свойству Image элемента меню itemDelete файл
с рисунком Delete.bmp через стандартный файл
глобальных ресурсов. Этот
файл может хранить в себе множественные ресурсы, которые доступны
в любом месте
Откройте
файл Form3.cs в режиме View Designer,
выделите через раскрывающийся список панели Properties элемент itemDelete,
раскройте его свойство Image и для режима Project resource file загрузите в элемент
кнопкой Import файл Delete.bmp из
прилагаемого к работе каталога Source
Загруженная пиктограмма немедленно появится слева от команды Delete на меню формы

Откройте
в каталоге Properties проекта стандартный
файл глобальных ресурсов Resources проекта
в режиме View
Em и убедитесь,
что оболочка поместила относительную ссылку на Delete.bmp в
этот файл<data name="Delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Delete.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Откройте
файл Form3.Designer.cs и в секции #region убедитесь, что
оболочка сразу прописала код присоединения рисунка к элементу
меню//
// itemDelete
//
this.itemDelete.Image = global::StripControls.Properties.Resources.Delete;
Никаких дополнительных строк кода для загрузки рисунка указанным способом в дальнейшем не потребуется, оболочка все сделала за нас.
Замечание.
Если пытаться немедленно присоединить к элементу файл рисунка
через локальный ресурс в использованном нами окне Select
,
то оболочка выкинет все ранее присоединенные файлы из этого
ресурса (В нашем случае и Copy )
и оставит только один непосредственно присоединяемый к элементу
Загрузим рисунок Open.bmp в файл пользовательский глобальных ресурсов MyResources.resx, который нужно создать и добавить к проекту в любое место.
В панели Solution Explorer вызовите контекстное меню
для папки Resources и добавьте в нее
командой Add/New
Item новый файл глобальных
ресурсов с именем MyResources.resx
В панели Solution Explorer двойным щелчком
на файле Resources.resx откройте его в режиме View
Designer
Переведите
редактор в режим Images и командой Add Resource/Add Existing File добавьте
к проекту файл Open.bmp глубиной
цвета 24bitcolor из прилагаемого каталога Source точно
также, как мы это делали ранее при загрузке файлов Cut.bmp и Copy.bmp в локальный ресурс Form3.resxОболочка физически скопирует файл Open.bmp в каталог Resources проекта и пропишет относительную ссылку на него в пользовательском XML -файле разметки MyResources.resx глобальных ресурсов
<data name="Open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Open.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Мы точно так же, как для файла локальных ресурсов, можем отредактировать
свойство ресурса и загрузить его физически в глобальный ресурс,
изменив свойство , или оставить
его подгружаемым в исполнимую сборку во время компиляции.
Файл Save.bmp мы будем загружать в элемент itemSaveAs непосредственно во время выполнения программы. Поэтому нужно присоединить его к проекту и настроить свойства так, чтобы оболочка при компиляции копировала этот файл в место размещения исполнимой сборки.
В панели Solution Explorer вызовите контекстное меню
для папки Resources и командой Add/Existing
Item добавьте в проект файл Save.bmp ( 24bitcolor )
из прилагаемого каталога Source
Выделите
скопированный файл Save.bmp и через панель Properties установите свойство Copy
to Output Directory в
значение Copy if newer,
чтобы оболочка скопировала файл в каталог размещения исполнимой
сборкиИтак, на предудущих этапах работы мы загрузили все нужные
файлы с пиктограммами в файлы Image элемента
меню itemDelete, поэтому дальнейших действий
для этого элемента никаких
не требуется. Ресурсы остальных пиктограмм пропишем в коде
и они будут присоединяться к элементам меню на этапе выполнения
программы.
Откройте
файл Form3.cs в режиме View Em и
добавьте в класс Form3 следующую функцию с именем LoadImages() для присоединения рисунков к элементам меню// Загрузка изображений в элементы меню
void LoadImages()
{
// Присоединяем 2 пиктограммы из
// файла локальных ресурсов Form3.resx
ResourceManager resources =
new ResourceManager(typeof(Form3));//или (this.GetType())
itemCopy.Image = (Image)resources.GetObject("Copy");
itemCut.Image = (Image)resources.GetObject("Cut");
// Присоединяем пиктограмму из файла,
// помеченного как Embedded Resource
itemPaste.Image = new Bitmap(this.GetType(), // или (typeof(Form3)
"Resources.Paste.bmp");
///////////////////////////////
// Пиктограмма для Delete внедрена декларативно
// через стандартный файл глобальных ресурсов,
// поэтому для нее ничего не делаем
///////////////////////////////
// Присоединяем пиктограмму из пользовательского
// файла глобальных ресурсов MyResources.resx
itemOpen.Image = Resources.MyResources.Open;
// Присоединяем пиктограмму непосредственно из файла рисунка
itemSaveAs.Image = new Bitmap("Resources\\Save.bmp");
}
Вставьте
в Form3 после
вызова метода InitializeComponent() следующий
код, выполняющий присоединениеpublic Form3()
{
InitializeComponent();
LoadImages();// Присоединяем пиктограммы
}
Запустите Упражнение 3 на выполнение и убедитесь в наличии пиктограмм
для назначенных элементов меню![]() |
![]() |
Обратите внимание на то, что для меню View заготовлен
только узел itemView, а его коллекция DropDownItems пока
пуста, поскольку ранее мы договорились наполнить ее
Меню View будет управлять формой представления рисунков элементом pictureBox, который мы добавили на Form3 ранее. Через общий
обработчик элементов меню View мы будем передавать в pictureBox требуемые значения его свойства SizeMode из библиотечного перечисления PictureBoxSizeMode. Закрепленное за элементом меню значение
перечисления PictureBoxSizeMode будем сохранять в свойстве элемента и извлекать его в обработчике с последующей распаковкой
Object в PictureBoxSizeMode.

Здесь свойство Name - это свойство элемента типа String, а
не имя ссылки с адресом объекта. При настройке элементов меню
по приведенному ниже коду не забудьте начать набор строки регистрации
обработчика события Click для первого элемента DropDownItems[0] вручную
- с применением клавиши Tab. Это позволит создать
обработчик с правильной Click последующих
элементов можно воспользоваться упрощенным синтаксисом.
Добавьте
в класс Form3 функцию с именем CreateView() для
создания и настройки элементов меню View.
Код функции соответствует приведенной выше схеме и представлен
ниже// Функция создания элементов меню View программным способом
void CreateView()
{
// Имя элемента меню на верхнем уровне для возможности
// адресации в коллекции Items по его имени
itemView.Name = "View";
ToolStripMenuItem item=new ToolStripMenuItem("Normal");
item.Tag = PictureBoxSizeMode.Normal;
// Создайте вручную!!!!!!!
item.Click += new EventHandler(ViewOnClick);
itemView.DropDownItems.Add(item);// Добавили в коллекцию View
// Имя элемента меню View для возможности адресации
// в коллекции DropDownItems по его имени
item.Name = "Normal";
// Отмечаем флажок, поскольку в начальном состоянии
// свойство SizeMode элемента PictureBox именно Normal
item.Checked = true;
item = new ToolStripMenuItem();
item.Text = "Center";
item.Tag = PictureBoxSizeMode.CenterImage;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "Center";
item = new ToolStripMenuItem("Stretch");
item.Tag = PictureBoxSizeMode.StretchImage;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "Stretch";
item = new ToolStripMenuItem();
item.Text = "Zoom";
item.Tag = PictureBoxSizeMode.Zoom;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "Zoom";
item = new ToolStripMenuItem("AutoSize");
item.Tag = PictureBoxSizeMode.AutoSize;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "AutoSize";
}
Еще раз вспомним, что порядок следования кода добавления элементов
в коллекцию определяет их порядок прорисовки в работающем приложении.
При создании элементов меню View мы
поочередно используем разные ToolStripMenuItem.
Мы задаем свойству типа Object упакованное
значение перечисления PictureBoxSizeMode, чтобы затем в обработчике
распаковать его. Помечая элемент значением свойства Name мы
перестраховываемся на тот случай, если вдруг далее нам придется
к нему адресоваться в коллекции по синтаксису ассоциативного массива
(
Вставьте
в конец конструктора Form3() одноименного класса код вызова
функции CreateView()public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
LoadImages();// Присоединяем пиктограммы
CreateView();// Динамически заполняем меню View
}
В созданный
обработчик ViewOnClick() внесите следующий кодvoid ViewOnClick(object sender, EventArgs e)
{
// Приводим ссылку на элемент меню View, сгенерировавший событие
ToolStripMenuItem item = (ToolStripMenuItem)sender;
// Распаковываем объект и используем сохраненную
// в нем информацию для управления контейнером рисунков
pictureBox.SizeMode = (PictureBoxSizeMode)item.Tag;
////////////////////////////////////////////////////////
// Управление флажками выполним в обработчике события
// DropDownOpening, которое информирует о раскрытия узла View
////////////////////////////////////////////////////////
//////////// Следующий код закомментирован /////////////
//////////// Жалко выбрасывать!!! /////////////
/*//////////////////////////////////////////////////////
// Сбрасываем флажки всех элементов
foreach (ToolStripMenuItem it in itemView.DropDownItems)
it.Checked = false;
// Устанавливаем флажок на выделенном элементе,
// адресуясь к коллекции по имени элемента
((ToolStripMenuItem)itemView.DropDownItems[item.Name]).Checked = true;
//item.Checked = true; // Альтернативный вариант
//*/////////////////////////////////////////////////////
}
Запустите Упражнение 3 и убедитесь, что команды узла View динамически
формируются
Команды меню (опции) являются неотъемлемой частью интерфейса пользователя в современных программах. Код обработчиков элементов меню реализует основную функциональность приложения и требует пристального внимания разработчика. Заполним наши обработчики команд нужным кодом. Тщательно разберитесь в нем, поскольку он реализует логику программы и во многом может стать для вас (программистов!!!, но не кочегаров и зольщиков) поучительным.
Ранее мы создали заготовки обработчиков для всех значимых элементов меню. Пришла пора их запрограммировать, тем самым обеспечив функциональность программе. Значимые элементы меню недаром называют командами. Именно через них пользователь, незнакомый с тонкостями программирования, управляет компьютером для выполнения своей работы.
Вначале запрограммируем все обработчики элементов меню верхнего уровня, которые срабатывают при раскрытии главных узлов. Главная задача состоит в том, чтобы сделать высплывающие меню контекстно-зависимыми, например, некоторые команды скрыть или показать недоступными для выбора.
Найдите
обработчик FileOnDropDown() события DropDownOpening,
которое генерируется при раскрытии элемента File,
и запрограммируйте его следующим образомprivate void FileOnDropDown(object sender, EventArgs e)
{
// Сделать недоступным команду SaveAs, если контейнер пустой
itemSaveAs.Enabled = pictureBox.Image != null;
}
Найдите
обработчик EditOnDropDown() события DropDownOpening узла Edit и запрограммируйте его такprivate void EditOnDropDown(object sender, EventArgs e)
{
/////////////////////////////////////////////////
// Регулируем доступность команд Copy, Cut, Delete
/////////////////////////////////////////////////
// Нечего редактировать - контейнер пустой
itemCopy.Enabled = itemCut.Enabled = itemDelete.Enabled =
pictureBox.Image != null;
/////////////////////////////////////////////////
// Регулируем доступность команды Paste
/////////////////////////////////////////////////
// Ивлекаем адрес системного буфера обмена,
// используя интерфейсную ссылку базового типа
IDataObject data = Clipboard.GetDataObject();
// Если системный буфер не содержит рисунков
// подходящего типа, то вставлять тоже нечего
itemPaste.Enabled = data.GetDataPresent(typeof(Bitmap))
|| data.GetDataPresent(typeof(System.Drawing.Imaging.Metafile));
}
Найдите
обработчик ViewOnDropDown() для узла View и внесите в него
следующий кодprivate void ViewOnDropDown(object sender, EventArgs e)
{
// При раскрытии узла View перебираем коллекцию
// и управляем флажками с помощью состояния pictureBox
foreach (ToolStripMenuItem item in itemView.DropDownItems)
{
item.Checked =
(PictureBoxSizeMode)item.Tag == pictureBox.SizeMode;
}
}
Приступим к программированию самих команд всплывающих меню.
Найдите
обработчик ExitOnClick() для команды Exit и закодируйте его
такprivate void ExitOnClick(object sender, EventArgs e)
{
this.Close();
}
Найдите
обработчик DeleteOnClick() для команды Delete и внесите в
него следующий кодprivate void DeleteOnClick(object sender, EventArgs e)
{
pictureBox.Image = null;
}
В этом обработчике мы просто обнуляем ссылку на рисунок и
изображение исчезает из контейнера pictureBox.
Найдите
обработчик CopyOnClick() для команды Copy и заполните его
такprivate void CopyOnClick(object sender, EventArgs e)
{
// Заполняем системный буфер обмена рисунком контейнера
// Если при раскрытии узла Edit текущий
// рисунок есть, то команда Copy доступна
Clipboard.SetDataObject(pictureBox.Image, true);
}
Здесь вызывается
Найдите
обработчик CutOnClick() для команды Cut и
заполните его такprivate void CutOnClick(object sender, EventArgs e)
{
////////////////////////////////////////////////
// Используем уже готовые обработчики!!!
////////////////////////////////////////////////
// Копируем в системный буфер обмена
CopyOnClick(sender, e);
// Обнуляем контейнер рисунка
DeleteOnClick(sender, e);
}
Найдите
обработчик PasteOnClick() для команды Paste и заполните его такprivate void PasteOnClick(object sender, EventArgs e)
{
// Подключаемся к системному буферу обмена
IDataObject data = Clipboard.GetDataObject();
// Распознаем тип изображения в буфере обмена и читаем данные
Type typeMeta = typeof(System.Drawing.Imaging.Metafile);
if (data.GetDataPresent(typeMeta))
pictureBox.Image = (Image)data.GetData(typeMeta);
else
pictureBox.Image = (Image)data.GetData(typeof(Bitmap));
}
Доступность команды Paste проверяется в узле Edit и
зависит от типа ожидаемых данных. Только в случае допустимости
типа изображения может сработать обработчик PasteOnClick(),
поэтому-то
мы и рассматриваем в нем только две альтернативы.
Для команд Open и Save As нам
потребуется механизм отображения имени загруженного файла в
заголовке окна наряду с самим заголовком. Для этого нужно объявить
два поля типа String.
Добавьте
в начало класса Form3 два поля, связанные с заголовком окна,
и инициализируйте одно из них в public partial class Form3 : Form
{
String originalTitle; // Поле для хранения чистого заголовка
String name; // Поле для хранения имени загруженного рисунка
public Form3()
{
InitializeComponent();
LoadImages();// Присоединяем пиктограммы
CreateView();// Динамически заполняем меню View
originalTitle = this.Text;// Сохраняем чистый заголовок
}
............................................................
}
В этой команде мы будем использовать стандартный диалог OpenFileDialog,
который должным образом настроим. При загрузке файла в контейнер
рисунка следует использовать защищенный код, перехватывающий
и обрабатывающий возможные системные исключения на случай попытки
загрузить файл формата, который элементом PictureBox не
поддерживается.
Найдите
обработчик OpenOnClick() для команды Open и заполните его так// Обработчик команды Open
OpenFileDialog openDialog; // Объявили ссылку на диалог
bool openFlag = true;// Запускается в первый раз
//
private void OpenOnClick(object sender, EventArgs e)
{
if (openFlag)
{
openFlag = false; // Больше не создавать и не настраивать!
// Создаем и настраиваем диалог
openDialog = new OpenFileDialog();
openDialog.Filter = "All allowable Image Files" + // 1)Заголовок
"|*.bmp;*.ico;*.gif;*.jpeg;*.jpg;*.jfif;*.png;*.tif;*.tiff;*.wmf;*.emf|" + // 1)|Шаблон|
"Windows Bitmap (*.bmp)|*.bmp|" + // 2) Заголовок|Шаблон|
"Graphics Interchange Format (*.gif)|*.gif|" + // 3) Заголовок|Шаблон|
"JPEG File Interchange Format (*.jpg)|*.jpeg;*.jpg;*.jfif|" + // 4) Заголовок|Шаблон|
"Portable Network Graphics (*.png)|*.png|" + // 5) Заголовок|Шаблон|
"Tagged Image File Format (*.tif)|*.tif;*.tiff|" + // 6) Заголовок|Шаблон|
"Windows Metafile (*.wmf)|*.wmf|" + // 7) Заголовок|Шаблон|
"Enhanced Metafile (*emf)|*.emf|" + // 8) Заголовок|Шаблон|
"All Files (*.*)|*.*"; // 9) Заголовок|Шаблон
// Устанавливаем последний шаблон активным (по умолчанию 1!)
String[] str = openDialog.Filter.Split('|');
openDialog.FilterIndex = str.Length / 2;
}
// Защищенный код для перехвата системных исключений на
// случай попытки загрузки неподдерживаемого формата данных
if (openDialog.ShowDialog() == DialogResult.OK)
try
{
pictureBox.Image = Image.FromFile(openDialog.FileName);
// Выводим имя рисунка в заголовок окна
name = openDialog.FileName;
String[] str = name.Split('\\');
name = str[str.Length - 1].ToLower();
this.Text = "(" + name + ") " + originalTitle;
}
catch (Exception ext)
{
MessageBox.Show(ext.Message, this.Text,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
В приведенном коде ссылка на диалог объявлена как поле, чтобы
он создавался только один раз и сохранял предыдущие настройки
пользователя при повторных обращениях. Фильтр диалога мы тоже
настраиваем только раз и для тренировки устанавливаем его на
последний шаблон (первый шаблон устанавливается по умолчанию
и
В этой команде мы будем использовать стандартный диалог SaveFileDialog,
который должным образом настроим. При выгрузке файла
рисунка на диск также, на случай
Найдите
обработчик SaveAsOnClick() для команды Save As и
заполните его так// Обработчик команды Save As
SaveFileDialog saveDialog; // Объявили ссылку на диалог
System.Drawing.Imaging.ImageFormat[] imageFormat;// Объявили ссылку на массив
bool saveFlag = true;// Запускается в первый раз
//
private void SaveAsOnClick(object sender, EventArgs e)
{
if (saveFlag)
{
saveFlag = false; // Больше не создавать и не настраивать!
// Создаем диалог
saveDialog = new SaveFileDialog();
// Настраиваем фильтр
saveDialog.Filter = "Windows Bitmap (*.bmp)|*.bmp|" +
"Graphics Interchange Format (*.gif)|*.gif|" +
"JPEG File Interchange Format (*.jpg)|*.jpeg;*.jpg;*.jfif|" +
"Portable Network Graphics (*.png)|*.png|" +
"Tagged Image File Format (*.tif)|*.tif;*.tiff";
// Инициализируем массив допустимыми форматами рисунков
imageFormat = new System.Drawing.Imaging.ImageFormat[] {
System.Drawing.Imaging.ImageFormat.Bmp,
System.Drawing.Imaging.ImageFormat.Gif,
System.Drawing.Imaging.ImageFormat.Jpeg,
System.Drawing.Imaging.ImageFormat.Png,
System.Drawing.Imaging.ImageFormat.Tiff
};
}
// Заполняем поле именем загруженного рисунка без расширения
saveDialog.FileName = name.Substring(0, name.LastIndexOf('.'));
// Защищенный код для перехвата системных
// исключений на случай сбоя записи
if (saveDialog.ShowDialog() == DialogResult.OK)
try
{
// Пытаемся сохранить в формате текущего фильтра
pictureBox.Image.Save(saveDialog.FileName,
imageFormat[saveDialog.FilterIndex - 1]);
// Обновляем контейнер новым форматом - ???????????????
//pictureBox.Image = Image.FromFile(saveDialog.FileName);
// Выводим в заголовок окна имя файла с новым расширением
name = saveDialog.FileName;
String[] str = name.Split('\\');
name = str[str.Length - 1].ToLower();
this.Text = "(" + name + ") " + originalTitle;
}
catch (Exception ext)
{
MessageBox.Show(ext.Message, this.Text,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
На этом выполнение Упражнения 3 завершено.
Source, или любых других.
Запустите
приложение и испытайте его работу. Результат должен получиться
примерно такимВ этом упражнении мы создадим заготовку меню с пиктограммами,
которые будем присоединять к элементам меню из списка изображений.
Сам список с изображениями можно заполнять из хранилища
ресурсов, которое находится внутри
файла исполнимой сборки нашего приложения. В свою очередь,
хранилище ресурсов сборки заполняется при компиляции сборки
из указанного XML -файла,
который нужно сформировать заранее и поместить в него ссылки
на необходимые текстовые или двоичные ресурсы. XML -
или двоичный файл можно создать декларативно (вручную) или
программно, написав соответствующую консольную утилиту.
Таким образом, план действий будет таким:
В пространстве имен System. двух
библиотечных сборок mscorlib.dll и System.Windows.Forms.dll находятся
все необходимые типы, которые обеспечивают программную работу
с ресурсами. Необходимые для работы приложения ресурсы находятся
в готовой исполнимой сборке в специальной секции, называемой
хранилищем ресурсов, или отдельно в двоичном файле. В хранилище
ресурсов они попадают при компиляции приложения из предварительно
подготовленного

Разработаем утилиту, которая будет создавать файл
ресурсов и наполнять его нужными объектами. Для
разнообразия в
Добавьте
к текущему решению командой File/Add/New
Project еще один проект с именем UtilityResource типа

Добавьте
к проекту UtilityResource ссылки на сборки System.Windows.Forms и System.Drawing.
Для этого выделите в панели Solution Explorer узел UtilityResource и
выполните команду Project/Add Reference
В панели Solution Explorer скопируйте файлы Cut.bmp, Copy.bmp, Paste.bmp, Delete.bmp, Open.bmp, Save.bmp из
узла Resources проекта StripControls в
корневой узел проекта UtilityResource (или
добавьте напрямую из прилагаемой папки Source,
как показано ниже для иконки приложения)
В панели Solution Explorer вызовите контекстное меню
для корневого узла UtilityResource и выполните
команду Add/Existing Item. В прилагаемом
каталоге Source выберите иконку, например 1.ico, и добавьте ее в проект UtilityResource
Выделите
все добавленные к проекту UtilityResource файлы
изображений и в панели Properties определите
для них опции Copy to Output Directory=Copy always, Build Action =None, чтобы при исполнении
проекта оболочка копировала их в каталог размещения сборки
using System;
namespace UtilityResource
{
using System.Resources;
using System.Drawing;
class Program
{
static void Main(string[] args)
{
// Создаем объект для записи в двоичный файл ресурсов
ResourceWriter rw = new ResourceWriter("ResXForm4.resources");
// Добавляем в файл описание ресурсов 'Ключ-Значение' (Key-Value)
// Ресурсы должны находиться в одном каталоге с утилитой!!!
rw.AddResource("Cut", new Bitmap("Cut.bmp"));
rw.AddResource("Copy", new Bitmap("Copy.bmp"));
rw.AddResource("Paste", new Bitmap("Paste.bmp"));
rw.AddResource("Delete", new Bitmap("Delete.bmp"));
rw.AddResource("Open", new Bitmap("Open.bmp"));
rw.AddResource("Save", new Bitmap("Save.bmp"));
rw.AddResource("strTitle", "Список изображений");
rw.AddResource("icoTitle", new Icon("1.ico"));
// Сохраняем файл с добавленными ресурсами и закрываем его
rw.Generate();
rw.Close();
}
}
}
В панели Solution Explorer вызовите контекстное меню
для узла UtilityResource и
выполните команду Set as StartUp Project,
чтобы сделать проект стартовым (либо просто щелкните левой
кнопкой мыши на узле UtilityResource ).
Корневой узел
Выполните
проект UtilityResource, чтобы создать файл
ресурсов с именем ResXForm4.resources программным
способом
В панели Solution Explorer вызовите контекстное меню для узла проекта UtilityResource и
исключите его из решения командой Remove
В панели Solution Explorer вызовите контекстное меню
для корневого узла проекта StripControls и
командой Add/Existing
Item добавьте
созданный ранее
Настройте
через панель Properties добавленный к
проекту StripControls файл
ресурсов ResXForm4.resources так, чтобы
он стал копируемым при компиляции проекта в каталог размещения
сборки, т.е. установите опции Copy to Output Directory=Copy always, Build Action =NoneНа этом подготовительная часть по формированию
Наша главная задача в упражнении 4 - посмотреть способ загрузки
пиктограмм в меню из
Добавьте
к проекту StripControls форму с именем Form4 командой Project/Add Windows Form
Поместите
из панели Toolbox на форму кнопку Button (или
любой другой элемент управления) и сразу удалите ее, чтобы
явно прописалось конструктором оболочки свойство Name=Form4 формы
в файле Form4.Designer.cs (или сами пропишите его в этом
файле)
Откройте
файл Start.cs в режиме View Designer,
выделите объект списка listBoxChoice и добавьте в его свойство Items строку:
"Упражнение 4."
Дополните
файл Start.cs блоком кода, запускающим созданную форму Form4// Если форма не запущена - запускаем
if (!IsRun)
{
switch (formName)
{
case "Form1":
frm = new Form1();
break;
case "Form2":
frm = new Form2();
break;
case "Form3":
frm = new Form3();
break;
case "Form4":
frm = new Form4();
break;
}
this.AddOwnedForm(frm); // Сделать новую frm подчиненной Start
// frm.Owner = this; // Алтернативный способ назначить владельца
frm.ShowInTaskbar = false; // Не отображать метку окна в панели задач
frm.Show(); // Показать новую форму
}
else
frm.Focus(); // Передать фокус запущенной форме
Откройте
файл Form4.cs в режиме View Em и
добавьте в него функцию CreateMenu() программного
создания меню, которая будет иметь следующий код// Видимые в методах класса поля-ссылки на элементы меню
MenuStrip menu;
ToolStripMenuItem itemFile;
ToolStripMenuItem itemOpen;
ToolStripMenuItem itemSaveAs;
ToolStripMenuItem itemExit;
ToolStripMenuItem itemEdit;
ToolStripMenuItem itemCut;
ToolStripMenuItem itemCopy;
ToolStripMenuItem itemPaste;
ToolStripMenuItem itemDelete;
void CreateMenu()
{
// Создание меню
menu = new MenuStrip();
menu.Parent = this;
// Формирование верхней линейки меню
itemFile = new ToolStripMenuItem("File");
menu.Items.Add(itemFile);
itemEdit = new ToolStripMenuItem("Edit");
menu.Items.Add(itemEdit);
// Сборка узла File
itemFile.DropDownItems.Add(itemOpen = new ToolStripMenuItem("Open"));
itemFile.DropDownItems.Add(itemSaveAs = new ToolStripMenuItem("Save As..."));
itemFile.DropDownItems.Add(new ToolStripSeparator());
itemFile.DropDownItems.Add(itemExit = new ToolStripMenuItem("Exit"));
// Сборка узла Edit
itemEdit.DropDownItems.Add(itemCut = new ToolStripMenuItem("Cut"));
itemEdit.DropDownItems.Add(itemCopy = new ToolStripMenuItem("Copy"));
itemEdit.DropDownItems.Add(itemPaste = new ToolStripMenuItem("Paste"));
itemEdit.DropDownItems.Add(itemDelete = new ToolStripMenuItem("Delete"));
}
Добавьте
в файл Form4.cs функцию LoadImage() загрузки
пиктограмм меню из // Создаем список для хранения изображений
ImageList imageList = new ImageList();
void LoadImage()
{
// Присоединяем ранее сформированные ресурсы из файла ресурсов
ResourceReader rr = new ResourceReader("ResXForm4.resources");
IDictionaryEnumerator en = rr.GetEnumerator();
while (en.MoveNext())
{
switch (en.Key.ToString())
{
default:
imageList.Images.Add(en.Key.ToString(), (Image)en.Value);
break;
case "icoTitle":
this.Icon = (Icon)en.Value; // Иконка окна
break;
case "strTitle":
this.Text = (String)en.Value; // Заголовок окна
break;
}
}
rr.Close(); // Закрыли файл ресурсов
// Определяем цвет, имитирующий прозрачность окраин пиктограмм
imageList.TransparentColor = Color.Magenta;
// Закрепляем пиктограммы из списка за командами меню с помощью ключей
itemOpen.Image = imageList.Images["Open"];
itemSaveAs.Image = imageList.Images["Save"];
itemCut.Image = imageList.Images["Cut"];
itemCopy.Image = imageList.Images["Copy"];
itemPaste.Image = imageList.Images["Paste"];
itemDelete.Image = imageList.Images["Delete"];
}
Вставьте
вызовы функций создания меню и загрузки изображений в конструктор
класса Form4 и добавьте инструкции подключения
библиотечных пространств именОбщий код файла Form4.cs будет таким
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace StripControls
{
using System.Resources;
using System.Collections;
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
CreateMenu();// Создаем меню
// Загружаем в меню пиктограммы опций
LoadImage();
}
// Видимые в методах поля-ссылки на элементы меню
MenuStrip menu;
ToolStripMenuItem itemFile;
ToolStripMenuItem itemOpen;
ToolStripMenuItem itemSaveAs;
ToolStripMenuItem itemExit;
ToolStripMenuItem itemEdit;
ToolStripMenuItem itemCut;
ToolStripMenuItem itemCopy;
ToolStripMenuItem itemPaste;
ToolStripMenuItem itemDelete;
void CreateMenu()
{
// Создание меню
menu = new MenuStrip();
menu.Parent = this;
// Формирование верхней линейки меню
itemFile = new ToolStripMenuItem("File");
menu.Items.Add(itemFile);
itemEdit = new ToolStripMenuItem("Edit");
menu.Items.Add(itemEdit);
// Сборка узла File
itemFile.DropDownItems.Add(itemOpen = new ToolStripMenuItem("Open"));
itemFile.DropDownItems.Add(itemSaveAs = new ToolStripMenuItem("Save As..."));
itemFile.DropDownItems.Add(new ToolStripSeparator());
itemFile.DropDownItems.Add(itemExit = new ToolStripMenuItem("Exit"));
// Сборка узла Edit
itemEdit.DropDownItems.Add(itemCut = new ToolStripMenuItem("Cut"));
itemEdit.DropDownItems.Add(itemCopy = new ToolStripMenuItem("Copy"));
itemEdit.DropDownItems.Add(itemPaste = new ToolStripMenuItem("Paste"));
itemEdit.DropDownItems.Add(itemDelete = new ToolStripMenuItem("Delete"));
}
// Создаем список для хранения изображений
ImageList imageList = new ImageList();
void LoadImage()
{
// Присоединяем ранее сформированные ресурсы из файла ресурсов
ResourceReader rr = new ResourceReader("ResXForm4.resources");
IDictionaryEnumerator en = rr.GetEnumerator();
while (en.MoveNext())
{
switch (en.Key.ToString())
{
default:
imageList.Images.Add(en.Key.ToString(), (Image)en.Value);
break;
case "icoTitle":
this.Icon = (Icon)en.Value; // Иконка окна
break;
case "strTitle":
this.Text = (String)en.Value; // Заголовок окна
break;
}
}
rr.Close(); // Закрыли файл ресурсов
// Определяем цвет фона, воспринимаемый прозрачным
imageList.TransparentColor = Color.Magenta;
// Закрепляем пиктограммы из списка за командами меню с помощью ключей
itemOpen.Image = imageList.Images["Open"];
itemSaveAs.Image = imageList.Images["Save"];
itemCut.Image = imageList.Images["Cut"];
itemCopy.Image = imageList.Images["Copy"];
itemPaste.Image = imageList.Images["Paste"];
itemDelete.Image = imageList.Images["Delete"];
}
}
}
Запустите
на выполнение Упражнение 4 и убедитесь, что пиктограммы нормально
загружаются из 
Контекстное меню вызывается по щелчку правой кнопки мыши на
соответствующем элементе управления и зависит от конкретного
состояния этого элемента. Контекстное меню представлено классом System.Windows.Forms.ContextMenuStrip. Любой
визуальный элемент управления, производный от класса Control,
наследует общедоступное виртуальное свойство ContextMenuStrip,
к которому можно привязать объект контекстного меню.
Элементами контекстного меню ContextMenuStrip,
как и в случае с основным меню, порожденным классом MenuStrip,
являются объекты класса ToolStripMenuItem.
Эти объекты добавляются в коллекцию Items,
являющуюся ContextMenuStrip.
Каждый из объектов-элементов контекстного меню имеет свойство-коллекцию DropDownItems,
в которую помещают элементы вложенного меню.
Мы можем заготовить несколько различных объектов контекстного меню и привязать каждый из них к определенному элементу управления. Но эти же несколько объектов контекстного меню можно поочередно привязывать к одному и тому же элементу управления в динамическом режиме в зависимости от текущего состояния контекста.
Рассмотрим Упражнение 5, в котором будем использовать объекты
контекстного меню. Мы создадим несколько контекстных меню,
часть динамическим способом, часть декларативным способом.
Объекты контекстных меню будем присоединять к различным элементам
управления через свойство этих элементов ContextMenuStrip.
В панели Solution
Explorer выделите корневой узел проекта StripControls и
командой меню оболочки Project/Add Windows Form добавьте
к проекту файл Form5.cs
Откройте
файл Start.cs в режиме View Designer,
выделите объект listBoxChoice и добавьте
через панель Properties в свойство Items ссылку
на Упражнение 5
Откройте
файл Start.cs в режиме View Em,
найдите обработчик listBoxChoice_SelectedIndexChanged() и
добавьте в него код запуска формы Form5Form frm; // Объявили поле
private void listBoxChoice_SelectedIndexChanged(object sender, EventArgs e)
{
// Привязали номер упражнения к индексу списка
ListBox list = (ListBox)sender;
string formName = "Form" + (list.SelectedIndex + 1);
///////////////////////////////////////////////////
// Следующий код взят из лабораторной работы №47
///////////////////////////////////////////////////
// Читаем коллекцию запущенных форм
FormCollection fc = Application.OpenForms;
bool IsRun = false;
// Перебираем коллекцию
foreach (Form form in fc)
{
// Имя очередной запущенной формы сравниваем с закрепленным за кнопкой
if (form.Name == formName)
{
IsRun = true; // Нашли, что форма запущена, поднимаем флаг
frm = form; // Сохраняем ссылку на запущенную форму для фокуса
break; // Выходим из цикла
}
}
// Если форма не запущена - запускаем
if (!IsRun)
{
switch (formName)
{
case "Form1":
frm = new Form1();
break;
case "Form2":
frm = new Form2();
break;
case "Form3":
frm = new Form3();
break;
case "Form4":
frm = new Form4();
break;
case "Form5":
frm = new Form5();
break;
}
this.AddOwnedForm(frm); // Сделать новую frm подчиненной Start
// frm.Owner = this; // Алтернативный способ назначить владельца
frm.ShowInTaskbar = false; // Не отображать метку окна в панели задач
frm.Show(); // Показать новую форму
}
else
frm.Focus(); // Передать фокус запущенной форме
}
Поместите
на форму Form5 компонент SplitContainer,
присвойте его экземпляру имя splitContainer и
установите свойства: Orientation = Horizontal , BorderStyle =Fixed3D, Dock =Fill
Откройте
файл Form5.cs в режиме View Code и
откорректируйте код, чтобы он стал такимusing System;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace StripControls
{
public partial class Form5 : Form
{
// Поле для сохранения выбранного элемента меню
ToolStripMenuItem itemChecked = new ToolStripMenuItem();
// Конструктор
public Form5()
{
InitializeComponent();
splitContainer.Panel1.BackColor = Color.White;// Начальный цвет верхней панели
// Разделение цветов на алфавитные группы, представленные подменю
// A, B, C, D, E, F, G, H, I, J, K, L, M,
int[] iMenu ={ 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 4,
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6 };
// N, O, P, Q, R, S, T, U, V, W, X, Y, Z
// Создание контекстного меню и присоединение к объекту формы
System.Windows.Forms.ContextMenuStrip menu =
new System.Windows.Forms.ContextMenuStrip();
// Будет вызываться для верхней панели
splitContainer.Panel1.ContextMenuStrip = menu;
// Формируем пункты подменю в контекстном меню
for (int i = 0; i <= 6; i++)
{
// Индекс первого вхождения образца соответствует коду ASCII для буквы
char first = Convert.ToChar(Array.IndexOf(iMenu, i) + 'A'); // Позиция в массиве
// Индекс последнего вхождения образца соответствует ASCII латинской буквы
char last = Convert.ToChar(Array.LastIndexOf(iMenu, i) + 'A');// Позиция в массиве
// Создали, настроили и добавили в коллекцию элемент меню
ToolStripMenuItem item = new ToolStripMenuItem();
item.Text = String.Format("Цвета от {0} до {1}", first, last);
menu.Items.Add(item);
// Включили в подменю столбец с флажками в
// дополнение к столбцу с изображениями
((ToolStripDropDownMenu)item.DropDown).ShowCheckMargin = true;
}
// Применяем рефлексию к структуре, представляющей цвета
PropertyInfo[] api = typeof(Color).GetProperties();
// Перебор цветов
foreach (PropertyInfo pi in api)
{
if (pi.CanRead pi.PropertyType == typeof(Color)
pi.Name!="Transparent")
{
// Извлекаем очередной цвет
Color color = (Color)pi.GetValue(null, null);
// Создаем очередной элемент подменю
ToolStripMenuItem item = new ToolStripMenuItem();
// По букве цвета определяем подменю, в котором разместим цвет
int i = iMenu[color.Name[0] - 'A'];// Любой букве - своя позиция
((ToolStripMenuItem)menu.Items[i]).DropDownItems.Add(item);
// Продолжаем настраивать команду
item.Text = InsertSpaceOut(color.Name);// Пробелы перед заглавными буквами цвета
item.Name = color.Name;// Сохраняем название цвета для обработчика ColorOnClick
item.Image = CreateBitmap(color);// Тонкой ссылке присваиваем толстый объект
item.Click += new EventHandler(ColorOnClick);// Регистрация обработчика
if (color.Equals(this.BackColor))// Отметить флажком текущий цвет и сохранить ссылку
(itemChecked = item).Checked = true;
}
}
}
void ColorOnClick(object sender, EventArgs e)
{
// Для выбранного устанавливаем новый флажок
// и меняем цвет клиентской области формы
ToolStripMenuItem item = (ToolStripMenuItem)sender;
itemChecked.Checked = false;// Сбрасываем старое
(itemChecked = item).Checked = true;// Устанавливаем флажок и запоминаем элемент
splitContainer.Panel1.BackColor = Color.FromName(item.Name);// Красим верхнюю панель
}
Bitmap CreateBitmap(Color color)
{
// Генерируем изображение в виде цветного прямоугольника
Bitmap bm = new Bitmap(16, 16);
Graphics gr = Graphics.FromImage(bm);
gr.FillRectangle(new SolidBrush(color), 0, 0, 16, 16);
gr.Dispose(); // Освобождаем контекст устройства
return bm;
}
string InsertSpaceOut(string str)
{
// Вставляем пробелы перед заглавными буквами названия цвета
for (int i = str.Length - 1; i > 1; i--)
if (Char.IsUpper(str[i]))
str = str.Insert(i, " ");
return str;
}
}
}
Запустите
приложение и для верхней панели сплиттера испытайте работу
контекстного менюДолжно получиться нечто, похожее показанному на рисунке с изменением выбранного цвета фона на верхней панели сплиттера

Теперь посмотрим, как присоединяются поочередно контекстные меню к одному и тому же элементу управления в зависимости от некоторых условий.
Поместите
на нижнюю панель объекта splitContainer еще
один экземпляр компонента SplitContainer и
оставьте его имя по умолчанию splitContainer1
Установите
настройки объекта splitContainer1: Dock =Fill , BorderStyle =Fixed3D
Поместите
на левую нижнюю панель splitContainer1.Panel1 три
экземпляра компонента радиокнопки RadioButton с
именами radioButton1, radioButton2, radioButton3 и
измените их свойство Text на ContextMenu1, ContextMenu2, ContextMenu1
Установите
для правой нижней панели splitContainer1.Panel2 свойство BackColor=LimeПосле проведения указанных действий интерфейс формы Form5 должен
стать таким

Создайте
три экземпляра компонента ContextMenuStrip с
именами по умолчанию: contextMenuStrip1, contextMenuStrip2, contextMenuStrip3
Последовательно
выделите каждый объект из только что добавленных контекстных
меню и создайте в нем декларативно заготовки элементов MenuItem
В результате должны получиться три объекта-заготовки контекстных меню со следующим интерфейсом
contextMenuStrip1 |
contextMenuStrip2 |
contextMenuStrip3 |
![]() |
![]() |
![]() |
Выделите
на форме Form5 все три радиокнопки одновременно
и через панель Properties в режиме Events для
события CheckedChanged создайте обработчик
с именем radioButton_CheckedChanged(),
который заполните следующим кодомprivate void radioButton_CheckedChanged(object sender, EventArgs e)
{
if (radioButton1.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip1;
else if (radioButton2.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip2;
else
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip3;
}
При смене активной радиокнопки в обработчике происходит присоединение
к правой нижней панели соответствующего объекта контекстного
меню. Вызов контекстного меню выполняется щелчком правой кнопки
мыши на правой нижней панели, представленной элементом splitContainer1.Panel2.
Для обеспечения подобного механизма работы при первом вызове
контекстного меню необходимо задействовать для этого элемента
событие BindingContextChanged.
Выделите
элемент splitContainer1.Panel2 и через панель Properties в
режиме Events создайте для события BindingContextChanged обработчик splitContainer1_Panel2_BindingContextChanged(),
который заполните такprivate void splitContainer1_Panel2_BindingContextChanged(object sender, EventArgs e)
{
if (radioButton1.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip1;
else if (radioButton2.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip2;
else
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip3;
}
Запустите
приложение и убедитесь в работоспособности всех 4-х контекстных
меню, первое из которых создано динамически, а три остальных
- декларативно
Файлы к лабораторной работе Вы можете скачать здесь.
В библиотеке .NET Framework 2.0 появились
три новых класса для организации пользовательского интерфейса
управления приложением ( -
полоса):
System.Windows.Forms.MenuStrip - главное
менюSystem.Windows.Forms.ToolStrip - панель инструментовSystem.Windows.Forms.StatusStrip - панель состоянияНаряду с этими новыми элементами существуют и поддерживаются
прежние аналогичные классы, существующие
в версиях .NET Framework 1.x:
System.Windows.Forms.MainMenu System.Windows.Forms.ToolBar System.Windows.Forms.StatusBar По умолчанию эти старые классы не подключены к панели Toolbox,
поэтому не видны в ней, хотя и находятся в GAC (

Обе группы классов, старые и новые, обладают почти равной функциональностью, но стиль редактирования в режиме дизайнера и возможности несколько отличаются, как и наименования некоторых свойств и методов. Кроме того, новые классы являются более совершенными. Они поддерживают не только кнопки с растрами, но и текстовые поля, списки и выпадающие меню.
В данной работе мы будем рассматривать вопросы, касающиеся способов построения различных видов меню. Для демонстрации упражнений с элементами меню создадим заготовку приложения со стартовой формой.
Мы еще не знаем, сколько упражнений получится в этой работе,
поэтому применим список ListBox, в который
можно добавлять сколько угодно опций вызова упражнений. При
этом будем использовать рассмотренный ранее прием предотвращения
запуска уже запущенной формы.
Выполните
команду File/New/Project и заполните мастер создания приложения
в соответствии с рисунком
Через
панель Solution Explorer переименуйте файл Form1.cs в Start.cs,
а заголовку окна формы присвойте значение Text =" Lab48
- Имя студента "
Поместите
на форму библиотечный компонент ListBox и
настройте его в соответствии с таблицей свойств| Свойство | Значение |
|---|---|
Name |
listBoxChoice |
Items |
Упражнение 1. |
| Упражнение 2. | |
| Упражнение 3. | |
|
Через
панель Properties создайте обработчик для
события SelectedIndexChanged объекта listBoxChoice,
который заполните такForm frm; // Объявили поле
private void listBoxChoice_SelectedIndexChanged(object sender, EventArgs e)
{
// Привязали номер упражнения к индексу списка
ListBox list = (ListBox)sender;
string formName = "Form" + (list.SelectedIndex + 1);
///////////////////////////////////////////////////
// Следующий код взят из лабораторной работы №47
///////////////////////////////////////////////////
// Читаем коллекцию запущенных форм
FormCollection fc = Application.OpenForms;
bool IsRun = false;
// Перебираем коллекцию
foreach (Form form in fc)
{
// Имя очередной запущенной формы сравниваем с закрепленным за кнопкой
if (form.Name == formName)
{
IsRun = true; // Нашли, что форма запущена, поднимаем флаг
frm = form; // Сохраняем ссылку на запущенную форму для фокуса
break; // Выходим из цикла
}
}
// Если форма не запущена - запускаем
if (!IsRun)
{
switch (formName)
{
case "Form1":
frm = new Form1();
break;
case "Form2":
frm = new Form2();
break;
case "Form3":
frm = new Form3();
break;
}
this.AddOwnedForm(frm); // Сделать новую frm подчиненной Start
// frm.Owner = this; // Алтернативный способ назначить владельца
frm.ShowInTaskbar = false; // Не отображать метку окна в панели задач
frm.Show(); // Показать новую форму
}
else
frm.Focus(); // Передать фокус запущенной форме
}
Недостаток данного кода в том, что мы привязали вызов формы упражнения к индексу списка.
Командой
меню оболочки Project/Add Windows Form добавьте
в проект еще три формы с именами Form1, Form2, Form3
Запустите
приложение и убедитесь в работоспособности механизма вызова
отдельных упражненийРассмотрим простую программу, имитирующую работу меню.
Откройте
файл Form1.cs в режиме View Code и добавьте к конструктору
класса код создания простого менюpublic Form1()
{
InitializeComponent();
this.Text = "Простое меню";
// Главное меню
MenuStrip menu = new MenuStrip();
this.Controls.Add(menu);
// Раздел File
ToolStripMenuItem itemFile = new ToolStripMenuItem();
itemFile.Text = "File";
menu.Items.Add(itemFile);// Элемент верхнего уровня
// Раздел Help
ToolStripMenuItem itemHelp = new ToolStripMenuItem();
itemHelp.Text = "Help";
menu.Items.Add(itemHelp);
// Заполнение раздела File
// Команда Open...
ToolStripMenuItem itemOpen = new ToolStripMenuItem();
itemOpen.Text = "Open...";
itemOpen.ShortcutKeys = Keys.Control | Keys.O; // Ctrl+O
itemOpen.ShowShortcutKeys = true;// Отображать в команде
// Регистрировать вручную!!!!!!!!!!!!!!
itemOpen.Click += new EventHandler(itemOpen_Click);
itemFile.DropDownItems.Add(itemOpen);
// itemOpen.ShowShortcutKeys = false;// Скрыть комбинацию
// Разделитель
ToolStripSeparator itemSep = new ToolStripSeparator();
itemFile.DropDownItems.Add(itemSep);
// Команда Exit
ToolStripMenuItem itemExit = new ToolStripMenuItem();
itemExit.Text = "Exit";
// Регистрировать вручную!!!!!!!!!!!!!!
itemExit.Click += new EventHandler(itemExit_Click);
itemFile.DropDownItems.Add(itemExit);
// Заполнение раздела Help
// Команда About...
ToolStripMenuItem itemAbout = new ToolStripMenuItem();
itemAbout.Text = "About...";
itemAbout.ShortcutKeys = Keys.Control | Keys.A; // Ctrl+A
itemAbout.ShortcutKeyDisplayString = "Ctrl^A";// Свое отображение
itemAbout.ShowShortcutKeys = true;// Отображать в команде
// Регистрировать вручную!!!!!!!!!!!!!!
itemAbout.Click += new EventHandler(itemAbout_Click);
itemHelp.DropDownItems.Add(itemAbout);
}
Перепишите
выделенные строки кода регистрации обработчиков события Click
вручную, чтобы правильно создать заготовки обработчиков,
и заполните обработчики следующим кодомvoid itemAbout_Click(object sender, EventArgs e)
{
MessageBox.Show("Заготовка опции \"About...\"", this.Text);
}
void itemExit_Click(object sender, EventArgs e)
{
this.Close();
}
void itemOpen_Click(object sender, EventArgs e)
{
MessageBox.Show("Заготовка опции \"Open\"", this.Text);
}
}
В программе мы создаем объект меню, как экземпляр класса MenuStrip,
и привязали его к объекту формы .
У класса MenuStrip есть свойство Items,
которое является коллекцией элементов меню верхнего уровня.
Мы создаем два таких объекта itemFile и itemHelp типа ToolStripMenuItem и
добавляем их в свойство Items. У каждого объекта
типа ToolStripMenuItem есть
свойство DropDownItems, готовое хранить коллекцию
команд и разделителей, в том числе вложенных для построения
меню сложной иерархии. В эти свойства элементов верхнего уровня
мы добавляем объекты команд.
Схематично такая процедура построения меню примера изображена на рисунке

Символ амперсанда в значении свойства Text наименования
команды ассоциируется со знаком подчеркивания следующей за
ним буквы и позволяет после нажатия клавиши Alt (не
удерживая) вызывать команды простым нажатием соответствующих
клавиш. Знак подчеркивания в работающей
программе появляется только после щелчка на клавише Alt.
Свойство ShortcutKeys объекта команды ожидает
побитовую комбинацию элементов перечисления System.Windows.Forms. Keys,
задающую клавиши быстрого вызова этой команды. Логическое свойство ShowShortcutKeys объекта команды включает
или выключает отображение заданной комбинации быстрого вызова
справа от команды. По умолчанию задано значение true и
комбинация клавиш отображается в том случае, если она назначена
в свойстве ShortcutKeys. Свойство ShortcutKeyDisplayString позволяет
задать иную строку отображения установленной комбинации, чем
принято по умолчанию (например, вместо Ctrl+A по умолчанию можно задать Ctrl^A ).
Событие Click объекта
команды возникает при выборе соответствующей команды и вызывает
выполнение закрепленного обработчика.
Следует иметь ввиду, что как в коллекции Items меню
верхнего уровня, так и в коллекциях DropDownItems выпадающих
меню элементы отображаются в порядке их добавления в коллекцию.
Свойства Items и DropDownItems являются
экземплярами класса ToolStripItemCollection,
который имеет метод Add(). Именно этим методом
мы добавляем в ToolStripMenuItem.
Однако можно
использовать и метод коллекции AddRange(),
который ожидает ссылку на массив объектов команд. Так, например,
пусть объекты itemFile, itemOpen, itemSep, itemExit уже
созданы. Тогда к объекту верхнего уровня можно присоединить
сразу все подчиненные объекты команд такой строкой кода:
itemFile.DropDownItems.AddRange(new ToolStripMenuItem[]{
itemOpen, itemSep, itemExit});
Add() позволяют добавить команду
меню вместе с текстом, текстом и изображением, текстом + изображением
+ именем обработчика события Click.
В коде упражнения есть один недостаток. Ссылки на команды
меню объявляются внутри конструктора как
В современных меню слева от команд находится столбец, в котором могут отображаться изображения или флажки. В этом упражнении рассмотрим управление флажками.
Появление флажка слева от команды включается булевым свойством Checked объекта команды. В качестве альтернативы
можно установить свойство CheckState в одно из значений
одноименного перечисления. Например для объекта itemOpen это
можно определить так
itemOpen.CheckState = CheckState.Indeterminate ;itemOpen.CheckState = CheckState.Checked ;itemOpen.CheckState = CheckState.Unchecked ;Опция позволяет отобразить неопределенное состояние,
когда на месте флажка помещается точка.
Булево свойство CheckOnClick делает флажок
самоуправляемым, когда его состояние меняется по щелчку автоматически.
Но если группа флажков используется как радиокнопки для выбора
одного из нескольких взаимоисключающих элементов, то управление
их состоянием нужно организовывать вручную.
В этом упражнении мы создадим меню Font, которое будет выводить
список шрифтов, поддерживаемых компьютером. Обычно у современных
операционных систем этот список большой и не помещается на
экране, поэтому будем использовать объект класса ToolStripDropDownMenu.
Этот класс отвечает за появление стрелок, позволяющих перемещаться
по меню, не вмещающему команды. Здесь мы используем создание
и настройку меню частично в декларативном, частично в программном
режиме. Ну уж а управление логикой работы меню можно выполнить
только в программном режиме.
Откройте
форму Form2 в режиме View Designer и
поместите из
панели Toolbox на форму экземпляр компонента MenuStrip с
именем menu
При
выделенном элементе меню в панели Properties раскройте свойство Items
В окне
редактора меню выберите из раскрывающегося списка элемент MenuItem и щелчком на кнопке Add добавьте
в коллекцию Items
Присвойте
добавленному элементу через таблицу свойств редактора меню Name = itemFormat, Text = Format и
щелчком на кнопке OK завершите редактирование
При
выделенном элементе ItemFormat в панели Properties раскройте
свойство DropDownItems
В
окне редактора меню выберите из раскрывающегося списка элемент MenuItem и
щелчком на кнопке Add добавьте в коллекцию DropDownItems объект
команды меню
Присвойте
добавленному элементу через таблицу свойств редактора меню Name = itemFont, Text = Font и
щелчком на кнопке OK завершите редактирование
Выполните
приложение и проверьте работоспособность Form2 на данном
этапе
Поместите
из панели Toolbox экземпляр элемента Panel и настройте его
свойства так:Name = panel, Dock = Fill
Выделите
объект panel и через панель Properties в режиме Events зарегистрируйте
для события Paint обработчик и именем PanelOnPaintНа этом декларативная часть работы закончена. Далее займемся программной частью.
Перейдите
в режим View Code формы Form2 и добавьте перед конструктором
класса Form2 объявление ссылки на элемент менюpublic partial class Form2 : Form
{
// Объявили поле-ссылку на объект команды меню
ToolStripMenuItem itemSelectedFont;
public Form2()
{
InitializeComponent();
}
private void PanelOnPaint(object sender, PaintEventArgs e)
{
}
}
Добавьте
в Form2 после вызова функции инициализации
компонентов код динамического заполнения списка команд в
коллекцию команды Fontpublic Form2()
{
InitializeComponent();
// Заполнение коллекции объекта itemFont
// списком доступных шрифтов стиля "Обычный"
Graphics gr = CreateGraphics(); // Ссылка на контекст устройства
// Читаем все шрифты
FontFamily[] fonts = FontFamily.GetFamilies(gr);
// Перебираем шрифты и формируем команды меню Font
foreach (FontFamily font in fonts)
{
// Выбираем шрифты одного стиля "Обычный"
if (font.IsStyleAvailable(FontStyle.Regular))
{
// Добавляем в коллекцию команды Font
ToolStripMenuItem item =
(ToolStripMenuItem)itemFont.DropDownItems.Add(font.Name);
// Если встретился текущий шрифт формы,
// запоминаем эту команду и отмечаем флажком
if (font.Name == this.Font.Name)
{
itemSelectedFont = item;
itemSelectedFont.Checked = true;
}
}
}
gr.Dispose(); // Освобождаем ограниченный ресурс
}
Запустите
приложение и удостоверьтесь, что при раскрытии команды меню Font она уже заполнена элементами названий шрифтовТеперь обеспечим нужную функциональность выбора шрифтов и
отображения их названия на клиентской области панели panel.
Подпишите
добавленный в коллекцию команды Font элемент item на событие Click. Для этого наберите вручную!!!, чтобы правильно создать
обработчик, следующий код в конце условия ifpublic Form2()
{
....................................................
// Перебираем шрифты и формируем команды меню Font
foreach (FontFamily font in fonts)
{
// Выбираем шрифты одного стиля "Обычный"
if (font.IsStyleAvailable(FontStyle.Regular))
{
...............................................
// Следующую строчку введите вручную!!!
item.Click += new EventHandler(item_Click);
}
}
gr.Dispose(); // Освобождаем ограниченный ресурс
}
Заполните
обработчик item_Click() следующим
кодом, который будет сбрасывать флажок с выбранного ранее
шрифта и устанавливать его на новом выбранном шрифтеvoid item_Click(object sender, EventArgs e)
{
// Повышаем полномочия ссылки для адресации к выделенной команде
ToolStripMenuItem item = (ToolStripMenuItem)sender;
itemSelectedFont.Checked = false; // Сбрасываем предыдущий флажок
item.Checked = true; // Поднимаем новый флажок
itemSelectedFont = item;// Запоминаем отмеченную команду
panel.Invalidate(); // Перерисовываем панель с новым шрифтом
}
Найдите
созданный ранее обработчик события Paint панели и заполните
его кодом перерисовки панели с новым шрифтомprivate void PanelOnPaint(object sender, PaintEventArgs e)
{
Graphics gr = e.Graphics; // Получаем контекст устройства
// Создаем новый шрифт по выбранному в меню с заведомо
// большим размером, чтобы потом подогнать уменьшением
float emSize = 100.0F;
System.Drawing.Font font =
new Font(itemSelectedFont.Text.Trim(), emSize);
String str = font.Name; // Строка для рисования
// Подгоняем размер текстового блока под ширину формы
float widthText = gr.MeasureString(str,
font = new Font(itemSelectedFont.Text, emSize)).Width;
while ((panel.ClientSize.Width - widthText) < 0.0F)
{
emSize -= 0.5F;
widthText = gr.MeasureString(str,
font = new Font(itemSelectedFont.Text, emSize)).Width;
}
// Настроим точку привязки текстового блока
System.Drawing.StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center; // По горизонтали
fmt.LineAlignment = StringAlignment.Center; // По вертикали
// Настроим точку привязки клиентской области панели
PointF point = new PointF(panel.ClientSize.Width / 2,
panel.ClientSize.Height / 2);
// Рисуем на панели название выбранного шрифта текущим цветом формы
gr.DrawString(str, font,
new SolidBrush(this.ForeColor), point, fmt);
}
В классе Form2 переопределите унаследованный OnResize() (ввод начните с ключевого слова override ) и заполните
его следующим кодомprotected override void OnResize(EventArgs e)
{
panel.Invalidate();
base.OnResize(e);
}
Этот метод нужен для перерисовки панели внутри формы при изменении пользователем ее размеров.
Запустите Form2 и удостовертесь в ее работоспособности,
результат будет таким
Обратите внимание, что мы обеспечили центрирование текста на панели и автоматическую его подгонку под размеры формы.
Хотя информативность названий команд меню может оказаться достаточной, в современных меню применяются еще и смысловые пиктограммы. Это нужно для того, чтобы постепенно приучить пользователя к изображениям команд и безболезненно перевести его на более быстрый способ использования кнопок панели инструментов с теми же самыми изображениями.
Visual
Studio 2005 Standard Edition входит архив с большим набором
готовых пиктограмм, который после установки на компьютере
находится в каталоге C:\Program Files\Microsoft Visual
Studio 8\Common7\VS2005ImageLibraryЕсли распаковать этот архив, то используемые в меню изображения
хранятся в папке bitmaps/commands, рассортированные
по разной глубине цвета. Если
в Вашей системе нет этих пиктограмм, то их можно найти в прилагаемом
к данной лабораторной работе каталоге Source.
Служебные изображения в программе удобнее использовать как
ресурсы, внедренные в исполнимую сборку и Project/Add Existing Item, после чего оболочка
физически скопирует его в каталог размещения проекта и он появится
в панели Solution Explorer.
Далее можно использовать
один из способов превращения этого файла в ресурс приложения:
выделить этот файл и через панель Properties установить
для него опцию Build равной . Это заставит оболочку при компиляции
внедрять указанный файл как ресурс в сборку приложения. Внутри
самого кода приложения мы можем адресоваться к этому файлу.
Пусть, например, мы хотим использовать пиктограмму Open. и
добавили этот файл в проект. Доступ к изображению из
Bitmap bmCut = new Bitmap(this.GetType(), "path.Open.bmp");
Первый аргумент должен ссылаться на класс приложения, в нашем случае это порождающий класс текущего объекта. Второй аргумент, это имя файла изображения с путем относительно корневой папки проекта
Присоединить изображение к элементу меню можно присвоением
ссылки свойству Image соответствующей команды,
например
itemCut.Image = bmCut;
После таких действий пиктограмма элемента будет отображаться слева от текста меню на месте отображения флажка.
В данном упражнении разработаем простой редактор изображений, в котором можно будет загружать и сохранять файлы изображений, использовать буфер обмена и отображать рисунки несколькими способами. При этом задействуем ряд стандартных диалоговых окон. Для тренировки рассмотрим несколько способов загрузки изображений в элементы меню.
Откройте
файл Form3.cs в режиме View Designer и задайте свойству Text формы значение " Простой редактор изображений "
Поместите
на форму компонент PictureBox и настройте его так: Name = pictureBox, Dock = Fill В дальнейшем экземпляр элемента управления PictuteBox будет
служить контейнером при просмотре изображений в нашем законченном
упражнении.
Меню для нашего упражнения мы создадим, чередуя декларативный
и Designer.cs. По большому
счету разницы нет, как создавать код: с помощью графического
инструмента или непосредственно вручную. Главное знать, что
писать и как позиционировать объекты. При создании меню проблемы
с
Поместите
на форму компонент MenuStrip с именем menu и
настройте его декларативно в соответствии с приведенным ниже
деревом свойствСоздавать декларативным
способом новые элементы меню любого уровня удобнее, если
навести курсор справа от заготовки команды и раскрыть список
предлагаемых типов, затем выделенный элемент меню можно настроить
через панель Properties.


На схеме свойство ( Name ) взято в скобки,
поскольку это не свойство элемента меню, а имя ссылки, адресующейся
к объекту. Хотя у элементов управления все же есть свойство Name типа String, но его чаще всего используют как метку элемента
для его узнаваемости в коллекции, но не как адресную ссылку.
Свойство ImageTransparentColor устанавливает
цвет в изображении пиктограммы, который система должна считать
прозрачным. В нашем случае каждая загружаемая пиктограмма
имеет рамку цвета , и если не установить
этот цвет в свойстве ImageTransparentColor,
то рамка будет видна на пиктограмме в меню.
Когда снимают
фильм со спецэффектами, передний план тоже снимают на фоне
экрана, выкрашенного в однотонный редко встречающийся цвет.
При монтаже цвет фона легко убирают и подсовывают вместо него
любой нужный материал. Этот редкий экзотический цвет и можно
считать ImageTransparentColor,
который система считает прозрачным.
Мы создали декларативным способом объекты команд всплывающего меню только для двух элементов верхнего уровня. Для третьего элемента объекты команд создадим программно чуть позже. Но сначала нужно в проект загрузить пиктограммы декларативно.
Изображения мы планируем добавить к следующим объектам команд:
| Элемент меню | Файл с пиктограммой |
|---|---|
itemOpen |
Open. |
itemCut |
|
itemCopy |
Copy. |
itemPaste |
Paste. |
itemDelete |
Delete. |
| itemSaveAs | Save. |
Большинство современных компьютеров поддерживают глубину цвета 24 бита, поэтому готовые рисунки будем копировать из соответствующей папки с именем 24bitcolor, которую можно найти в прилагаемом каталоге Source (или в архиве C:\Program Files\Microsoft Visual Studio 8\Common7\VS2005ImageLibrary\VS2005ImageLibrary.zip ).
Пиктограммы в исполнимую сборку программы, для тренировки, будем загружать несколькими способами:
Embedded Resource ),
чтобы компилятор оболочки автоматически включил их код в
исполнимую сборку (файл Paste.bmp )Image элемента
через стандартный файл глобальных ресурсов, используя графические
средства оболочки (файл Delete.bmp )Во всех случаях после компиляции код рисунков должен оказаться в сборке и мы во время исполнения будем присоединять его к элементам меню по мере необходимости, правда, немного по разному.
В панели Solution Explorer двойным щелчком на локальном XML -файле разметки Form3.resx вызовите
редактор ресурсов и через первый узел меню установите режим Images
Выполните
команду Add Resource/Add Existing File меню
редактора ресурсов и, удерживая клавишу Ctrl,
добавьте в ресурс файлы Cut.bmp и Copy.bmp глубиной 24bitcolor из прилагаемого к работе каталога Source
Выполните
команду View Details меню View редактора
ресурсов, чтобы отобразить представление ресурсов в виде
таблицы
Откройте
панель Solution Explorer и убедитесь, что
оболочка создала каталог Resources, в который
скопировала отмеченные файлы
Оболочка автоматически присвоила ресурсам подходящие имена,
которые мы можем изменить в панели Properties,
выделяя соответствующие ресурсы. Здесь же мы можем указать,
где физически до компиляции будут располагаться рисунки: в XML -XML -файле
разметки будут помещены только ссылки.

Пока у нас рисунки имеют состояние Linked at ,
что означает, что при компиляции их код будет внедрен в исполнимую
сборку прямо из BMP -файлов по ссылкам, помещенным
в XML -файл Form3.resx. Поэтому
присутствие BMP -файлов в проекте обязательно
при каждой компиляции. При установке для любого рисунка опции оболочка
уже на
Чтобы убедиться в сказанном, выполните следующее:
В панели Solution Explorer вызовите контекстное меню
для узла Form3.resx и
выполните команду View Em, чтобы открыть XML -
Убедитесь,
что в конце XML -файла прописаны только ссылки
на добавленные в проект файлы рисунков<data name="Copy" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Copy.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Cut" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Cut.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
В панели Solution Explorer вызовите контекстное меню
для узла Form3.resx и выполните команду View
Designer, чтобы открыть
Выделите
ресурс Cut и через панель Properties установите
для него свойство Persistence в значение Embedded in
.resx
Вновь
откройте Form3.resx в режиме View
Em и убедитесь, что вместо
ссылки на файл Cut.bmp оболочка включила
в ресурс сам код этого рисунка<data name="Copy" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Copy.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Cut" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
Qk02AwAAAAAAADYAAAAoAAAAEAAAABAAAAABABgAAAAAAAAAAAASCwAAEgsAAAAAAAAAAAAA/wD//wD/
/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/u6GUl19FY0Iy
oo2C/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/zYZjq2I/ilAzYkU3vb2+uZ+SZkM0
Xj8wqJGG/wD//wD//wD//wD//wD//wD//wD/3YNVw7KquJyOhU4zwMDAxYJhqmI+YzwpLyUj/wD//wD/
/wD//wD//wD//wD//wD/4YVXxbOqva6ojFI1vLi21HxSwrKqrpSGa0It/wD//wD//wD//wD//wD//wD/
/wD/2ph25YZWsGZBj1U4nHJdyXVNwbm1up6Qj1U2/wD//wD//wD//wD//wD//wD//wD/yLWs645d24JW
n19AlGBHvnRP1XtPuWtFrYd0/wD//wD//wD//wD//wD//wD//wD//wD/yLau3pl234RWxo1wzHhN0nxR
xpF3vaec/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/iHNmcWBWhmdVq56Z/wD//wD//wD//wD/
/wD//wD//wD//wD//wD//wD//wD/uLSxjoB0fm5jaV1VdnJu/wD//wD//wD//wD//wD//wD//wD//wD/
/wD//wD//wD/urCqopSLqKCZjoF3Y1tV/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/vrGq
oZSLyMbEzcXAa19Wra2u/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/qZuSsqijw8PD6OTifG9m
qKmq/wD//wD//wD//wD//wD//wD//wD//wD//wD/u7i2o5aMvLq5/wD/3dzcvLKrpqGe/wD//wD//wD/
/wD//wD//wD//wD//wD//wD/v7izs6ie/wD//wD/y8vL4t3aoZeQ/wD//wD//wD//wD//wD//wD//wD/
/wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD//wD/
</value>
</data>
Выделите
ресурс Cut и через панель Properties верните
свойство Persistence в прежнее значение Linked
at compile time Добавим к проекту файл рисунка Paste.bmp и сообщим компилятору о необходимости включать его код в исполнимую сборку.
В панели Solution Explorer вызовите контекстное меню
для папки Resources,
в которой уже должны находиться файлы Cut.bmp и Copy.bmp с
глубиной цвета 24bitcolor, и командой Add/Existing
Item добавьте к проекту из каталога Source файл Paste.bmp с
такой же глубиной цвета, предварительно установив для диалогового
окна соответствующий фильтр
Выделите
файл Paste.bmp и в панели Properties установите для него Build Action = Embedded Resource При такой настройке при компиляции код файла будет копироваться в исполнимую сборку.
Присоединим к свойству Image элемента меню itemDelete файл
с рисунком Delete.bmp через стандартный файл
глобальных ресурсов. Этот
файл может хранить в себе множественные ресурсы, которые доступны
в любом месте
Откройте
файл Form3.cs в режиме View Designer,
выделите через раскрывающийся список панели Properties элемент itemDelete,
раскройте его свойство Image и для режима Project resource file загрузите в элемент
кнопкой Import файл Delete.bmp из
прилагаемого к работе каталога Source
Загруженная пиктограмма немедленно появится слева от команды Delete на меню формы

Откройте
в каталоге Properties проекта стандартный
файл глобальных ресурсов Resources проекта
в режиме View
Em и убедитесь,
что оболочка поместила относительную ссылку на Delete.bmp в
этот файл<data name="Delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Delete.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Откройте
файл Form3.Designer.cs и в секции #region убедитесь, что
оболочка сразу прописала код присоединения рисунка к элементу
меню//
// itemDelete
//
this.itemDelete.Image = global::StripControls.Properties.Resources.Delete;
Никаких дополнительных строк кода для загрузки рисунка указанным способом в дальнейшем не потребуется, оболочка все сделала за нас.
Замечание.
Если пытаться немедленно присоединить к элементу файл рисунка
через локальный ресурс в использованном нами окне Select
,
то оболочка выкинет все ранее присоединенные файлы из этого
ресурса (В нашем случае и Copy )
и оставит только один непосредственно присоединяемый к элементу
Загрузим рисунок Open.bmp в файл пользовательский глобальных ресурсов MyResources.resx, который нужно создать и добавить к проекту в любое место.
В панели Solution Explorer вызовите контекстное меню
для папки Resources и добавьте в нее
командой Add/New
Item новый файл глобальных
ресурсов с именем MyResources.resx
В панели Solution Explorer двойным щелчком
на файле Resources.resx откройте его в режиме View
Designer
Переведите
редактор в режим Images и командой Add Resource/Add Existing File добавьте
к проекту файл Open.bmp глубиной
цвета 24bitcolor из прилагаемого каталога Source точно
также, как мы это делали ранее при загрузке файлов Cut.bmp и Copy.bmp в локальный ресурс Form3.resxОболочка физически скопирует файл Open.bmp в каталог Resources проекта и пропишет относительную ссылку на него в пользовательском XML -файле разметки MyResources.resx глобальных ресурсов
<data name="Open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Open.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Мы точно так же, как для файла локальных ресурсов, можем отредактировать
свойство ресурса и загрузить его физически в глобальный ресурс,
изменив свойство , или оставить
его подгружаемым в исполнимую сборку во время компиляции.
Файл Save.bmp мы будем загружать в элемент itemSaveAs непосредственно во время выполнения программы. Поэтому нужно присоединить его к проекту и настроить свойства так, чтобы оболочка при компиляции копировала этот файл в место размещения исполнимой сборки.
В панели Solution Explorer вызовите контекстное меню
для папки Resources и командой Add/Existing
Item добавьте в проект файл Save.bmp ( 24bitcolor )
из прилагаемого каталога Source
Выделите
скопированный файл Save.bmp и через панель Properties установите свойство Copy
to Output Directory в
значение Copy if newer,
чтобы оболочка скопировала файл в каталог размещения исполнимой
сборкиИтак, на предудущих этапах работы мы загрузили все нужные
файлы с пиктограммами в файлы Image элемента
меню itemDelete, поэтому дальнейших действий
для этого элемента никаких
не требуется. Ресурсы остальных пиктограмм пропишем в коде
и они будут присоединяться к элементам меню на этапе выполнения
программы.
Откройте
файл Form3.cs в режиме View Em и
добавьте в класс Form3 следующую функцию с именем LoadImages() для присоединения рисунков к элементам меню// Загрузка изображений в элементы меню
void LoadImages()
{
// Присоединяем 2 пиктограммы из
// файла локальных ресурсов Form3.resx
ResourceManager resources =
new ResourceManager(typeof(Form3));//или (this.GetType())
itemCopy.Image = (Image)resources.GetObject("Copy");
itemCut.Image = (Image)resources.GetObject("Cut");
// Присоединяем пиктограмму из файла,
// помеченного как Embedded Resource
itemPaste.Image = new Bitmap(this.GetType(), // или (typeof(Form3)
"Resources.Paste.bmp");
///////////////////////////////
// Пиктограмма для Delete внедрена декларативно
// через стандартный файл глобальных ресурсов,
// поэтому для нее ничего не делаем
///////////////////////////////
// Присоединяем пиктограмму из пользовательского
// файла глобальных ресурсов MyResources.resx
itemOpen.Image = Resources.MyResources.Open;
// Присоединяем пиктограмму непосредственно из файла рисунка
itemSaveAs.Image = new Bitmap("Resources\\Save.bmp");
}
Вставьте
в Form3 после
вызова метода InitializeComponent() следующий
код, выполняющий присоединениеpublic Form3()
{
InitializeComponent();
LoadImages();// Присоединяем пиктограммы
}
Запустите Упражнение 3 на выполнение и убедитесь в наличии пиктограмм
для назначенных элементов меню![]() |
![]() |
Обратите внимание на то, что для меню View заготовлен
только узел itemView, а его коллекция DropDownItems пока
пуста, поскольку ранее мы договорились наполнить ее
Меню View будет управлять формой представления рисунков элементом pictureBox, который мы добавили на Form3 ранее. Через общий
обработчик элементов меню View мы будем передавать в pictureBox требуемые значения его свойства SizeMode из библиотечного перечисления PictureBoxSizeMode. Закрепленное за элементом меню значение
перечисления PictureBoxSizeMode будем сохранять в свойстве элемента и извлекать его в обработчике с последующей распаковкой
Object в PictureBoxSizeMode.

Здесь свойство Name - это свойство элемента типа String, а
не имя ссылки с адресом объекта. При настройке элементов меню
по приведенному ниже коду не забудьте начать набор строки регистрации
обработчика события Click для первого элемента DropDownItems[0] вручную
- с применением клавиши Tab. Это позволит создать
обработчик с правильной Click последующих
элементов можно воспользоваться упрощенным синтаксисом.
Добавьте
в класс Form3 функцию с именем CreateView() для
создания и настройки элементов меню View.
Код функции соответствует приведенной выше схеме и представлен
ниже// Функция создания элементов меню View программным способом
void CreateView()
{
// Имя элемента меню на верхнем уровне для возможности
// адресации в коллекции Items по его имени
itemView.Name = "View";
ToolStripMenuItem item=new ToolStripMenuItem("Normal");
item.Tag = PictureBoxSizeMode.Normal;
// Создайте вручную!!!!!!!
item.Click += new EventHandler(ViewOnClick);
itemView.DropDownItems.Add(item);// Добавили в коллекцию View
// Имя элемента меню View для возможности адресации
// в коллекции DropDownItems по его имени
item.Name = "Normal";
// Отмечаем флажок, поскольку в начальном состоянии
// свойство SizeMode элемента PictureBox именно Normal
item.Checked = true;
item = new ToolStripMenuItem();
item.Text = "Center";
item.Tag = PictureBoxSizeMode.CenterImage;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "Center";
item = new ToolStripMenuItem("Stretch");
item.Tag = PictureBoxSizeMode.StretchImage;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "Stretch";
item = new ToolStripMenuItem();
item.Text = "Zoom";
item.Tag = PictureBoxSizeMode.Zoom;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "Zoom";
item = new ToolStripMenuItem("AutoSize");
item.Tag = PictureBoxSizeMode.AutoSize;
item.Click += ViewOnClick;
itemView.DropDownItems.Add(item);
item.Name = "AutoSize";
}
Еще раз вспомним, что порядок следования кода добавления элементов
в коллекцию определяет их порядок прорисовки в работающем приложении.
При создании элементов меню View мы
поочередно используем разные ToolStripMenuItem.
Мы задаем свойству типа Object упакованное
значение перечисления PictureBoxSizeMode, чтобы затем в обработчике
распаковать его. Помечая элемент значением свойства Name мы
перестраховываемся на тот случай, если вдруг далее нам придется
к нему адресоваться в коллекции по синтаксису ассоциативного массива
(
Вставьте
в конец конструктора Form3() одноименного класса код вызова
функции CreateView()public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
LoadImages();// Присоединяем пиктограммы
CreateView();// Динамически заполняем меню View
}
В созданный
обработчик ViewOnClick() внесите следующий кодvoid ViewOnClick(object sender, EventArgs e)
{
// Приводим ссылку на элемент меню View, сгенерировавший событие
ToolStripMenuItem item = (ToolStripMenuItem)sender;
// Распаковываем объект и используем сохраненную
// в нем информацию для управления контейнером рисунков
pictureBox.SizeMode = (PictureBoxSizeMode)item.Tag;
////////////////////////////////////////////////////////
// Управление флажками выполним в обработчике события
// DropDownOpening, которое информирует о раскрытия узла View
////////////////////////////////////////////////////////
//////////// Следующий код закомментирован /////////////
//////////// Жалко выбрасывать!!! /////////////
/*//////////////////////////////////////////////////////
// Сбрасываем флажки всех элементов
foreach (ToolStripMenuItem it in itemView.DropDownItems)
it.Checked = false;
// Устанавливаем флажок на выделенном элементе,
// адресуясь к коллекции по имени элемента
((ToolStripMenuItem)itemView.DropDownItems[item.Name]).Checked = true;
//item.Checked = true; // Альтернативный вариант
//*/////////////////////////////////////////////////////
}
Запустите Упражнение 3 и убедитесь, что команды узла View динамически
формируются
Команды меню (опции) являются неотъемлемой частью интерфейса пользователя в современных программах. Код обработчиков элементов меню реализует основную функциональность приложения и требует пристального внимания разработчика. Заполним наши обработчики команд нужным кодом. Тщательно разберитесь в нем, поскольку он реализует логику программы и во многом может стать для вас (программистов!!!, но не кочегаров и зольщиков) поучительным.
Ранее мы создали заготовки обработчиков для всех значимых элементов меню. Пришла пора их запрограммировать, тем самым обеспечив функциональность программе. Значимые элементы меню недаром называют командами. Именно через них пользователь, незнакомый с тонкостями программирования, управляет компьютером для выполнения своей работы.
Вначале запрограммируем все обработчики элементов меню верхнего уровня, которые срабатывают при раскрытии главных узлов. Главная задача состоит в том, чтобы сделать высплывающие меню контекстно-зависимыми, например, некоторые команды скрыть или показать недоступными для выбора.
Найдите
обработчик FileOnDropDown() события DropDownOpening,
которое генерируется при раскрытии элемента File,
и запрограммируйте его следующим образомprivate void FileOnDropDown(object sender, EventArgs e)
{
// Сделать недоступным команду SaveAs, если контейнер пустой
itemSaveAs.Enabled = pictureBox.Image != null;
}
Найдите
обработчик EditOnDropDown() события DropDownOpening узла Edit и запрограммируйте его такprivate void EditOnDropDown(object sender, EventArgs e)
{
/////////////////////////////////////////////////
// Регулируем доступность команд Copy, Cut, Delete
/////////////////////////////////////////////////
// Нечего редактировать - контейнер пустой
itemCopy.Enabled = itemCut.Enabled = itemDelete.Enabled =
pictureBox.Image != null;
/////////////////////////////////////////////////
// Регулируем доступность команды Paste
/////////////////////////////////////////////////
// Ивлекаем адрес системного буфера обмена,
// используя интерфейсную ссылку базового типа
IDataObject data = Clipboard.GetDataObject();
// Если системный буфер не содержит рисунков
// подходящего типа, то вставлять тоже нечего
itemPaste.Enabled = data.GetDataPresent(typeof(Bitmap))
|| data.GetDataPresent(typeof(System.Drawing.Imaging.Metafile));
}
Найдите
обработчик ViewOnDropDown() для узла View и внесите в него
следующий кодprivate void ViewOnDropDown(object sender, EventArgs e)
{
// При раскрытии узла View перебираем коллекцию
// и управляем флажками с помощью состояния pictureBox
foreach (ToolStripMenuItem item in itemView.DropDownItems)
{
item.Checked =
(PictureBoxSizeMode)item.Tag == pictureBox.SizeMode;
}
}
Приступим к программированию самих команд всплывающих меню.
Найдите
обработчик ExitOnClick() для команды Exit и закодируйте его
такprivate void ExitOnClick(object sender, EventArgs e)
{
this.Close();
}
Найдите
обработчик DeleteOnClick() для команды Delete и внесите в
него следующий кодprivate void DeleteOnClick(object sender, EventArgs e)
{
pictureBox.Image = null;
}
В этом обработчике мы просто обнуляем ссылку на рисунок и
изображение исчезает из контейнера pictureBox.
Найдите
обработчик CopyOnClick() для команды Copy и заполните его
такprivate void CopyOnClick(object sender, EventArgs e)
{
// Заполняем системный буфер обмена рисунком контейнера
// Если при раскрытии узла Edit текущий
// рисунок есть, то команда Copy доступна
Clipboard.SetDataObject(pictureBox.Image, true);
}
Здесь вызывается
Найдите
обработчик CutOnClick() для команды Cut и
заполните его такprivate void CutOnClick(object sender, EventArgs e)
{
////////////////////////////////////////////////
// Используем уже готовые обработчики!!!
////////////////////////////////////////////////
// Копируем в системный буфер обмена
CopyOnClick(sender, e);
// Обнуляем контейнер рисунка
DeleteOnClick(sender, e);
}
Найдите
обработчик PasteOnClick() для команды Paste и заполните его такprivate void PasteOnClick(object sender, EventArgs e)
{
// Подключаемся к системному буферу обмена
IDataObject data = Clipboard.GetDataObject();
// Распознаем тип изображения в буфере обмена и читаем данные
Type typeMeta = typeof(System.Drawing.Imaging.Metafile);
if (data.GetDataPresent(typeMeta))
pictureBox.Image = (Image)data.GetData(typeMeta);
else
pictureBox.Image = (Image)data.GetData(typeof(Bitmap));
}
Доступность команды Paste проверяется в узле Edit и
зависит от типа ожидаемых данных. Только в случае допустимости
типа изображения может сработать обработчик PasteOnClick(),
поэтому-то
мы и рассматриваем в нем только две альтернативы.
Для команд Open и Save As нам
потребуется механизм отображения имени загруженного файла в
заголовке окна наряду с самим заголовком. Для этого нужно объявить
два поля типа String.
Добавьте
в начало класса Form3 два поля, связанные с заголовком окна,
и инициализируйте одно из них в public partial class Form3 : Form
{
String originalTitle; // Поле для хранения чистого заголовка
String name; // Поле для хранения имени загруженного рисунка
public Form3()
{
InitializeComponent();
LoadImages();// Присоединяем пиктограммы
CreateView();// Динамически заполняем меню View
originalTitle = this.Text;// Сохраняем чистый заголовок
}
............................................................
}
В этой команде мы будем использовать стандартный диалог OpenFileDialog,
который должным образом настроим. При загрузке файла в контейнер
рисунка следует использовать защищенный код, перехватывающий
и обрабатывающий возможные системные исключения на случай попытки
загрузить файл формата, который элементом PictureBox не
поддерживается.
Найдите
обработчик OpenOnClick() для команды Open и заполните его так// Обработчик команды Open
OpenFileDialog openDialog; // Объявили ссылку на диалог
bool openFlag = true;// Запускается в первый раз
//
private void OpenOnClick(object sender, EventArgs e)
{
if (openFlag)
{
openFlag = false; // Больше не создавать и не настраивать!
// Создаем и настраиваем диалог
openDialog = new OpenFileDialog();
openDialog.Filter = "All allowable Image Files" + // 1)Заголовок
"|*.bmp;*.ico;*.gif;*.jpeg;*.jpg;*.jfif;*.png;*.tif;*.tiff;*.wmf;*.emf|" + // 1)|Шаблон|
"Windows Bitmap (*.bmp)|*.bmp|" + // 2) Заголовок|Шаблон|
"Graphics Interchange Format (*.gif)|*.gif|" + // 3) Заголовок|Шаблон|
"JPEG File Interchange Format (*.jpg)|*.jpeg;*.jpg;*.jfif|" + // 4) Заголовок|Шаблон|
"Portable Network Graphics (*.png)|*.png|" + // 5) Заголовок|Шаблон|
"Tagged Image File Format (*.tif)|*.tif;*.tiff|" + // 6) Заголовок|Шаблон|
"Windows Metafile (*.wmf)|*.wmf|" + // 7) Заголовок|Шаблон|
"Enhanced Metafile (*emf)|*.emf|" + // 8) Заголовок|Шаблон|
"All Files (*.*)|*.*"; // 9) Заголовок|Шаблон
// Устанавливаем последний шаблон активным (по умолчанию 1!)
String[] str = openDialog.Filter.Split('|');
openDialog.FilterIndex = str.Length / 2;
}
// Защищенный код для перехвата системных исключений на
// случай попытки загрузки неподдерживаемого формата данных
if (openDialog.ShowDialog() == DialogResult.OK)
try
{
pictureBox.Image = Image.FromFile(openDialog.FileName);
// Выводим имя рисунка в заголовок окна
name = openDialog.FileName;
String[] str = name.Split('\\');
name = str[str.Length - 1].ToLower();
this.Text = "(" + name + ") " + originalTitle;
}
catch (Exception ext)
{
MessageBox.Show(ext.Message, this.Text,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
В приведенном коде ссылка на диалог объявлена как поле, чтобы
он создавался только один раз и сохранял предыдущие настройки
пользователя при повторных обращениях. Фильтр диалога мы тоже
настраиваем только раз и для тренировки устанавливаем его на
последний шаблон (первый шаблон устанавливается по умолчанию
и
В этой команде мы будем использовать стандартный диалог SaveFileDialog,
который должным образом настроим. При выгрузке файла
рисунка на диск также, на случай
Найдите
обработчик SaveAsOnClick() для команды Save As и
заполните его так// Обработчик команды Save As
SaveFileDialog saveDialog; // Объявили ссылку на диалог
System.Drawing.Imaging.ImageFormat[] imageFormat;// Объявили ссылку на массив
bool saveFlag = true;// Запускается в первый раз
//
private void SaveAsOnClick(object sender, EventArgs e)
{
if (saveFlag)
{
saveFlag = false; // Больше не создавать и не настраивать!
// Создаем диалог
saveDialog = new SaveFileDialog();
// Настраиваем фильтр
saveDialog.Filter = "Windows Bitmap (*.bmp)|*.bmp|" +
"Graphics Interchange Format (*.gif)|*.gif|" +
"JPEG File Interchange Format (*.jpg)|*.jpeg;*.jpg;*.jfif|" +
"Portable Network Graphics (*.png)|*.png|" +
"Tagged Image File Format (*.tif)|*.tif;*.tiff";
// Инициализируем массив допустимыми форматами рисунков
imageFormat = new System.Drawing.Imaging.ImageFormat[] {
System.Drawing.Imaging.ImageFormat.Bmp,
System.Drawing.Imaging.ImageFormat.Gif,
System.Drawing.Imaging.ImageFormat.Jpeg,
System.Drawing.Imaging.ImageFormat.Png,
System.Drawing.Imaging.ImageFormat.Tiff
};
}
// Заполняем поле именем загруженного рисунка без расширения
saveDialog.FileName = name.Substring(0, name.LastIndexOf('.'));
// Защищенный код для перехвата системных
// исключений на случай сбоя записи
if (saveDialog.ShowDialog() == DialogResult.OK)
try
{
// Пытаемся сохранить в формате текущего фильтра
pictureBox.Image.Save(saveDialog.FileName,
imageFormat[saveDialog.FilterIndex - 1]);
// Обновляем контейнер новым форматом - ???????????????
//pictureBox.Image = Image.FromFile(saveDialog.FileName);
// Выводим в заголовок окна имя файла с новым расширением
name = saveDialog.FileName;
String[] str = name.Split('\\');
name = str[str.Length - 1].ToLower();
this.Text = "(" + name + ") " + originalTitle;
}
catch (Exception ext)
{
MessageBox.Show(ext.Message, this.Text,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
На этом выполнение Упражнения 3 завершено.
Source, или любых других.
Запустите
приложение и испытайте его работу. Результат должен получиться
примерно такимВ этом упражнении мы создадим заготовку меню с пиктограммами,
которые будем присоединять к элементам меню из списка изображений.
Сам список с изображениями можно заполнять из хранилища
ресурсов, которое находится внутри
файла исполнимой сборки нашего приложения. В свою очередь,
хранилище ресурсов сборки заполняется при компиляции сборки
из указанного XML -файла,
который нужно сформировать заранее и поместить в него ссылки
на необходимые текстовые или двоичные ресурсы. XML -
или двоичный файл можно создать декларативно (вручную) или
программно, написав соответствующую консольную утилиту.
Таким образом, план действий будет таким:
В пространстве имен System. двух
библиотечных сборок mscorlib.dll и System.Windows.Forms.dll находятся
все необходимые типы, которые обеспечивают программную работу
с ресурсами. Необходимые для работы приложения ресурсы находятся
в готовой исполнимой сборке в специальной секции, называемой
хранилищем ресурсов, или отдельно в двоичном файле. В хранилище
ресурсов они попадают при компиляции приложения из предварительно
подготовленного

Разработаем утилиту, которая будет создавать файл
ресурсов и наполнять его нужными объектами. Для
разнообразия в
Добавьте
к текущему решению командой File/Add/New
Project еще один проект с именем UtilityResource типа

Добавьте
к проекту UtilityResource ссылки на сборки System.Windows.Forms и System.Drawing.
Для этого выделите в панели Solution Explorer узел UtilityResource и
выполните команду Project/Add Reference
В панели Solution Explorer скопируйте файлы Cut.bmp, Copy.bmp, Paste.bmp, Delete.bmp, Open.bmp, Save.bmp из
узла Resources проекта StripControls в
корневой узел проекта UtilityResource (или
добавьте напрямую из прилагаемой папки Source,
как показано ниже для иконки приложения)
В панели Solution Explorer вызовите контекстное меню
для корневого узла UtilityResource и выполните
команду Add/Existing Item. В прилагаемом
каталоге Source выберите иконку, например 1.ico, и добавьте ее в проект UtilityResource
Выделите
все добавленные к проекту UtilityResource файлы
изображений и в панели Properties определите
для них опции Copy to Output Directory=Copy always, Build Action =None, чтобы при исполнении
проекта оболочка копировала их в каталог размещения сборки
using System;
namespace UtilityResource
{
using System.Resources;
using System.Drawing;
class Program
{
static void Main(string[] args)
{
// Создаем объект для записи в двоичный файл ресурсов
ResourceWriter rw = new ResourceWriter("ResXForm4.resources");
// Добавляем в файл описание ресурсов 'Ключ-Значение' (Key-Value)
// Ресурсы должны находиться в одном каталоге с утилитой!!!
rw.AddResource("Cut", new Bitmap("Cut.bmp"));
rw.AddResource("Copy", new Bitmap("Copy.bmp"));
rw.AddResource("Paste", new Bitmap("Paste.bmp"));
rw.AddResource("Delete", new Bitmap("Delete.bmp"));
rw.AddResource("Open", new Bitmap("Open.bmp"));
rw.AddResource("Save", new Bitmap("Save.bmp"));
rw.AddResource("strTitle", "Список изображений");
rw.AddResource("icoTitle", new Icon("1.ico"));
// Сохраняем файл с добавленными ресурсами и закрываем его
rw.Generate();
rw.Close();
}
}
}
В панели Solution Explorer вызовите контекстное меню
для узла UtilityResource и
выполните команду Set as StartUp Project,
чтобы сделать проект стартовым (либо просто щелкните левой
кнопкой мыши на узле UtilityResource ).
Корневой узел
Выполните
проект UtilityResource, чтобы создать файл
ресурсов с именем ResXForm4.resources программным
способом
В панели Solution Explorer вызовите контекстное меню для узла проекта UtilityResource и
исключите его из решения командой Remove
В панели Solution Explorer вызовите контекстное меню
для корневого узла проекта StripControls и
командой Add/Existing
Item добавьте
созданный ранее
Настройте
через панель Properties добавленный к
проекту StripControls файл
ресурсов ResXForm4.resources так, чтобы
он стал копируемым при компиляции проекта в каталог размещения
сборки, т.е. установите опции Copy to Output Directory=Copy always, Build Action =NoneНа этом подготовительная часть по формированию
Наша главная задача в упражнении 4 - посмотреть способ загрузки
пиктограмм в меню из
Добавьте
к проекту StripControls форму с именем Form4 командой Project/Add Windows Form
Поместите
из панели Toolbox на форму кнопку Button (или
любой другой элемент управления) и сразу удалите ее, чтобы
явно прописалось конструктором оболочки свойство Name=Form4 формы
в файле Form4.Designer.cs (или сами пропишите его в этом
файле)
Откройте
файл Start.cs в режиме View Designer,
выделите объект списка listBoxChoice и добавьте в его свойство Items строку:
"Упражнение 4."
Дополните
файл Start.cs блоком кода, запускающим созданную форму Form4// Если форма не запущена - запускаем
if (!IsRun)
{
switch (formName)
{
case "Form1":
frm = new Form1();
break;
case "Form2":
frm = new Form2();
break;
case "Form3":
frm = new Form3();
break;
case "Form4":
frm = new Form4();
break;
}
this.AddOwnedForm(frm); // Сделать новую frm подчиненной Start
// frm.Owner = this; // Алтернативный способ назначить владельца
frm.ShowInTaskbar = false; // Не отображать метку окна в панели задач
frm.Show(); // Показать новую форму
}
else
frm.Focus(); // Передать фокус запущенной форме
Откройте
файл Form4.cs в режиме View Em и
добавьте в него функцию CreateMenu() программного
создания меню, которая будет иметь следующий код// Видимые в методах класса поля-ссылки на элементы меню
MenuStrip menu;
ToolStripMenuItem itemFile;
ToolStripMenuItem itemOpen;
ToolStripMenuItem itemSaveAs;
ToolStripMenuItem itemExit;
ToolStripMenuItem itemEdit;
ToolStripMenuItem itemCut;
ToolStripMenuItem itemCopy;
ToolStripMenuItem itemPaste;
ToolStripMenuItem itemDelete;
void CreateMenu()
{
// Создание меню
menu = new MenuStrip();
menu.Parent = this;
// Формирование верхней линейки меню
itemFile = new ToolStripMenuItem("File");
menu.Items.Add(itemFile);
itemEdit = new ToolStripMenuItem("Edit");
menu.Items.Add(itemEdit);
// Сборка узла File
itemFile.DropDownItems.Add(itemOpen = new ToolStripMenuItem("Open"));
itemFile.DropDownItems.Add(itemSaveAs = new ToolStripMenuItem("Save As..."));
itemFile.DropDownItems.Add(new ToolStripSeparator());
itemFile.DropDownItems.Add(itemExit = new ToolStripMenuItem("Exit"));
// Сборка узла Edit
itemEdit.DropDownItems.Add(itemCut = new ToolStripMenuItem("Cut"));
itemEdit.DropDownItems.Add(itemCopy = new ToolStripMenuItem("Copy"));
itemEdit.DropDownItems.Add(itemPaste = new ToolStripMenuItem("Paste"));
itemEdit.DropDownItems.Add(itemDelete = new ToolStripMenuItem("Delete"));
}
Добавьте
в файл Form4.cs функцию LoadImage() загрузки
пиктограмм меню из // Создаем список для хранения изображений
ImageList imageList = new ImageList();
void LoadImage()
{
// Присоединяем ранее сформированные ресурсы из файла ресурсов
ResourceReader rr = new ResourceReader("ResXForm4.resources");
IDictionaryEnumerator en = rr.GetEnumerator();
while (en.MoveNext())
{
switch (en.Key.ToString())
{
default:
imageList.Images.Add(en.Key.ToString(), (Image)en.Value);
break;
case "icoTitle":
this.Icon = (Icon)en.Value; // Иконка окна
break;
case "strTitle":
this.Text = (String)en.Value; // Заголовок окна
break;
}
}
rr.Close(); // Закрыли файл ресурсов
// Определяем цвет, имитирующий прозрачность окраин пиктограмм
imageList.TransparentColor = Color.Magenta;
// Закрепляем пиктограммы из списка за командами меню с помощью ключей
itemOpen.Image = imageList.Images["Open"];
itemSaveAs.Image = imageList.Images["Save"];
itemCut.Image = imageList.Images["Cut"];
itemCopy.Image = imageList.Images["Copy"];
itemPaste.Image = imageList.Images["Paste"];
itemDelete.Image = imageList.Images["Delete"];
}
Вставьте
вызовы функций создания меню и загрузки изображений в конструктор
класса Form4 и добавьте инструкции подключения
библиотечных пространств именОбщий код файла Form4.cs будет таким
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace StripControls
{
using System.Resources;
using System.Collections;
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
CreateMenu();// Создаем меню
// Загружаем в меню пиктограммы опций
LoadImage();
}
// Видимые в методах поля-ссылки на элементы меню
MenuStrip menu;
ToolStripMenuItem itemFile;
ToolStripMenuItem itemOpen;
ToolStripMenuItem itemSaveAs;
ToolStripMenuItem itemExit;
ToolStripMenuItem itemEdit;
ToolStripMenuItem itemCut;
ToolStripMenuItem itemCopy;
ToolStripMenuItem itemPaste;
ToolStripMenuItem itemDelete;
void CreateMenu()
{
// Создание меню
menu = new MenuStrip();
menu.Parent = this;
// Формирование верхней линейки меню
itemFile = new ToolStripMenuItem("File");
menu.Items.Add(itemFile);
itemEdit = new ToolStripMenuItem("Edit");
menu.Items.Add(itemEdit);
// Сборка узла File
itemFile.DropDownItems.Add(itemOpen = new ToolStripMenuItem("Open"));
itemFile.DropDownItems.Add(itemSaveAs = new ToolStripMenuItem("Save As..."));
itemFile.DropDownItems.Add(new ToolStripSeparator());
itemFile.DropDownItems.Add(itemExit = new ToolStripMenuItem("Exit"));
// Сборка узла Edit
itemEdit.DropDownItems.Add(itemCut = new ToolStripMenuItem("Cut"));
itemEdit.DropDownItems.Add(itemCopy = new ToolStripMenuItem("Copy"));
itemEdit.DropDownItems.Add(itemPaste = new ToolStripMenuItem("Paste"));
itemEdit.DropDownItems.Add(itemDelete = new ToolStripMenuItem("Delete"));
}
// Создаем список для хранения изображений
ImageList imageList = new ImageList();
void LoadImage()
{
// Присоединяем ранее сформированные ресурсы из файла ресурсов
ResourceReader rr = new ResourceReader("ResXForm4.resources");
IDictionaryEnumerator en = rr.GetEnumerator();
while (en.MoveNext())
{
switch (en.Key.ToString())
{
default:
imageList.Images.Add(en.Key.ToString(), (Image)en.Value);
break;
case "icoTitle":
this.Icon = (Icon)en.Value; // Иконка окна
break;
case "strTitle":
this.Text = (String)en.Value; // Заголовок окна
break;
}
}
rr.Close(); // Закрыли файл ресурсов
// Определяем цвет фона, воспринимаемый прозрачным
imageList.TransparentColor = Color.Magenta;
// Закрепляем пиктограммы из списка за командами меню с помощью ключей
itemOpen.Image = imageList.Images["Open"];
itemSaveAs.Image = imageList.Images["Save"];
itemCut.Image = imageList.Images["Cut"];
itemCopy.Image = imageList.Images["Copy"];
itemPaste.Image = imageList.Images["Paste"];
itemDelete.Image = imageList.Images["Delete"];
}
}
}
Запустите
на выполнение Упражнение 4 и убедитесь, что пиктограммы нормально
загружаются из 
Контекстное меню вызывается по щелчку правой кнопки мыши на
соответствующем элементе управления и зависит от конкретного
состояния этого элемента. Контекстное меню представлено классом System.Windows.Forms.ContextMenuStrip. Любой
визуальный элемент управления, производный от класса Control,
наследует общедоступное виртуальное свойство ContextMenuStrip,
к которому можно привязать объект контекстного меню.
Элементами контекстного меню ContextMenuStrip,
как и в случае с основным меню, порожденным классом MenuStrip,
являются объекты класса ToolStripMenuItem.
Эти объекты добавляются в коллекцию Items,
являющуюся ContextMenuStrip.
Каждый из объектов-элементов контекстного меню имеет свойство-коллекцию DropDownItems,
в которую помещают элементы вложенного меню.
Мы можем заготовить несколько различных объектов контекстного меню и привязать каждый из них к определенному элементу управления. Но эти же несколько объектов контекстного меню можно поочередно привязывать к одному и тому же элементу управления в динамическом режиме в зависимости от текущего состояния контекста.
Рассмотрим Упражнение 5, в котором будем использовать объекты
контекстного меню. Мы создадим несколько контекстных меню,
часть динамическим способом, часть декларативным способом.
Объекты контекстных меню будем присоединять к различным элементам
управления через свойство этих элементов ContextMenuStrip.
В панели Solution
Explorer выделите корневой узел проекта StripControls и
командой меню оболочки Project/Add Windows Form добавьте
к проекту файл Form5.cs
Откройте
файл Start.cs в режиме View Designer,
выделите объект listBoxChoice и добавьте
через панель Properties в свойство Items ссылку
на Упражнение 5
Откройте
файл Start.cs в режиме View Em,
найдите обработчик listBoxChoice_SelectedIndexChanged() и
добавьте в него код запуска формы Form5Form frm; // Объявили поле
private void listBoxChoice_SelectedIndexChanged(object sender, EventArgs e)
{
// Привязали номер упражнения к индексу списка
ListBox list = (ListBox)sender;
string formName = "Form" + (list.SelectedIndex + 1);
///////////////////////////////////////////////////
// Следующий код взят из лабораторной работы №47
///////////////////////////////////////////////////
// Читаем коллекцию запущенных форм
FormCollection fc = Application.OpenForms;
bool IsRun = false;
// Перебираем коллекцию
foreach (Form form in fc)
{
// Имя очередной запущенной формы сравниваем с закрепленным за кнопкой
if (form.Name == formName)
{
IsRun = true; // Нашли, что форма запущена, поднимаем флаг
frm = form; // Сохраняем ссылку на запущенную форму для фокуса
break; // Выходим из цикла
}
}
// Если форма не запущена - запускаем
if (!IsRun)
{
switch (formName)
{
case "Form1":
frm = new Form1();
break;
case "Form2":
frm = new Form2();
break;
case "Form3":
frm = new Form3();
break;
case "Form4":
frm = new Form4();
break;
case "Form5":
frm = new Form5();
break;
}
this.AddOwnedForm(frm); // Сделать новую frm подчиненной Start
// frm.Owner = this; // Алтернативный способ назначить владельца
frm.ShowInTaskbar = false; // Не отображать метку окна в панели задач
frm.Show(); // Показать новую форму
}
else
frm.Focus(); // Передать фокус запущенной форме
}
Поместите
на форму Form5 компонент SplitContainer,
присвойте его экземпляру имя splitContainer и
установите свойства: Orientation = Horizontal , BorderStyle =Fixed3D, Dock =Fill
Откройте
файл Form5.cs в режиме View Code и
откорректируйте код, чтобы он стал такимusing System;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace StripControls
{
public partial class Form5 : Form
{
// Поле для сохранения выбранного элемента меню
ToolStripMenuItem itemChecked = new ToolStripMenuItem();
// Конструктор
public Form5()
{
InitializeComponent();
splitContainer.Panel1.BackColor = Color.White;// Начальный цвет верхней панели
// Разделение цветов на алфавитные группы, представленные подменю
// A, B, C, D, E, F, G, H, I, J, K, L, M,
int[] iMenu ={ 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 4,
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6 };
// N, O, P, Q, R, S, T, U, V, W, X, Y, Z
// Создание контекстного меню и присоединение к объекту формы
System.Windows.Forms.ContextMenuStrip menu =
new System.Windows.Forms.ContextMenuStrip();
// Будет вызываться для верхней панели
splitContainer.Panel1.ContextMenuStrip = menu;
// Формируем пункты подменю в контекстном меню
for (int i = 0; i <= 6; i++)
{
// Индекс первого вхождения образца соответствует коду ASCII для буквы
char first = Convert.ToChar(Array.IndexOf(iMenu, i) + 'A'); // Позиция в массиве
// Индекс последнего вхождения образца соответствует ASCII латинской буквы
char last = Convert.ToChar(Array.LastIndexOf(iMenu, i) + 'A');// Позиция в массиве
// Создали, настроили и добавили в коллекцию элемент меню
ToolStripMenuItem item = new ToolStripMenuItem();
item.Text = String.Format("Цвета от {0} до {1}", first, last);
menu.Items.Add(item);
// Включили в подменю столбец с флажками в
// дополнение к столбцу с изображениями
((ToolStripDropDownMenu)item.DropDown).ShowCheckMargin = true;
}
// Применяем рефлексию к структуре, представляющей цвета
PropertyInfo[] api = typeof(Color).GetProperties();
// Перебор цветов
foreach (PropertyInfo pi in api)
{
if (pi.CanRead pi.PropertyType == typeof(Color)
pi.Name!="Transparent")
{
// Извлекаем очередной цвет
Color color = (Color)pi.GetValue(null, null);
// Создаем очередной элемент подменю
ToolStripMenuItem item = new ToolStripMenuItem();
// По букве цвета определяем подменю, в котором разместим цвет
int i = iMenu[color.Name[0] - 'A'];// Любой букве - своя позиция
((ToolStripMenuItem)menu.Items[i]).DropDownItems.Add(item);
// Продолжаем настраивать команду
item.Text = InsertSpaceOut(color.Name);// Пробелы перед заглавными буквами цвета
item.Name = color.Name;// Сохраняем название цвета для обработчика ColorOnClick
item.Image = CreateBitmap(color);// Тонкой ссылке присваиваем толстый объект
item.Click += new EventHandler(ColorOnClick);// Регистрация обработчика
if (color.Equals(this.BackColor))// Отметить флажком текущий цвет и сохранить ссылку
(itemChecked = item).Checked = true;
}
}
}
void ColorOnClick(object sender, EventArgs e)
{
// Для выбранного устанавливаем новый флажок
// и меняем цвет клиентской области формы
ToolStripMenuItem item = (ToolStripMenuItem)sender;
itemChecked.Checked = false;// Сбрасываем старое
(itemChecked = item).Checked = true;// Устанавливаем флажок и запоминаем элемент
splitContainer.Panel1.BackColor = Color.FromName(item.Name);// Красим верхнюю панель
}
Bitmap CreateBitmap(Color color)
{
// Генерируем изображение в виде цветного прямоугольника
Bitmap bm = new Bitmap(16, 16);
Graphics gr = Graphics.FromImage(bm);
gr.FillRectangle(new SolidBrush(color), 0, 0, 16, 16);
gr.Dispose(); // Освобождаем контекст устройства
return bm;
}
string InsertSpaceOut(string str)
{
// Вставляем пробелы перед заглавными буквами названия цвета
for (int i = str.Length - 1; i > 1; i--)
if (Char.IsUpper(str[i]))
str = str.Insert(i, " ");
return str;
}
}
}
Запустите
приложение и для верхней панели сплиттера испытайте работу
контекстного менюДолжно получиться нечто, похожее показанному на рисунке с изменением выбранного цвета фона на верхней панели сплиттера

Теперь посмотрим, как присоединяются поочередно контекстные меню к одному и тому же элементу управления в зависимости от некоторых условий.
Поместите
на нижнюю панель объекта splitContainer еще
один экземпляр компонента SplitContainer и
оставьте его имя по умолчанию splitContainer1
Установите
настройки объекта splitContainer1: Dock =Fill , BorderStyle =Fixed3D
Поместите
на левую нижнюю панель splitContainer1.Panel1 три
экземпляра компонента радиокнопки RadioButton с
именами radioButton1, radioButton2, radioButton3 и
измените их свойство Text на ContextMenu1, ContextMenu2, ContextMenu1
Установите
для правой нижней панели splitContainer1.Panel2 свойство BackColor=LimeПосле проведения указанных действий интерфейс формы Form5 должен
стать таким

Создайте
три экземпляра компонента ContextMenuStrip с
именами по умолчанию: contextMenuStrip1, contextMenuStrip2, contextMenuStrip3
Последовательно
выделите каждый объект из только что добавленных контекстных
меню и создайте в нем декларативно заготовки элементов MenuItem
В результате должны получиться три объекта-заготовки контекстных меню со следующим интерфейсом
contextMenuStrip1 |
contextMenuStrip2 |
contextMenuStrip3 |
![]() |
![]() |
![]() |
Выделите
на форме Form5 все три радиокнопки одновременно
и через панель Properties в режиме Events для
события CheckedChanged создайте обработчик
с именем radioButton_CheckedChanged(),
который заполните следующим кодомprivate void radioButton_CheckedChanged(object sender, EventArgs e)
{
if (radioButton1.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip1;
else if (radioButton2.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip2;
else
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip3;
}
При смене активной радиокнопки в обработчике происходит присоединение
к правой нижней панели соответствующего объекта контекстного
меню. Вызов контекстного меню выполняется щелчком правой кнопки
мыши на правой нижней панели, представленной элементом splitContainer1.Panel2.
Для обеспечения подобного механизма работы при первом вызове
контекстного меню необходимо задействовать для этого элемента
событие BindingContextChanged.
Выделите
элемент splitContainer1.Panel2 и через панель Properties в
режиме Events создайте для события BindingContextChanged обработчик splitContainer1_Panel2_BindingContextChanged(),
который заполните такprivate void splitContainer1_Panel2_BindingContextChanged(object sender, EventArgs e)
{
if (radioButton1.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip1;
else if (radioButton2.Checked)
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip2;
else
splitContainer1.Panel2.ContextMenuStrip = contextMenuStrip3;
}
Запустите
приложение и убедитесь в работоспособности всех 4-х контекстных
меню, первое из которых создано динамически, а три остальных
- декларативно
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.