CodeNet / Платформы / Windows / MFC / Основы программирования с помощью библиотеки Microsoft Foundation Classes
Иконки и курсоры - MFC
Иконки и курсоры являются ресурсами и обычно хранятся в области ресурсов исполняемого файла. В этой главе мы рассмотрим способ установки для окна собственных иконки и курсора, который годится для динамического изменения этих элементов, когда окно уже отображено на экране. Кроме того, мы рассмотрим, как сделать диалог главным окном программы. Это очень удобно для небольших утилит. Изменять иконку и курсор для диалога труднее, чем для обычного окна. Но мы рассмотрим именно универсальный метод, который годится для всех окон. Вам рекомендуется использовать в своих программах именно этот метод.
Создание иконки и курсора
Для создания иконки и курсора нужно использовать ресурсный редактор. Иконки сохраняются в файлах с расширением ico, а курсоры - в файлах с расширением cur.
Все курсоры имеют размер 32x32. Обычно используются монохромные курсоры. Каждый курсор имеет так называемую горячую точку, по которой определяется положение курсора на экране. Она может располагаться в любом месте курсора. Для курсоров типа "указатель" это обычно вершина указателя.
Иконки могут иметь размер 16х16, 32х32 и 48х48. Последний размер обычно не используется. Иконки могут иметь 16 или 256 цветов. При использовании ресурсного редактора среды Visual C++ 4.0 и выше иконки всех размеров можно хранить в одном файле, что обычно и делается. Для современных приложений обязательно наличие иконок размером как 16х16, так и 32х32.
Загрузка иконки и курсора из ресурсов
К сожалению, на текущий момент MFC плохо поддерживает этот процесс. Поэтому иногда удобнее пользоваться функциями Windows API. Перед рассмотрением функций следует рассмотреть понятие дескриптора. Дескриптор - это 32-разрядное беззнаковое целое значение, которое идентифицирует объект в Windows. При традиционном SDK-программировании дескрипторы используются очень широко. Например, свои дескрипторы имеют иконки, курсоры и само приложение. Для дескрипторов каждого объекта существует свой тип, например, HICON, HCURSOR, HINSTANCE.
Следующее понятие, которое нам нужно рассмотреть, это класс окна. Класс окна - это структура данных в Windows, которая определяет атрибуты окна. Перед тем, как создать реальное окно, приложение регистрирует класс окна, а затем на его основании может быть создано сколько угодно окон. Обычно MFC полностью автоматизирует этот процесс. Но так как такие параметры окна, как иконка и курсор, определяются именно в классе окна, то нам придется их изменять явно.
Будем предполагать, что в декларации класса основного окна (теперь уже идет речь о классе С++) объявлены переменные m_hIconSmall, m_hIconBig, m_hCursor - соответственно дескрипторы иконки 16х16, иконки 32х32 и курсора. Тогда для загрузки иконок и курсора нужно выполнить следующий код:
// Получить дескриптор модуля (приложения) HINSTANCE hInst = AfxGetInstanceHandle(); // Загрузить иконку 16х16 m_hIconSmall = (HICON) ::LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); // Загрузить иконку 32x32 m_hIconBig = (HICON) ::LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); // Загрузить курсор m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR);Сначала мы получаем дескриптор модуля с помощью глобальной функции MFC AfxGetInstanceHandle(). Затем мы загружаем маленькую и большую иконки с помощью функции Windows API LoadImage(), и получаем их дескрипторы. Эта функция очень мощная, и позволяет загружать изображения из ресурсов и из файлов. И наконец, мы загружаем курсор уже с помощью функции MFC LoadCursor(), члена класса CWinApp. Функция AfxGetApp() возвращает адрес объекта приложения.
Изменение иконки и курсора окна
После того, как мы получили дескрипторы иконок и курсора, нужно изменить класс окна. (Класс окна нужно изменять для установки курсора, а для установки иконок подойдут и функции MFC.) Для этого используется API-функция SetClassLong(), которая изменяет атрибут класса окна (здесь опять имеется в виду структура данных). Первый параметр этой функции - дескриптор окна. Дескриптор окна хранится в члене класса MFC CWnd под названием m_hWnd. Если главным окном приложения является диалог, то для изменения иконки и курсора потребуется следующий код (он будет работоспособен и для обычных окон):
// Устанавливаем курсор для диалогового окна. // Так как MFC не поддерживает динамическое // изменение курсоров окна, // делаем это с помощью API, модифицируя оконный класс. SetClassLong(m_hWnd, GCL_HCURSOR, (long) m_hCursor); // Делаем то же самое для всех элементов управления for(int i = 0; i < 0xDFFF; ++i) { CWnd *pCtrl = this->GetDlgItem(i); if(pCtrl != 0) SetClassLong(pCtrl->m_hWnd, GCL_HCURSOR, (long) m_hCursor); } // Устанавливаем иконки. Здесь уже можно использовать MFC. this->SetIcon(m_hIconBig, TRUE); this->SetIcon(m_hIconSmall, FALSE);Сначала мы модифицируем курсор в оконном классе самого окна. Затем мы то же самое проделываем со всеми элементами управления, которые есть или могут быть в диалоговом окне (если их нет в окне, то ничего страшного не произойдет). Мы перебираем все возможные идентификаторы, и если элемент присутствует, изменяем его оконный класс. И наконец, мы изменяем большую и маленькую иконки. Здесь уже есть хорошая поддержка со стороны MFC.
Использование диалога в качестве главного окна
Как уже говорилось, это часто бывает очень удобным. Сделать это достаточно просто. Во-первых, нужно создать диалог в ресурсах. Во-вторых, нужно класс главного окна приложения породить от CDialog. Перед конструктором класса главного окна нужно вызвать конструктор класса CDialog, где привязать объект к ресурсам, например:
CMainFrame::CMainFrame() :CDialog(IDD_MYDIALOG) { ... здесь тело конструктора }И в-третьих, в функции CApp::InitInstance() должен присутствовать следующий код:
// Создаем объект диалогового окна CMainFrame dlgWnd; // Cообщаем MFC адрес окна m_pMainWnd = &dlgWnd; // Отображаем модальный диалог dlgWnd.DoModal(); // Возвратим FALSE, чтобы MFC не пыталась инициировать // очередь сообщений главного окна. return FALSE;Мы отображаем модальный диалог. Так как при завершении функции DoModal() нам уже не нужна очередь сообщений, мы "обманываем" MFC, делая вид, что инициализация прошла неудачно. На самом же деле просто уже пора завершать приложение.
Пример программы
CUR_ICO.hpp #include "STDAFX.H" // Класс диалогового окна, главного окна приложения class CMainFrame: public CDialog { public: CMainFrame(); private: enum {IDD = IDD_MAINFRAME}; BOOL OnInitDialog(); // Внутренние дескрипторы иконки и курсора HICON m_hIconSmall, m_hIconBig; HCURSOR m_hCursor; DECLARE_MESSAGE_MAP() }; // Класс приложения class CApp: public CWinApp { public: BOOL InitInstance(); }; CUR_ICO.cpp #include "STDAFX.H" #include <string.h> #include "resource.h" #include "CUR_ICO.HPP" CApp App; BOOL CApp::InitInstance() { // Создаем объект диалогового окна CMainFrame dlgWnd; // Cообщаем MFC адрес окна m_pMainWnd = &dlgWnd; // Отображаем модальный диалог int responce = dlgWnd.DoModal(); // Возвратим FALSE, чтобы MFC не пыталась инициировать // очередь сообщений главного окна. return FALSE; } BEGIN_MESSAGE_MAP(CMainFrame, CDialog) ON_WM_DESTROY() END_MESSAGE_MAP() CMainFrame::CMainFrame() :CDialog(CMainFrame::IDD) { // Загружаем из ресурсов иконку и курсор // MFC плохо поддерживает процесс динамического // изменения иконок, // поэтому используем функции Windows API HINSTANCE hInst = AfxGetInstanceHandle(); // Получить дескриптор модуля m_hIconSmall = (HICON) ::LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); //Иконка 16x16 m_hIconBig = (HICON) ::LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); //Иконка 32x32 m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR); } BOOL CMainFrame::OnInitDialog() { // Устанавливаем курсор для диалогового окна. // Так как MFC не поддерживает динамическое // изменение курсоров окна, // делаем это с помощью API, модифицируя оконный класс. SetClassLong(m_hWnd, GCL_HCURSOR, (long) m_hCursor); // Делаем то же самое для всех элементов управления for(int i = 0; i < 0xDFFF; ++i) { CWnd *pCtrl = this->GetDlgItem(i); if(pCtrl != 0) SetClassLong(pCtrl->m_hWnd, GCL_HCURSOR, (long) m_hCursor); } this->SetIcon(m_hIconBig, TRUE); this->SetIcon(m_hIconSmall, FALSE); return TRUE; }
Поэкспериментируйте с программой. Попробуйте отключить цикл, который модифицирует класс окна для элементов управления, и посмотрите, что из этого получится. Измените программу так, чтобы главным окном было обычное окно-рамка, как в предыдущих примерах.
Стандартные иконки и курсоры
Часто в программах нужно использовать курсоры и иконки, уже предопределенные в Windows. Для этого можно использовать или функцию API LoadImage(), или функции MFC LoadStandardIcon() и LoadStandardCursor(). Работа со стандартными курсорами и иконками почти ничем не отличается от работы с пользовательскими. Для стандартных иконок есть предопределенные идентификаторы с префиксом IDI_, а для стандартных курсоров - с префиксом IDC_. Например, стандартный курсор в виде "песочных часов" имеет идентификатор IDC_WAIT.