Шаг 166 - Диалог - добавим акселераторы

Акселераторы (accelerators) позволяют существенно упростить пользователю работу с Вашей программой за счет быстрого доступа к ее элементам. Нажатием комбинации клавиш "Ctrl + O" в Microsoft Word Вы мгновенно попадаете в диалог открытия документа, минуя потери времени на перебирание пунктов меню. Для человека, знакомого хотя бы с одной программой Windows, многие комбинации клавиш других программ уже известны, например:

Alt+XВыход из программы
Ctrl+C (Ctrl+Ins)Копирование данных в буфер обмена (clipboard)
Ctrl+X (Shift+Del)Удаление данных с копированием их в буфер обмена
Ctrl+V (Shift+Ins)Вставка данных из буфера обмена
Ctrl+Z (Alt+BackSpace)Отказ от последней операции (UNDO)

К счастью, чтобы использовать в программе акселераторы, не требуется почти никаких затрат, поскольку поддержка акселераторов встроена в операционную систему. Но, как обычно, не может быть все столь хорошо. Дело в том, что сначала таблицу акселераторов необходимо загрузить из ресурсов Вашей программы (впрочем, Вы можете создать ее в памяти программно, но сейчас об этом не будем), после чего вызывать функцию TranslateAccelerator в цикле обработки сообщений главного окна программы. Вот здесь-то и кроется засада, поскольку в модальном диалоге цикл обработки сообщений скрыт где-то с недрах операционки и в явном виде недоступен. Конечно, можно было бы создать безмодальный диалог, но это потребует дополнительных усилий программиста - например, в случае вложенных безмодальных диалогов придется изрядно попотеть, стараясь недопустить переключения между диалогом-родителем и диалогом-потомком. К счастью, программеры из одной чрезвычайно крупной компании при разработке MFC поступили весьма интересно, поскольку в MFC нет модальных диалогов в чистом виде - при использовании функции DoModal в наследнике CDialog создается типичный безмодальный диалог с циклом выборки сообщений, а "модальность" такого диалога эмулируется программно. Благодаря такому решению у нас есть доступ к циклу сообщений, а для реализации акселераторов большего нам и не надо.

За основу возьмем все тот же проект (см. Шаг 165). Переходим на закладку "ResourceView", выбираем AdvDlg resources и давим правую кнопку. В контекстном меню необходимо выбрать пункт Insert..., а в появившемся диалоге создать новый ресурс - таблицу акселераторов. Вновь созданной таблице назначим идентификатор IDR_ACCELTABLE, после чего дважды кликаем на первой строчке таблицы. В диалоге создания акселератора поле ID, как обычно, задает идентификатор сообщения, которое будет посылаться окну при нажатии заданной комбинации клавиш. Поле Key можно заполнить и вручную, но для этого необходимо, как минимум, знать принятые в Windows обозначения клавиш, поэтому мы пойдем простым путем и нажмем кнопку "Next key typed", после чего кнопку клавиатуры F12, поле Key будет заполнено автоматически кодом VK_F12. На этом создание таблицы акселераторов закончим.

166_1.gif (4481 b)

Как я уже говорил, перед использованием акселераторов они должны быть загружены в память, для чего используется функция LoadAccelerators, которая вернет нам хэндл загруженной таблицы акселераторов. Хэндл придется сохранить, поскольку он используется в качестве параметра функции TranslateAccelerator, поэтому в Visual Stidio перейдем в на закладку "ClassView" и, после правого тыка на классе основного диалога, выбираем пункт меню Add member variable... (добавить переменную - член класса). Чтобы не описывать процесс добавления, лучше я покажу картинку:

166_2.gif (2721 b)

Почему я выбрал тип доступа Protected? Правила хорошего тона C++: не надо давать доступ к членам класса больше, чем это действительно необходимо, поскольку помогает избавиться от глупой модификации переменных класса вне класса. К сожалению, от неглупой модификации это не спасает :-).

Теперь в конструкторе диалога добавляем код загрузки таблицы акселераторов:

CAdvDlgDlg::CAdvDlgDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CAdvDlgDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CAdvDlgDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	hAccel = LoadAccelerators( AfxGetResourceHandle(), MAKEINTRESOURCE( IDR_ACCELTABLE ) );
}

Несколько слов об MAKEINTRESOURCE(). Во первых, это не функция, а макрос, что вовсе не уменьшает его полезности. Дело в том, что большинство функций Win32, работающих с ресурсами, для ссылок на ресурсы используют их текстовые имена. Однако в обычной практике программирования для ресурсов используют числовые идентификаторы, поскольку это уменьшает размер программы. Макрос MAKEINTRESOURCE() как раз и позволяет выполнить преобразование числового идентификатора ресурса к символьному указателю.

А теперь нам осталось нанести тот самый завершающий штрих, после которого картина оживет... Добавляем виртуальную функцию PreTranslateMessage, которая замечательна тем, что вызывается при поступлении очередных (т.е. из очереди) сообщений, но еще до того, как вызвать стандартные обработчики. Так что запускайте ClassWizard и творите чудеса. Ну, а если есть сомнения в своих силах - смотрите картинку:

166_3.gif (5930 b)

Осталось слегка модифицировать тело PreTranslateMessage:

BOOL CAdvDlgDlg::PreTranslateMessage(MSG* pMsg) 
  {
	// TODO: Add your specialized code here and/or call the base class
	if ( TranslateAccelerator( m_hWnd, hAccel, pMsg ) ) return TRUE;
	
	return CDialog::PreTranslateMessage(pMsg);
  }

Все. Компиляция, сборка, пробный запуск (на языке акселераторов Visual Studio - F7, Ctrl+F5 :-). Вы еще не забыли, что в качестве акселератора в программе мы выбрали F12? Если нет - давите ее и смотрите за результатом...

Шаг прислал Галицкий Игорь (ig@ntvplus.ru)


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 13.04.2000