7. Обработка событий в приложениях класса mfc
Затем рассматривается ввод с помощью мыши. Благодаря библиотеке базовых классов Мисrosoft (МFС - Мисrosoft Faundetion Classes) времена низкоуровневого кода обработки событий мыши остались в далеком прошлом (безусловно, если у разработчика достаточно свободного времени, он может позволить себе и такую роскошь). В данном разделе описываются методы МFС, обеспечивающих контроль и манипулирование мышью, а также рассматриваются такие вопросы, как внешний вид (форма) курсора мыши, захват мыши и ограничения перемещения курсора мыши.
И в заключение рассмотрим способы, позволяющие приложению использовать ввод с клавиатуры. Применяя такие элементы пользовательского интерфейса, как поле ввода, можно предоставить Windows и МFС возможность самостоятельно выполнять все задачи по обработке ввода с клавиатуры. Однако довольно часто бывает необходим более жесткий контроль над событиями клавиатуры. В ходе обсуждения этой темы будут затронуты такие понятия, как фокус ввода с клавиатуры (Keyboard focus ), текстовый курсор (точка ввода) (keyboard kursor (insertion point )), состояние выделения (selection stste) и многие другие элементы конструкции пользовательского интерфейса, связанные с вводом с клавиатуры.
Срок управляемый событиями (event - driven) означает, что при каждом нажатии пользователем клавиши или щелчке мышью вместо кода опроса, соответствующего нажатию клавиш или щелчок мышью, операционная система передает приложению сообщения Windows (WM - windows message).
Библиотека MFC соотносит (map) сообщения Windows с функциями С, называемыми обработчиками сообщений (message handler). Операторы, обрабатывающие данные, вводимые в приложение, расположены именно в этих функциях-обработчиках сообщений.
Сообщения мыши
По аналогии с сообщениями клавиатуры сообщение мыши генерируются при ее перемещениях и при нажатии кнопок мыши. Однако, в отличие от сообщений клавиатуры, сообщение мыши передаются в любое окно, над которым находится указатель или над которым был выполнен щелчок, независимо от наличия у этого окна фокуса. Каждое окно реагирует на сообщение мыши в соответствии со своими собственными задачами.
Позиция мыши отображается на экране с помощью указателя, который по умолчанию имеет форму стрелки. Конец стрелки называется активной точкой (hotspot ), поскольку он соответствует реальной точки для стандартной
Все операции, связанные с мышью, выполняются исходя из текущих координат активной точки. Перечислим основные операции с мышью, поскольку для каждой из них генерируются свои собственные сообщения:
• Щелчок - нажатие и отпускание кнопки мыши
• Двойной щелчок - два последовательных нажатия и отпускания кнопки мыши, пауза между кото ¬ рыми не превышает заданного значения
• Перемещение - Движение мыши без нажатия на кнопки
• Перетаскивание - Движение мыши при нажатой кнопке
Эти операции определяют типы сообщений, которые генерируются Windows при соответствующих событиях, связанных с мышью. Сообщения мыши делятся на два класса: сообщение для рабочей области окна и текст служебной панели. Сообщение для рабочей области окна используются значительно чаще, поэтому им будет уделено первоочередное внимание. В табл. 8.2 приведен список
Сообщение WM_MOUSEMOVE передается в том случае, когда указатель мыши перемещается в рабочей области окна. Сообщение WM_MOUSEACTIVATE передается при щелчок над окном, которое до этого было неактивным, в результате чего окно становится активным. Сообщение WM_MOUSEHOVER и WM_MOUSELEAVE передаются в ответ на трассировку мыши при вызове функции TrackMouseEvent (). Сообщение WM_MOUSEHOVER генерируется в том случае, если в течение заданного периода времени трассировки указатель мыши не выходил за пределы указанной прямоугольной области. Обычно это со ¬ общения служит для вызова контекстных подсказок. Сообщение WM_MOUSELEAVE генерируется в том случае, если в течение заданного периода времени трассировки указатель мыши выходит за пределы рабочей области окна.
Остальные сообщения связанные с вращением колесика прокрутки и щелчками на кнопках мыши. Колесико прокрутки - это сравнительно новое усовершенствование мыши, которое впервые появилось в модели Microsoft Intcllimouse. При вращении колесика прокрутки генерируется сообщение WM_MOUSEWHEEL, содержащий информацию об угле поворота. В большинстве приложений это колесико используется как альтернатива полосах прокрутки. Иными словами, поворот колесика эквивалентен щелчку на стрелке в конце полосы прокрутки.
Сообщение WM_XBUTTONDOWN передаются в результате нажатия кнопки мыши в пределах рабочей области окна, а сообщение WM_XBUTTONUP передаются при отпускании кнопки. Сообщение WMXBUTTONDBLCLK генерируются при двойном нажатии в рабочей области окна. Все эти сообщения существуют в трех вариантах, соответствующих левой, правой и средней кнопок. Во многих типов мыши средняя кнопка отсутствует, поэтому они не могут нарушить соответствующие сообщения.
Ниже представлены объявления процедуры OnMouseMoveQ, r котором указаны параметры, которые используются большинством подпрограмм обработки мыши:
afxmsg void OnMouseMove (UINT nFlags, CPoint point);
Параметр nFlags соответствует нажатию различных виртуальных клавиш. Этот параметр может содержать произвольное сочетание следующих флагов:
• MK_LBUTTON - устанавливается при нажатии левой кнопки мыши
• М КМ В UTTON - устанавливается при нажатии средней кнопки мыши
• MK_RBUTTON - устанавливается при нажатии правой кнопки мыши
• MK_CONTROL - устанавливается при нажатии клавиши Ctrl
• MK_SHIFT - устанавливается при нажатии клавиши Shift
Второй параметр, point, содержит координаты х и у указателя мыши. Эти координаты соответствуют текущей позиции активной точки. Они отчисляются относительно левого верхнего угла окна, над которым находится указатель мыши.
Введение с помощью мыши
Как уже говорилось, Windows сообщает окна программ о действиях пользователя, посылая им сообщения, хранящиеся в аппаратной очереди ввода (input queue). Табл. 1 содержит список основных сообщений Windows для мыши, обработчики которых используются для организации реакции на действия пользователя.
Таблица 1 - Сообщение Windows для мыши
Имена сообщений начинаются с префикса WM (Windows Message ), что указывает, что это сообщение Windows.
Использование методов ClassWizard для работы с мышью Создайте проект SDI -приложения с именем mouser. Наделите ее теми же функциями работы с клавиатурой, и предварительную программу carets; иначе говоря, вводимые пользователем символы должны храниться в объекте документа St ringData.
Кроме того, добавьте методы OnKillFocus () и OnSetFocus () и включите в них вызовы HideCaret () и ShowCaret (), как это было сделано в предыдущей программе (можно скопировать необходимые фрагменты кода). Перейдем к отображению данных, вводимые пользователем.
Если пользователь щелкнул мышью в некоторой точке клиентской области, он хочет, чтобы текст выводился в указанном им месте. Мы должны воспользоваться
ClassWizard и добавить метод для обработки сообщения Windows WM_LBUTT0ND0WN;
ClassWizard присваивает ему имя OnLButtonDown ().
Запустите ClassWizard (рис. 4.3).
Проследите, чтобы в списке Class name был избран класс вида CMouserView, и найдите в списке Messages сообщение WM_LBUTT0ND0WN. Дважды щелкните на его имени; при этом создается и выводится в списке Member functions метод OnLButtonDown ().
Дважды щелкните на строке с eFO именем OnLButtonDown (), чтобы перейти к коду:
void CMouserView:: OnLButtonDown (UINT nFlags, CPoint point)
{
/ / TODO: добавьте код обработки сообщения
/ / И / или вызовите обработчик по умолчанию
CView:: 0nl_ButtonDown (nFlags, point);
}
Кроме метода OnLButtonDown (), можно использовать и другие методы для работы с мышью - OnLButtonUp (), отвечающий за обработку отпуск левой кнопки мыши, OnRButtonDownO, соответствующий нажатию правой кнопки мыши, OnLButtonDblClick (), обрабатывающий двойной щелчок левой кнопкой, и т. д.
Методы для работы с мышью перечислены в табл. 2.
Методом OnLButtonDown () передаются два параметра, nFlags и point. Первый содержит информацию о состоянии различных служебных клавиш и может принимать следующие значения:
MK_C0NTR0L нажата клавиша Ctrl
MK_LBUTTON нажата левая кнопка мыши
MK_MBUTTON нажата средняя кнопка мыши
MK_RBUTTON нажата правая кнопка мыши
MK_SHIFT нажата клавиша Shift
Параметр point, объект класса CPoint, содержит текущие координаты указателя мыши.
Итак, кнопка мыши нажата. Первым делом необходимо сохранить текущее положение указателя. Мы воспользуемся переменными х и у и присвоим им значения, полученные из переменных х и у объекта point:
void CMouserView:: OnLButtonDown (UINT nFlags, CPoint point)
{
/ / TODO: добавьте код обработки сообщения
/ / И / или вызовите обработчик по умолчанию
х = point.х;
у = point.у;
...
}
Переменные для хранения координат объявляются в заголовочном файле вида
mouserView.h:
/ / MouserView.h: интерфейс класса CMouserView
...
protected: / / создание только при сериализации
CMouserViewO;
DECLARE_DYNCREATE (CMouserView)
CPoint CaretPosition;
boolean CaretCreated;
int x, y;
...
Щелчок мышью означает, что текст будет выводиться в новом положении, - для очистки срочного объекта мы воспользуемся методом Empty () класса CString:
void CMouserView:: OnLButtonDown (UINT nFlags, CPoint point)
{
/ / TODO: добавьте код обработки сообщения
/ / И / или вызовите обработчик по умолчанию
х = point.х;
у = point.у;
CMouserDoc * pDoc = GetDocument ();
ASSERT__VALID (pDoc);
pDoc-> StringData.Empty ();
}
Остается только объявить текущее состояние вида недействительным, чтобы перерисовать его и отразить курсор в новом месте (соответствующий код был добавлен в метод OnDraw ()):
void CMouserView:: OnLButtonDown (UINT nFlags, CPoint point)
{
/ / TODO: добавьте код обработки сообщения
/ / И / или вызовите обработчик по умолчанию
х = point.х;
у = point.у;
CMouserDoc * pDoc = GetDocument ();
ASSERT_VALID (pDoc);
pDoc-> StringData.Empty ();
Invalidate ();
CView:: 0nl_ButtonDown (nFlags, point);
}
Координаты указателя мыши сохранены. Следующий шаг - вывод в месте, выбранном пользователем.
Обработка сообщений
Давайте теперь сделаем так, что бы наша программа, созданная на предыдущей лекции, обращала внимание на наши действия.
Откроем проект, созданный и сохраненный на прошлом занятии. File-> Open Workspace... -> имя файла.dsw
Создадим, например, чтобы при щелчке мышкой выскакивал MessageBox (окно сообщения).
Для этого в наш класс вставьте следующий строчки:
class CMyMainWnd: public CFrameWnd
{
public:
CMyMainWnd ()
{
Create (NULL, "My title");
}
afx_msg void OnLButtonDown (UINT, CPoint);
DECLARE_MESSAGE_MAP ()
};
Далее после класса напишите
BEGIN_MESSAGE_MAP (CMyMainWnd, CFrameWnd) ON_WM_LBUTTONDOWN () END_MESSAGE_MAP ()
И, наконец, в конце файла добавьте строки
CMyApp theApp; void CMyMainWnd:: OnLButtonDown (UINT, CPoint )
{
AfxMessageBox (" Левая кнопка мыши");
}
Откомпилируйте приложение (Ctrl F5). При нажатии левой кнопки мыши в окне должен выскочить MessageBox с надписью " Левая кнопка мыши".
Давайте теперь обсудим код. Для того, чтобы наш класс обращал внимание на наши действия, мы должны сделать следующие действия.
Первое. Мы должны вставить в конец нашего класса макрос DECLARE_MESSAGE_MAP (). Это достаточно сделать один раз. Этот макрос в классе и означает, что этот класс будет реагировать на некоторые сообщения.
Второе. Мы должны где-то после класса добавить два макроса BEGIN_MESSAGE_MAP () и END_MESSAGE_MAP (). Это тоже достаточно сделать только один раз. Это так называемая карта сообщений. В первый макрос первым параметром вы должны вставить имя вашего класса, вторым - имя родительского класса. Первый параметр показывает, для какого класса мы пишем нашу карту сообщений, а второй - кто должен обрабатывать то или иное сообщение, если наш класс не может.
Теперь мы должны написать код для конкретного сообщения. Для этого делаем следующий шаги.
Третий шаг. В классе пишем метод для обработки конкретного сообщения. Для стандартных сообщений имена методов стандартные. Образуются они так: пишем новый префикс On, после которого пишем нужное сообщение Windows без префикса WM_, причем в нем большими буквами пишем только первые буквы в каждом слове. Например, сообщение WM_ONLBUTTONDOWN превратится в OnLButtonDown. Параметры и возвращаемое значение берем из подсказок. Перед названием метода не забудем написать afx_msg. В нашем примере это afx_msg void OnLButtonDown (UINT, CPoint);
Четвертый шаг. В карту сообщений пишем макрос для нашего уведомления. В нашем примере это строка ON_WM_LBUTTONDOWN ()
BEGIN_MESSAGE_MAP (CMyMainWnd, CFrameWnd) ON_WM_LBUTTONDOWN () END_MESSAGE_MAP ()
Его имя - это ON_ плюс имя сообщения.
Пятый шаг. Пишем, что же конкретно делает наш метод. Здесь мы для примера написали
void CMyMainWnd:: OnLButtonDown (UINT, CPoint)
{
AfxMessageBox ("Левая кнопка мыши");
}
Функции с префиксом Afx определены в MFC глобально. Они не принадлежат конкретному классу.
Рисование простых элементов в окне
Давайте теперь посмотрим, как можно в нашем окне что-нибудь нарисовать. В Windows все рисование происходит на так называемом контексте устройства (Device context по - английски). При этом рисование происходит одинаково и для экрана, и для принтера, и для плоттера и т. п. Вы можете думать, что контекст устройства - это как бы полотно. На нем вы рисуете, применяя кисти, перья, шрифты и другие объекты.
Когда окна надо либо перерисовать, оно получает сообщение WM_PAINT. Для рисования нам надо написать обработчик для этого события. Для этого мы должны сделать шаги 3-5 из предыдущего вопроса. Напомним их.
Итак, вносим объявление функции в класс:
class CMyMainWnd:
public CFrameWnd
{
...
afx_msg void OnLButtonDown (UINT, CPoint);
afx_msg void OnPaint ();
DECLARE_MESSAGE_MAP ()
};
Затем добавляем макрос в карту сообщений:
BEGIN_MESSAGE_MAP (CMyMainWnd, CFrameWnd)... ON_WM_PAINT () END_MESSAGE_MAP ()
И, наконец, пишем реализацию нашей функции:
void CMyMainWnd:: OnPaint ()
{
CPaintDC * pDC = new CPaintDC (this);
pDC-> Rectangle (10,10,500,500);
pDC-> Ellipse (10,130,500,500);
pDC-> MoveTo (500,300);
pDC-> LineTo (800,130);
}
В реализации мы делаем следующее - заводим контекст устройства для рисования в строке
CPaintDC * pDC = new CPaintDC (this);
Здесь контест устройства мы создаем динамично. Слово this означает, что мы его тут же прикрепляем к нашему окна (так как сейчас мы находимся в классе CMyMainWnd). В контекста устройства много различных методов. Один из них - это рисование прямоугольника, который мы и используем. Его параметры - это координаты верхнего левого и правого нижнего углов. Есть и множество других методов - для рисования круга и эллипса
pDC - > Rectangle (10,10,500,500); / / Рисования прямоугольника
pDC - > Ellipse (10,130,500,500); / / Рисования эллипса
pDC - > MoveTo (500,300); / / Перенос начальной точки
pDC - > LineTo (800,130); / / Рисования линии
Запускаем программу. В левом углу должен появится квадратик, эллипс, линия.
КОНТРОЛЬНЫЕ ВОПРОСЫ
1. Что вы понимаете под термином автоматизация процесса создания приложения ?
2. Что такое генератор приложения ?
3. Что такое MFC AppWizard, для чего он нужен ?
4. Что такое MFC ?
5. Дайте определение API (Application Programming Interfase) ?
6. Дайте определение DLL, какие функции она выполняет?
7. Как вы понимаете определение многозадачнисть в Windows?
8. Как организован цикл сообщений в Windows?
9. Назовите основные типы данных в Windows ?
10. Назовите преимущества использования MFC ?
11. Назовите типы мастеров проектов?
12. Назовите преимущества использования мастеров проектов?
13. Что означает управляемый событиями ?
14. Перечислите основные операции с мышью ?
15. Приведите примеры сообщений win32, связанные с событиями мыши ?
16. Как осуществить ввод с помощью мыши ?
17. Какие методы для работы с мышью вы знаете?
