Практикум прикладного программирования на MFC и C++ в среде VS.NET

Программирование мыши и клавиатуры MFC

Разбить на страницы
Показывать лекцию целиком
Файлы к данной лабораторной работе, Вы можете скачать здесь.

Часто в приложениях нужно определить, какая кнопка мыши была нажата, где была нажата, как долго удерживалась, когда и где была отпущена.

Цель работы - изучить:

  • Какие события мыши существуют и как выбрать событие, удовлетворяющее нуждам данного приложения
  • Как в приложениях, написанных на Visual C++, отслеживать события мыши и регировать на них
  • Какие события клавиатуры существуют и какое действие инициирует каждое событие
  • Как перехватывать сообщения клавиатуры и, в зависимости от произошедшего события, выполнять определенные действия
  • События мыши

    Операционная система Windows генерирует достаточное количество сообщений о событиях, связанных с мышью, чтобы строить гибкие приложения, позволяющие решать практически любые задачи. Если выделить диалоговую форму и во вкладке Properties (Свойства) щелкнуть по кнопке Messages (Сообщения), то в прокручивающемся списке можно обнаружить следующие сообщения для мыши

    Сообщения о событиях мыши
    Сообщение Описание
    WM_LBUTTONDOWN Левая кнопка мыши нажата
    WM_LBUTTONUP Левая кнопка мыши отпущена
    WM_LBUTTONDBLCLK Двойной щелчок левой кнопкой мыши
    WM_RBUTTONDOWN Правая кнопка мыши нажата
    WM_RBUTTONUP Правая кнопка мыши отпущена
    WM_RBUTTONDBLCLK Двойной щелчок правой кнопкой мыши
    WM_MBUTTONDOWN Средняя кнопка мыши нажата (в случае трехкнопочной мыши)
    WM_MBUTTONUP Средняя кнопка мыши отпущена (в случае трехкнопочной мыши)
    WM_MBUTTONDBLCLK Двойной щелчок средней кнопкой мыши (в случае трехкнопочной мыши)
    WM_MOUSEMOVE Перемещение указателя мыши в окне приложения
    WM_MOUSEWHEEL Колесо мыши прокручивается

    Рисование с помощью мыши "точка за точкой"

  • Создайте новый проект с именем Таблица настройки мастера создания каркаса приложения Свойство проекта Значение Вкладка Application Type Application Type Dialog Based Resource language: Английский (США) Project style: MFC standard Use of MFC: Use MFC in a shared DLL Вкладка Advanced Features Все сбросить! Вкладка User Interface Features Main frame styles: System menu About box Dialog title: Mouse and Keyboard
  • Русифицируйте ресурсы узлов Dialog и String Table через панель Resource View
  • Раскройте ресурс IDD_MOUSE_DIALOG. Через меню Edit/Select All выделите и удалите все элементы управления из диалогового окна приложения

    Тем самым вся поверхность окна будет отведена для рисования. Если этого на сделать, то любому из оставшихся элементов управления, получившему фокус ввода, диалоговое окно будет передавать события клавиатуры как своему дочернему окну.

  • Выделите диалоговое окно приложения и во вкладке Properties щелкните по кнопке Messages. В открывшейся вкладке сообщений найдите сообщение WM_MOUSEMOVE и обработайте ее следующим кодом
    Обработчик сообщения WM_MOUSEMOVE файла MouseDlg.cpp
    void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	// Что сделать: Добавьте свой код обработчика сообщения
    	// и/или вызовите заданный по умолчанию
    	
    	// Выяснить, нажата ли левая кнопка мыши
    	if((nFlags  MK_LBUTTON) == MK_LBUTTON){
    		// Создать объект контекста устройства
    		CClientDC dc(this);
    		// Нарисовать точку в текущей позиции курсора
    		dc.SetPixel(point.x, 
    					point.y, 
    					RGB(0, 0, 0));
    	}
    	
    	CDialog::OnMouseMove(nFlags, point);
    }

    Контекст устройства - это некий логический экран, обеспечивающий согласованною связь с драйверами конкретных устройств вывода: физического экрана или принтера. Контекст устройства как класс имеет необходимые методы и свойства для рисования. Конструктору этого класса при создании объекта контекста устройства передается указатель this на само диалоговое окно и областью рисования будет считаться клиентская область этого диалогового окна.

    В коде применяются маски выделения флага о состоянии кнопок мыши и управляющих клавиш. Выделение происходит с помощью побитовой операции умножения.

    Маски выделения состояний кнопок и клавиш
    Маска Описание
    MK_CONTROL Нажата клавиша CTRL
    MK_LBUTTON Нажата левая кнопка мыши
    MK_MBUTTON Нажата средняя кнопка мыши (в случае трехкнопочной мыши)
    MK_RBUTTON Нажата правая кнопка мыши
    MK_SHIFT Нажата клавиша SHIFT
  • Постройте и выполните проект
  • Обратите внимание, что при быстром движении мыши линия рисования имеет разрывы. Дело в том, что при выполнении функции OnMouseMove() параметр point показывает координаты курсора мыши во время последней проверки местоположения, а сама функция выполняется не непрерывно, а по квантам времени. Между этими квантами процессор выполняет другие задачи.

    Рисование с помощью мыши "линия за линией"

    Все графические редакторы из-за перегруженности процессора рисуют непрерывные кривые соединением отдельных точек местоположения мыши отрезками прямых. Реализуем это.

  • Перейдите на вкладку Class View
  • Выберите класс диалогового окна CMouseDlg, производный от MFC -класса CDialog
  • Щелкните правой кнопкой мыши и выберите из контекстного меню Add/Add Variables...
  • В диалоговом окне
  • То же самое повторите для создания переменной m_iPrevY

    Мастер поместил объявления переменных в объявление класса CMouseDlg, создав код

    Объявления переменных-членов класса CMouseDlg файла MouseDlg.h
    // CMouseDlg dialog
    class CMouseDlg : public CDialog
    {
    .......
    	
    private:
    	// Предыдущая координата курсора
    	int m_iPrevX;
    	int m_iPrevY;
    };
  • Задайте инициализацию этих переменных в обработчике, выполняемом при каждом нажатии левой кнопки мыши. Для этого выделите диалоговое окно в визуальном редакторе диалоговых окон
  • Во вкладке Properties установите режим Messages и обработайте сообщение WM_LBUTTONDOWN кодом

    Обработчик нажатия левой кнопки мыши в начале рисования каждой новой линии
    void CMouseDlg::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	
    	// Установить текущую точку в качестве исходной
    	m_iPrevX = point.x;
    	m_iPrevY = point.y;
    	
    	CDialog::OnLButtonDown(nFlags, point);
    }
  • Измените код функции-обработчика OnMouseMove() так, чтобы рисовались не точки, а линии

    Обработчик перемещения мыши при нажатой левой кнопке
    void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	// Что сделать: Добавьте свой код обработчика сообщения
    	// и/или вызовите заданный по умолчанию
    	
    	// Выяснить, нажата ли левая кнопка мыши
    	if((nFlags  MK_LBUTTON) == MK_LBUTTON){
    		// Создать объект контекста устройства
    		CClientDC dc(this);
    	
    		// Нарисовать точку в текущей позиции курсора
    		//	dc.SetPixel(point.x, 
    		//				point.y, 
    		//				RGB(0, 0, 0));
    	
    		// Провести линию от предыдущей точки до текущей
    		dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
    		dc.LineTo(point.x, point.y); // рисовать до текущей
    	
    		// Обновить предыдущую точку для нового кванта времени
    		m_iPrevX = point.x;
    		m_iPrevY = point.y;
    	}
    	
    	CDialog::OnMouseMove(nFlags, point);
    }
  • Постройте проект и убедитесь, что получили то, что нужно
  • Измените код функции-обработчика OnMouseMove() так, чтобы линии рисовались толщиной 10 пикселов и красным цветом

    Линия в 10 пикселов красного цвета
    void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	// Что сделать: Добавьте свой код обработчика сообщения
    	// и/или вызовите заданный по умолчанию
    	
    	// Выяснить, нажата ли левая кнопка мыши
    	if((nFlags  MK_LBUTTON) == MK_LBUTTON){
    		// Создать объект контекста устройства
    		CClientDC dc(this);
    	
    		// Создать новое перо (solid, 10 pixels, red)
    		CPen newPen(PS_SOLID,
    					10,
    					RGB(255, 0, 0));
    		// Установить новое перо текущим
    		dc.SelectObject(newPen);
    		
    		// Нарисовать точку в текущей позиции курсора
    		//	dc.SetPixel(point.x, 
    		//				point.y, 
    		//				RGB(0, 0, 0));
    	
    		// Провести линию от предыдущей точки до текущей
    		dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
    		dc.LineTo(point.x, point.y); // рисовать до текущей
    	
    		// Обновить предыдущую точку для нового кванта времени
    		m_iPrevX = point.x;
    		m_iPrevY = point.y;
    	}
    	
    	CDialog::OnMouseMove(nFlags, point);
    }
  • Постройте проект и убедитесь, что результат будет, примерно, таким
  • Перехват событий клавиатуры для изменения формы курсора

    Событий для клавиатуры меньше, чем для мыши. Но существуют много действий, которые могут быть выполнены только при помощи клавиатуры.

    Сообщения о событиях клавиатуры
    Сообщение Описание
    WM_KEYDOWN Клавиша нажата
    WM_KEYUP Клавиша отпущена
    WM_SYSKEYDOWN Нажата клавиша F10 или Alt в сочетании с другой клавишей
    WM_SYSKEYUP Отпущена клавиша F10 или Alt в сочетании с другой клавишей

    Сообщения о событиях клавиатуры доступны объекту самого диалогового окна только тогда, когда ни один его дочерний элемент управления не имеет фокуса ввода.

    Сделаем так, чтобы при нажатии на определенные клавиши курсор менял свою форму по схеме

    Смена формы курсора
    Нажата клавиша Действие
    Символ "A" Загрузить курсор в виде стрелки
    Символ "B" Загрузить курсор в виде текстового указателя
    Символ "C" Загрузить курсор в виде песочных часов
    Символ "E" Выйти из приложения
  • Выделите диалоговое окно и в панели Properties обработайте сообщение WM_KEYDOWN следующим кодом

    Перехват клавиш для изменения курсора
    void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    	char ch; // текущий нажимаемый символ
    	HCURSOR hCursor = 0; // дескриптор курсора, который будет отображен
    	HCURSOR hPrevCursor = 0; // дескриптор предыдущего курсора
    	
    	// Преобразовать нажатую клавишу в символ
    	ch = char(nChar); // символ
    	bool endFlag = false; // Выход	
    	
    	switch(ch){
    		// Курсор в виде стрелки
    		case 'A': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    			break;
    			
    		// Курсор в виде текстового указателя
    		case 'B': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
    			break;
    			
    		// Курсор в форме песочных часов
    		case 'C': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
    			break;
    			
    		// Восстановить курсор в виде стрелки
    		case 'E': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    			endFlag = true;
    	}
    	
    	hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
    	if(hCursor){ // Уничтожить предыдущий курсор для освобождения ресурсов
    		DestroyCursor(hPrevCursor);
    		hCursor = 0;
    	}
    	
    	if(endFlag)
    		OnOK();
    	
    	CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
    }
  • Функция AfxGetApp() является глобальной и возвращает указатель на экземпляр класса текущего приложения. В исходном коде приложения существует глобальная переменная класса приложения theApp, но явно ссылаться на нее нельзя, а нужно пользоваться функцией AfxGetApp() всякий раз, когда нужно вызвать объект приложения.

    Фиксация формы курсора

  • Постройте проект и убедитесь, что курсор меняет форму

    Но если двинуть мышь - курсор восстанавливается. Это происходит потому, что каждый раз, когда нужно перерисовать указатель мыши, приложению направляется сообщение WM_SETCURSOR, по которому восстанавливается курсор по умолчанию. Если блокировать это сообщение, то будет перерисовываться установленный текущий курсор. Введем логическую переменную-флаг, информирующую о том, что курсор был изменен.

  • Для этого

  • Выделите класс CMouseDlg и вызовите мастер Add Member Variable Wizard
  • В диалоговом окне Add Member Variable Wizard заполните поля так, как указано на рисунке

    Получится код

    Объявление переменной-флага
    // CMouseDlg dialog
    class CMouseDlg : public CDialog
    {
    .....................
    private:
    	// Флаг, что курсор был изменен
    	bool m_bMyCursor;
    };
  • В функции начальной инициализации OnInitDialog() добавьте код
    Инициализация переменной-флага
    BOOL CMouseDlg::OnInitDialog()
    {
    .................................
    	// TODO: Add extra initialization here
    	// Инициализировать курсор по умолчанию
    	m_bMyCursor = false;
    	
    	return TRUE;  // return TRUE  unless you set the focus to a control
    }
  • Скорректируйте функцию-обработчик OnKeyDown()
    Поднимем переменную-флаг
    void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    ........................................
    	hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
    	if(hCursor){// Уничтожить предыдущий курсор для освобождения ресурсов
    		DestroyCursor(hPrevCursor);
    		hCursor = 0;
    		m_bMyCursor = true; // Курсор изменили
    	}
    	
    	if(endFlag)
    		OnOK();
    	
    	CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
    }
  • Обработайте сообщение WM_SETCURSOR так
    Обработчик сообщения WM_SETCURSOR
    BOOL CMouseDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
    {
    	// TODO: Add your message handler code here and/or call default
    	if(m_bMyCursor) 
    		return TRUE; // Выйти без выполнения нижнего кода
    	
    	return CDialog::OnSetCursor(pWnd, nHitTest, message);
    }
  • Постройте приложение и проверьте его работоспособность
  • Упражнения

  • Измените графическое приложение так, чтобы при нажатии левой кнопки мыши рисовалось красным цветом, а при нажатии правой - синим.
  • Усовершенствуйте функцию OnKeyDown, добавив в нее следующие стандартные формы курсора
  • IDC_CROSS
  • IDC_UPARROW
  • IDC_SIZEALL
  • IDC_SIZENWSE
  • IDC_SIZENESW
  • IDC_SIZEWE
  • IDC_SIZENS
  • IDC_NO
  • IDC_APPSTARTING
  • IDC_HELP
  • Страницы:
    Файлы к данной лабораторной работе, Вы можете скачать здесь.

    Часто в приложениях нужно определить, какая кнопка мыши была нажата, где была нажата, как долго удерживалась, когда и где была отпущена.

    Цель работы - изучить:

  • Какие события мыши существуют и как выбрать событие, удовлетворяющее нуждам данного приложения
  • Как в приложениях, написанных на Visual C++, отслеживать события мыши и регировать на них
  • Какие события клавиатуры существуют и какое действие инициирует каждое событие
  • Как перехватывать сообщения клавиатуры и, в зависимости от произошедшего события, выполнять определенные действия
  • События мыши

    Операционная система Windows генерирует достаточное количество сообщений о событиях, связанных с мышью, чтобы строить гибкие приложения, позволяющие решать практически любые задачи. Если выделить диалоговую форму и во вкладке Properties (Свойства) щелкнуть по кнопке Messages (Сообщения), то в прокручивающемся списке можно обнаружить следующие сообщения для мыши

    Сообщения о событиях мыши
    Сообщение Описание
    WM_LBUTTONDOWN Левая кнопка мыши нажата
    WM_LBUTTONUP Левая кнопка мыши отпущена
    WM_LBUTTONDBLCLK Двойной щелчок левой кнопкой мыши
    WM_RBUTTONDOWN Правая кнопка мыши нажата
    WM_RBUTTONUP Правая кнопка мыши отпущена
    WM_RBUTTONDBLCLK Двойной щелчок правой кнопкой мыши
    WM_MBUTTONDOWN Средняя кнопка мыши нажата (в случае трехкнопочной мыши)
    WM_MBUTTONUP Средняя кнопка мыши отпущена (в случае трехкнопочной мыши)
    WM_MBUTTONDBLCLK Двойной щелчок средней кнопкой мыши (в случае трехкнопочной мыши)
    WM_MOUSEMOVE Перемещение указателя мыши в окне приложения
    WM_MOUSEWHEEL Колесо мыши прокручивается

    Рисование с помощью мыши "точка за точкой"

  • Создайте новый проект с именем Таблица настройки мастера создания каркаса приложения Свойство проекта Значение Вкладка Application Type Application Type Dialog Based Resource language: Английский (США) Project style: MFC standard Use of MFC: Use MFC in a shared DLL Вкладка Advanced Features Все сбросить! Вкладка User Interface Features Main frame styles: System menu About box Dialog title: Mouse and Keyboard
  • Русифицируйте ресурсы узлов Dialog и String Table через панель Resource View
  • Раскройте ресурс IDD_MOUSE_DIALOG. Через меню Edit/Select All выделите и удалите все элементы управления из диалогового окна приложения

    Тем самым вся поверхность окна будет отведена для рисования. Если этого на сделать, то любому из оставшихся элементов управления, получившему фокус ввода, диалоговое окно будет передавать события клавиатуры как своему дочернему окну.

  • Выделите диалоговое окно приложения и во вкладке Properties щелкните по кнопке Messages. В открывшейся вкладке сообщений найдите сообщение WM_MOUSEMOVE и обработайте ее следующим кодом
    Обработчик сообщения WM_MOUSEMOVE файла MouseDlg.cpp
    void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	// Что сделать: Добавьте свой код обработчика сообщения
    	// и/или вызовите заданный по умолчанию
    	
    	// Выяснить, нажата ли левая кнопка мыши
    	if((nFlags  MK_LBUTTON) == MK_LBUTTON){
    		// Создать объект контекста устройства
    		CClientDC dc(this);
    		// Нарисовать точку в текущей позиции курсора
    		dc.SetPixel(point.x, 
    					point.y, 
    					RGB(0, 0, 0));
    	}
    	
    	CDialog::OnMouseMove(nFlags, point);
    }

    Контекст устройства - это некий логический экран, обеспечивающий согласованною связь с драйверами конкретных устройств вывода: физического экрана или принтера. Контекст устройства как класс имеет необходимые методы и свойства для рисования. Конструктору этого класса при создании объекта контекста устройства передается указатель this на само диалоговое окно и областью рисования будет считаться клиентская область этого диалогового окна.

    В коде применяются маски выделения флага о состоянии кнопок мыши и управляющих клавиш. Выделение происходит с помощью побитовой операции умножения.

    Маски выделения состояний кнопок и клавиш
    Маска Описание
    MK_CONTROL Нажата клавиша CTRL
    MK_LBUTTON Нажата левая кнопка мыши
    MK_MBUTTON Нажата средняя кнопка мыши (в случае трехкнопочной мыши)
    MK_RBUTTON Нажата правая кнопка мыши
    MK_SHIFT Нажата клавиша SHIFT
  • Постройте и выполните проект
  • Обратите внимание, что при быстром движении мыши линия рисования имеет разрывы. Дело в том, что при выполнении функции OnMouseMove() параметр point показывает координаты курсора мыши во время последней проверки местоположения, а сама функция выполняется не непрерывно, а по квантам времени. Между этими квантами процессор выполняет другие задачи.

    Рисование с помощью мыши "линия за линией"

    Все графические редакторы из-за перегруженности процессора рисуют непрерывные кривые соединением отдельных точек местоположения мыши отрезками прямых. Реализуем это.

  • Перейдите на вкладку Class View
  • Выберите класс диалогового окна CMouseDlg, производный от MFC -класса CDialog
  • Щелкните правой кнопкой мыши и выберите из контекстного меню Add/Add Variables...
  • В диалоговом окне
  • То же самое повторите для создания переменной m_iPrevY

    Мастер поместил объявления переменных в объявление класса CMouseDlg, создав код

    Объявления переменных-членов класса CMouseDlg файла MouseDlg.h
    // CMouseDlg dialog
    class CMouseDlg : public CDialog
    {
    .......
    	
    private:
    	// Предыдущая координата курсора
    	int m_iPrevX;
    	int m_iPrevY;
    };
  • Задайте инициализацию этих переменных в обработчике, выполняемом при каждом нажатии левой кнопки мыши. Для этого выделите диалоговое окно в визуальном редакторе диалоговых окон
  • Во вкладке Properties установите режим Messages и обработайте сообщение WM_LBUTTONDOWN кодом

    Обработчик нажатия левой кнопки мыши в начале рисования каждой новой линии
    void CMouseDlg::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	
    	// Установить текущую точку в качестве исходной
    	m_iPrevX = point.x;
    	m_iPrevY = point.y;
    	
    	CDialog::OnLButtonDown(nFlags, point);
    }
  • Измените код функции-обработчика OnMouseMove() так, чтобы рисовались не точки, а линии

    Обработчик перемещения мыши при нажатой левой кнопке
    void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	// Что сделать: Добавьте свой код обработчика сообщения
    	// и/или вызовите заданный по умолчанию
    	
    	// Выяснить, нажата ли левая кнопка мыши
    	if((nFlags  MK_LBUTTON) == MK_LBUTTON){
    		// Создать объект контекста устройства
    		CClientDC dc(this);
    	
    		// Нарисовать точку в текущей позиции курсора
    		//	dc.SetPixel(point.x, 
    		//				point.y, 
    		//				RGB(0, 0, 0));
    	
    		// Провести линию от предыдущей точки до текущей
    		dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
    		dc.LineTo(point.x, point.y); // рисовать до текущей
    	
    		// Обновить предыдущую точку для нового кванта времени
    		m_iPrevX = point.x;
    		m_iPrevY = point.y;
    	}
    	
    	CDialog::OnMouseMove(nFlags, point);
    }
  • Постройте проект и убедитесь, что получили то, что нужно
  • Измените код функции-обработчика OnMouseMove() так, чтобы линии рисовались толщиной 10 пикселов и красным цветом

    Линия в 10 пикселов красного цвета
    void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: Add your message handler code here and/or call default
    	// Что сделать: Добавьте свой код обработчика сообщения
    	// и/или вызовите заданный по умолчанию
    	
    	// Выяснить, нажата ли левая кнопка мыши
    	if((nFlags  MK_LBUTTON) == MK_LBUTTON){
    		// Создать объект контекста устройства
    		CClientDC dc(this);
    	
    		// Создать новое перо (solid, 10 pixels, red)
    		CPen newPen(PS_SOLID,
    					10,
    					RGB(255, 0, 0));
    		// Установить новое перо текущим
    		dc.SelectObject(newPen);
    		
    		// Нарисовать точку в текущей позиции курсора
    		//	dc.SetPixel(point.x, 
    		//				point.y, 
    		//				RGB(0, 0, 0));
    	
    		// Провести линию от предыдущей точки до текущей
    		dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
    		dc.LineTo(point.x, point.y); // рисовать до текущей
    	
    		// Обновить предыдущую точку для нового кванта времени
    		m_iPrevX = point.x;
    		m_iPrevY = point.y;
    	}
    	
    	CDialog::OnMouseMove(nFlags, point);
    }
  • Постройте проект и убедитесь, что результат будет, примерно, таким
  • Перехват событий клавиатуры для изменения формы курсора

    Событий для клавиатуры меньше, чем для мыши. Но существуют много действий, которые могут быть выполнены только при помощи клавиатуры.

    Сообщения о событиях клавиатуры
    Сообщение Описание
    WM_KEYDOWN Клавиша нажата
    WM_KEYUP Клавиша отпущена
    WM_SYSKEYDOWN Нажата клавиша F10 или Alt в сочетании с другой клавишей
    WM_SYSKEYUP Отпущена клавиша F10 или Alt в сочетании с другой клавишей

    Сообщения о событиях клавиатуры доступны объекту самого диалогового окна только тогда, когда ни один его дочерний элемент управления не имеет фокуса ввода.

    Сделаем так, чтобы при нажатии на определенные клавиши курсор менял свою форму по схеме

    Смена формы курсора
    Нажата клавиша Действие
    Символ "A" Загрузить курсор в виде стрелки
    Символ "B" Загрузить курсор в виде текстового указателя
    Символ "C" Загрузить курсор в виде песочных часов
    Символ "E" Выйти из приложения
  • Выделите диалоговое окно и в панели Properties обработайте сообщение WM_KEYDOWN следующим кодом

    Перехват клавиш для изменения курсора
    void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    	char ch; // текущий нажимаемый символ
    	HCURSOR hCursor = 0; // дескриптор курсора, который будет отображен
    	HCURSOR hPrevCursor = 0; // дескриптор предыдущего курсора
    	
    	// Преобразовать нажатую клавишу в символ
    	ch = char(nChar); // символ
    	bool endFlag = false; // Выход	
    	
    	switch(ch){
    		// Курсор в виде стрелки
    		case 'A': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    			break;
    			
    		// Курсор в виде текстового указателя
    		case 'B': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
    			break;
    			
    		// Курсор в форме песочных часов
    		case 'C': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
    			break;
    			
    		// Восстановить курсор в виде стрелки
    		case 'E': // латинская заглавная
    			hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    			endFlag = true;
    	}
    	
    	hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
    	if(hCursor){ // Уничтожить предыдущий курсор для освобождения ресурсов
    		DestroyCursor(hPrevCursor);
    		hCursor = 0;
    	}
    	
    	if(endFlag)
    		OnOK();
    	
    	CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
    }
  • Функция AfxGetApp() является глобальной и возвращает указатель на экземпляр класса текущего приложения. В исходном коде приложения существует глобальная переменная класса приложения theApp, но явно ссылаться на нее нельзя, а нужно пользоваться функцией AfxGetApp() всякий раз, когда нужно вызвать объект приложения.

    Фиксация формы курсора

  • Постройте проект и убедитесь, что курсор меняет форму

    Но если двинуть мышь - курсор восстанавливается. Это происходит потому, что каждый раз, когда нужно перерисовать указатель мыши, приложению направляется сообщение WM_SETCURSOR, по которому восстанавливается курсор по умолчанию. Если блокировать это сообщение, то будет перерисовываться установленный текущий курсор. Введем логическую переменную-флаг, информирующую о том, что курсор был изменен.

  • Для этого

  • Выделите класс CMouseDlg и вызовите мастер Add Member Variable Wizard
  • В диалоговом окне Add Member Variable Wizard заполните поля так, как указано на рисунке

    Получится код

    Объявление переменной-флага
    // CMouseDlg dialog
    class CMouseDlg : public CDialog
    {
    .....................
    private:
    	// Флаг, что курсор был изменен
    	bool m_bMyCursor;
    };
  • В функции начальной инициализации OnInitDialog() добавьте код
    Инициализация переменной-флага
    BOOL CMouseDlg::OnInitDialog()
    {
    .................................
    	// TODO: Add extra initialization here
    	// Инициализировать курсор по умолчанию
    	m_bMyCursor = false;
    	
    	return TRUE;  // return TRUE  unless you set the focus to a control
    }
  • Скорректируйте функцию-обработчик OnKeyDown()
    Поднимем переменную-флаг
    void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    ........................................
    	hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
    	if(hCursor){// Уничтожить предыдущий курсор для освобождения ресурсов
    		DestroyCursor(hPrevCursor);
    		hCursor = 0;
    		m_bMyCursor = true; // Курсор изменили
    	}
    	
    	if(endFlag)
    		OnOK();
    	
    	CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
    }
  • Обработайте сообщение WM_SETCURSOR так
    Обработчик сообщения WM_SETCURSOR
    BOOL CMouseDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
    {
    	// TODO: Add your message handler code here and/or call default
    	if(m_bMyCursor) 
    		return TRUE; // Выйти без выполнения нижнего кода
    	
    	return CDialog::OnSetCursor(pWnd, nHitTest, message);
    }
  • Постройте приложение и проверьте его работоспособность
  • Упражнения

  • Измените графическое приложение так, чтобы при нажатии левой кнопки мыши рисовалось красным цветом, а при нажатии правой - синим.
  • Усовершенствуйте функцию OnKeyDown, добавив в нее следующие стандартные формы курсора
  • IDC_CROSS
  • IDC_UPARROW
  • IDC_SIZEALL
  • IDC_SIZENWSE
  • IDC_SIZENESW
  • IDC_SIZEWE
  • IDC_SIZENS
  • IDC_NO
  • IDC_APPSTARTING
  • IDC_HELP
  • Вернуться к учебному плану