Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 1000 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 4:

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

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

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

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

  • То же самое повторите для создания переменной 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);
    }
  • Постройте приложение и проверьте его работоспособность

Упражнения

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

Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .