Windows Ribbon в .NET-приложениях (ч.2)
В предыдущей статье мы очень бегло рассмотрели, что представляет собой элемент управления Windows Ribbon и как он появился на свет, а также обратились к некоторым техническим подробностям написания Ариком Познански его враппера и, наконец, создали наше первое приложение с ленточным интерфейсом.
Сегодня мы начнём непосредственно изучение возможностей Windows Ribbon в приложениях WinForms. В данной статье мы научимся создавать на ленте меню приложения (Application Menu) с различными видами кнопок — простой кнопкой (Button), кнопкой типа DropDownButton (для краткости я буду называть её «выпадающей») и кнопкой типа SplitButton (назову её «разделяющейся»).
Итак, брифинг окончен, начнём.
Команды и виды
Перед тем, как мы начнём использовать возможности ленты, нам необходимо изучить основы разметки ленты.
Команда — это действие, идентифицируемое номером. Командой может быть открытие диалога «Сохранить как», печать документа, закрытие приложения и т.д. — любое действие, которое можно выполнить в вызове функции.
Представление — графическое отображение [обычно нескольких] команд. Оно назначает типы элементов управления, используемых для активации команд, их размер, порядок и расположение на экране.
Таким образом, использование команд и представлений на самом деле является просто очередным случаем применения паттерна (шаблона проектирования)
Теперь мы напишем новое WinForms-приложение с лентой, которое будет использовать меню с простыми кнопками. Мы начнём это «упражнение» с пустого проекта WinForms, уже включающего поддержку ленты (см.
- Секцию Commands (команды) разметки ленты
- Секцию Views (представления) разметки ленты
- Код, ответственный за события ленты
Общий обзор разметки
Просто напоминание. Наша простейшая разметка ленты выглядит примерно так:
<?xml version='1.0' encoding='utf-8'?>
<Application xmlns='http://schemas.microsoft.com/windows/2009/Ribbon'>
<Application.Commands>
</Application.Commands>
<Application.Views>
<Ribbon>
</Ribbon>
</Application.Views>
</Application>
Задание команд в разметке ленты
Ниже показано, как задаются некоторые команды в разметке ленты:
<Application.Commands>
<Command Name="cmdButtonNew"
Id="1001"
LabelTitle="&Создать"
LabelDescription="Создать - Описание"
TooltipTitle="Создать"
TooltipDescription="Создать новое изображение.">
<Command.LargeImages>
<Image>Res/New32.bmp</Image>
</Command.LargeImages>
<Command.SmallImages>
<Image>Res/New16.bmp</Image>
</Command.SmallImages>
</Command>
<Command Name="cmdButtonOpen"
Id="1002"
LabelTitle="Открыть"
LabelDescription="Открыть - Описание"
TooltipTitle="Открыть"
TooltipDescription="Открыть существующее изображение.">
<Command.LargeImages>
<Image>Res/Open32.bmp</Image>
</Command.LargeImages>
<Command.SmallImages>
<Image>Res/Open16.bmp</Image>
</Command.SmallImages>
</Command>
<Command Name="cmdButtonSave"
Id="1003"
LabelTitle="Сохранить"
LabelDescription="Сохранить - Описание"
TooltipTitle="Сохранить"
TooltipDescription="Сохранить текущее изображение.">
<Command.LargeImages>
<Image>Res/Save32.bmp</Image>
</Command.LargeImages>
<Command.SmallImages>
<Image>Res/Save16.bmp</Image>
</Command.SmallImages>
</Command>
<Command Name="cmdButtonExit"
Id="1004"
LabelTitle="Выход"
LabelDescription="Выход - Описание"
TooltipTitle="Выход"
TooltipDescription="Выйти из приложения.">
<Command.LargeImages>
<Image>Res/Exit32.bmp</Image>
</Command.LargeImages>
<Command.SmallImages>
<Image>Res/Exit16.bmp</Image>
</Command.SmallImages>
</Command>
</Application.Commands>
mallImages>
<Image>Res/Exit16.bmp</Image>
</Command.SmallImages>
</Command>
</Application.Commands>[/code]
Пояснение: здесь мы задаём 4 разные команды. Каждая команда имеет свойства, назначенные либо в XML-атрибутах, либо в дочерних элементах. Мы используем следующие (полный список доступен в статье «
- Name — это имя впоследствии будет использоваться в секции представлений для обращения к этой команде
- Id — это идентификатор (ID) команды. Мы используем его в коде, когда команда вызывает событие
- LabelTitle — заголовок метки команды
- LabelDescription — описание метки команды
- TooltipTitle — заголовок всплывающей подсказки (англ. tooltip — прим. переводчика) команды
- TooltipDescription — описание всплывающей подсказки команды
- LargeImages — имя файла с большим изображением для команды, обычно 32x32 точек
- SmallImages — имя файла с маленьким изображением для команды, обычно 16x16 точек
Задание «горячих клавиш» элементам меню
Задание «горячей клавиши» элементу меню производится добавлением текста «&» в LabelTitle перед буквой, которая должна быть «горячей клавишей» (подобно таковым в «старой» системе меню), примером является LabelTitle команды «Создать».
Несколько комментариев касательно ресурсов с изображениями в разметке ленты
Имя файла, указанное в разметке (как в элементах LargeImages и SmallImages), должно быть достоверным (относительным или полным) путём к файлу, иначе компилятор ресурсов (rc.exe) выдаст ошибку компиляции: «error RC2135: file not found: <filename>» (англ. «файл не найден» — прим. переводчика).
Файл с изображением должен быть точечным рисунком (BMP) с форматом пикселей 32 BPP (бит/точку) ARGB. Многие программы редактирования изображений, например, Microsoft Paint, не сохраняют 8-битовый альфа-канал высшего порядка, то есть создают только 24-разрядные изображения, и в результате изображение вообще не появляется на ленте. Средство
В каждом «Images»-элементе можно указать несколько файлов с изображениями различных размеров, фреймворк ленты выберет наиболее подходящий согласно текущей настройке DPI. Для нас, обычных пользователей, двух изображений 32x32 и 16x16 должно быть достаточно. Подробнее можно прочитать в статье "
Задание представлений в разметке ленты
Ниже показано, как задаются представления в нашей разметке ленты:
[code]<Application.Views>
<Ribbon>
<Ribbon.ApplicationMenu>
<ApplicationMenu>
<MenuGroup>
<Button CommandName='cmdButtonNew' />
<Button CommandName='cmdButtonOpen' />
<Button CommandName='cmdButtonSave' />
</MenuGroup>
<MenuGroup>
<Button CommandName='cmdButtonExit' />
</MenuGroup>
</ApplicationMenu>
</Ribbon.ApplicationMenu>
</Ribbon>
</Application.Views>[/code]
Пояснение: здесь мы задаём меню приложения (Application Menu), содержащее две группы меню (Menu Group) и 4 кнопки (Button). Атрибут кнопки «CommandName» указывает на команду, которую должна запускать кнопка при нажатии.
Обработка событий ленты
Теперь мы посмотрим, как обработать событие нажатия одной из наших кнопок меню.
Следующий код должен находиться в файле нашей главной формы (form1.cs в моём примере):
[code]public enum RibbonMarkupCommands : uint
{
cmdApplicationMenu = 1000,
cmdButtonNew = 1001,
cmdButtonOpen = 1002,
cmdButtonSave = 1003,
cmdButtonExit = 1004,
}[/code]
Это лишь вспомогательное перечисление, чтобы сделать код более удобочитаемым. Каждый идентификатор команды получает читабельный символ.
[code]private Ribbon _ribbon;
private RibbonButton _buttonNew;
public Form1()
{
InitializeComponent();
_ribbon = new Ribbon();
_buttonNew = new RibbonButton(_ribbon, (uint)RibbonMarkupCommands.cmdButtonNew);
_buttonNew.OnExecute += new OnExecuteEventHandler(_buttonNew_OnExecute);
}
void _buttonNew_OnExecute(PropertyKeyRef key, PropVariantRef currentValue, IUISimplePropertySet commandExecutionProperties)
{
MessageBox.Show("нажата новая кнопка");
}[/code]
Разумеется, в начало файла мы добавили следующее:
[code]using RibbonLib;
using RibbonLib.Controls;
using RibbonLib.Controls.Events;
using RibbonLib.Interop;[/code]
Итак, вот оно, приложение WinForms с меню приложения на ленте.
Меню приложения с SplitButton и DropButton
Теперь я покажу вам, как использовать следующие возможности ленты в WinForms-приложении:
- Группа меню (Menu Group)
- Разделяющаяся кнопка (Split Button) в меню приложения
- Выпадающая кнопка (Drop Down Button) в меню приложения
Результат будет похож на следующий:
SplitButton против DropDownButton
Какая же на самом деле между ними разница?
DropDownButton НЕ является кнопкой, то есть нажатие на неё ничего не делает. Если задержать на ней указатель мыши, откроется список кнопок.
Что касается SplitButton, она является кнопкой, которая откликается на нажатие. Если задержать на ней курсор, также откроется список кнопок.
Общее применение DropDownButton — когда вы хотите вывести список элементов, у которых нет конкретной опции «по умолчанию». Пример — функция «Повернуть» в Paint. У вас есть опции «Повернуть на 90 градусов», «Повернуть на 180 градусов» и «Повернуть на 270 градусов», но ни одна из них не является очевидным умолчанием.
Общее применение же SplitButton — когда нужно вывести список элементов, у которых есть конкретное умолчание. Пример — кнопка «Сохранить как», где есть стандартный формат файла.
Использование SplitButton и DropDownButton в меню приложения на ленте
Секция команд разметки остаётся прежней, нужно только задать команды, которые будут впоследствии перечислены в секции представлений. Например:
[code]<Command Name="cmdButtonDropA"
Id="1008"
LabelTitle="Выпад A"
LabelDescription="Подкнопка A"
TooltipTitle="Выпад A">
<Command.LargeImages>
<Image>Res/DropA32.bmp</Image>
</Command.LargeImages>
</Command>
<Command Name="cmdButtonDropB"
Id="1009"
LabelTitle="Выпад B"
LabelDescription="Подкнопка B"
TooltipTitle="Выпад B">
<Command.LargeImages>
<Image>Res/DropB32.bmp</Image>
</Command.LargeImages>
</Command>[/code]
Соответствующая разметка представлений выглядит так:
[code]<DropDownButton CommandName='cmdDropDownButton'>
<MenuGroup Class='MajorItems'>
<Button CommandName='cmdButtonDropA' />
<Button CommandName='cmdButtonDropB' />
<Button CommandName='cmdButtonDropC' />
</MenuGroup>
</DropDownButton>
<SplitButton>
<SplitButton.ButtonItem>
<Button CommandName='cmdButtonDropB' />
</SplitButton.ButtonItem>
<SplitButton.MenuGroups>
<MenuGroup Class='MajorItems'>
<Button CommandName='cmdButtonDropA' />
<Button CommandName='cmdButtonDropB' />
<Button CommandName='cmdButtonDropC' />
</MenuGroup>
</SplitButton.MenuGroups>
</SplitButton>[/code]
Сопутствующий код также похож на предыдущий.
MenuGroup
Группа меню (Menu Group) — это коллекция элементов меню внутри меню приложения. Наиболее полезная её возможность — задание заголовка для группы элементов, как, например, «Меню Файл» на последнем изображении.
Если вам нужен простой разделитель между элементами меню, используйте MenuGroup без привязки к какой-либо команде.
[code]<MenuGroup>
…
</MenuGroup>[/code]
Замечание касаемо метода DestroyFramework
Теперь я хотел бы сделать то самое замечание касаемо метода DestroyFramework, о котором говорил в заключении предыдущей статьи. Оно также будет являться переводом одного из сообщений Арика Познански в его блоге. Итак, замечание.
Ошибка при закрытии приложения с Windows Ribbon Framework
Поскольку ко мне уже третий раз
Описание ошибки
Вы добавляете на ленту кнопку, которая закрывает приложение. Приложение выдаёт ошибку на выходе.
Не пилите сук, на котором сидите
Проблема в том, что вы пытаетесь вызвать метод ribbon.DestroyFramework, который в итоге вызывает IUIFramework.Destroy из обработчика команд ленты. То есть вы пытаетесь «убить» ленту в то время как обрабатывается её событие. Справедливо, что элемент управления ленты «наносит ответный удар».
Решение
Либо внедряйте метод Close() асинхронно:
[code]void _exitButton_OnExecute(
PropertyKeyRef key,
PropVariantRef currentValue,
IUISimplePropertySet commandExecutionProperties)
{
// Закрывайте форму асинхронно, поскольку мы находимся в
// обработчике команд ленты, т.е. лента всё ещё используется
// и вызов Close тут же вызовет _ribbon.DestroyFramework(),
// чего делать никак нельзя, если лента ещё используется.
this.BeginInvoke(new MethodInvoker(this.Close));
}[/code]
Либо не вызывайте DestroyFramework во время выхода из приложения (и доверьте Windows освобождение ресурсов).
Заключение
На этом пока всё. Единственное авторское отступление сегодня — обязательно смотрите примеры, включённые в «комплект поставки» библиотеки Windows Ribbon for WinForms, поскольку, как можно заметить, Арик старается наиболее кратко описывать простые для подготовленного программиста вещи.
В следующей статье мы рассмотрим, как создавать на ленте вкладки с группами и кнопку справки, а также воспользуемся такими элементами управления, как счётчик (Spinner) и раскрывающийся список (ComboBox).
Кстати, пока писалась эта статья, Арик выпустил обновление своей библиотеки Windows Ribbon for WinForms, на данный момент последней версией является 2.3. Рекомендуется пользоваться ей.
Август 2010
Комментарии
Исходные сообщения в блоге Арика Познански, переведённые в этой статье:
Также вчера Арик выпустил версию 2.4 своей библиотеки
По теме
- Видео-курсы от MS: Разработка приложений виртуальной и расширенной реальности на платформе Microsoft
- ECMA JavaScript 6: Объекты
- Как просто создать приложение для iPhone
- Разработка приложений под Windows Phone 8
- Индексированное хранилище, часть 2
- Индексированное хранилище, часть 1
- Начала Metro-программирования: создание настраиваемых приложений (ч.2)
- Начала Metro-программирования: создание настраиваемых приложений (ч.1)
- Начала Metro-программирования: создание компонентов WinRT (ч.3)
- Начала Metro-программирования: создание компонентов WinRT (ч.2)