
Лекции / seconda
.htmlИспользование Microsoft Visual 2008. Часть 2 Использование Microsoft Visual 2008. Часть 2
С помощью MFC Application Wizard создайте однодокументное приложение, для отображения документа выберите класс CView (см. пункты 1-5 части 1). Создайте меню и панель инструментов с опциями и кнопками для создания соответствующей фигуры и удаления всех фигур (см. пункты 24-27 и 36-37 части 1). Создайте функции, которые будут выполняться при выборе этих пунктов меню (см. пункт 27 части 1). Для создания новой фигуры, прежде всего, необходимо вызвать диалог, в котором пользователь может задать параметры фигуры. Создайте диалог для задания местоположения и цвета фигуры (см. пункты 28-35 части 1). Цвет фигуры нужно задавать числом, которое затем будет использоваться в функциях рисования. Чтобы не задумываться, какое число представляет какой цвет, можно использовать стандартный диалог для выбора цвета. Создайте на диалоге кнопку выбора цвета. Отредактируйте функцию, вызываемую по нажатию этой кнопки (чтобы создать эту функцию, можно дважды щелкнуть по ней мышью): void CNewCircle::OnButtonColor() { CColorDialog cd;
// Переменная для стандартного диалога выбора цвета // TODO: Add your control notification handler code here if (cd.DoModal() == IDCANCEL) return; UpdateData(1); m_Colour = cd.GetColor(); // Функция GetColor возвращает числовое представление выбранного цвета UpdateData(0); } Можно сделать так, чтобы на кнопке вместо текста было некоторый рисунок.
В свойствах кнопки установите значение TRUE для свойства Bitmap. Добавьте переменную категории Control для кнопки. В ресурсы добавьте объект Bitmap и нарисуйте на нём подходящее изображение. В класс для диалога добавьте переменную bitmap типа CBitmap. Отредактируйте функцию OnInitDialog: BOOL CNewCircle::OnInitDialog() { CDialog::OnInitDialog();
// TODO: Add extra initialization here bitmap.LoadBitmap(IDB_COLOUR); // IDB_COLOUR – идентификатор объекта Bitmap m_ButtonColour.SetBitmap(HBITMAP(bitmap));
// m_ButtonColour – переменная кнопки return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } Далее вам необходимо создать класс для представления одной фигуры и список объектов этого класса. Для того чтобы нарисовать фигуры на экране, необходимо использовать функцию класса CSecondProgramView OnDraw. Эта функция имеет один параметр – указатель на объект класса CDC. Класс CDC используется для работы с так называемым контекстом устройства. Для экрана контекст устройства представляет собой, говоря упрощённо, копию части экрана. Все функции рисования получают этот указатель и выводят изображение именно в контекст устройства, и затем при необходимости изображение копируется на экран. Функция OnDraw вызывается автоматически при восстановлении приложения, перемещении и т.д. Если вы хотите изменить изображение на экране, например, при появлении новой фигуры, изменении цвета фигуры, перемещении или удалении фигуры, вам также необходимо вызывать эту функцию. Однако она вызывается не напрямую, а с помощью функции InvalidateRect. Функция InvalidateRect имеет один параметр – указатель на объект класса CRect. Класс CRect используется для представления прямоугольника и содержит четыре целых числа, представляющие собой координаты верхнего левого и правого нижнего угла прямоугольника. Если параметр функции InvalidateRect равен NULL, перерисовывается всё окно. Если параметр этой функции не равен NULL, перерисовывается только часть окна, заданная параметром функции. Обычно не стоит вызывать функцию InvalidateRect с параметром, равным NULL, т.к. в этом случае экран будет подмигивать, особенно при многократной перерисовке, когда вы, например, перемещаете фигуру. Для рисования различных фигур используются такие функции, как Ellipse, Polygon, Rectangle и т.д. Однако прежде чем что-то рисовать, необходимо выбрать «карандаш» и «кисть». «Карандаш» используется для рисования контура фигуры, а «кисть» – для заполнения фигуры. CPen pen; // Переменная «карандаш» CBrush brush; // Переменная «кисть» brush.CreateSolidBrush(colour); // Создаём сплошную кисть. Здесь colour – переменная, содержащая цвет рисуемой фигуры pen.CreatePen(PS_SOLID, 1, colour); // Создаём сплошной карандаш толщиной 1 pDC->SelectObject(&pen); // Выбираем карандаш и кисть для рисования pDC->SelectObject(&brush); // pDC – указатель на объект класса CDC pDC->Ellipse(x - radius, y + radius, x + radius, y - radius); // Рисуем круг, используя выбранные инструменты Добавьте в функцию OnDraw операторы для рисования всех фигур из списка. Если вы создали класс для представления фигуры, то функция рисования одной фигуры должна принадлежать этому классу. Ей необходимо передать параметр функции OnDraw. В функции OnDraw тогда необходимо вызвать функцию рисования для каждого объекта класса. После вызова диалога создания фигуры добавьте вызов функции InvalidateRect. Добавляйте вызов функции InvalidateRect каждый раз, когда необходимо изменить изображение на экране. Далее необходимо запрограммировать перемещение и изменение размера фигуры с помощью мыши. В дереве классов выделите класс CSecondProgramView, откройте окно свойств этого класса. В панели инструментов этого окна нажмите третью слева кнопку (Messages), найдите сообщение WM_MOUSEMOVE и добавьте функцию, которая будет вызываться при получении приложением данного сообщения. Эта функция получает два параметра – переменную nFlags типа UINT, которая содержит флаги, определяющие, какие клавиши на клавиатуре и на мыши были нажаты во время перемещения мыши, и переменную point типа CPoint, которая содержит координаты точки, куда была передвинута мышь. Поскольку передаются координаты только одной точки, необходимо каждый раз после обработки сообщения сохранять координаты точки, чтобы следующий раз использовать её, как точку, откуда была передвинута мышь, чтобы определить расстояние. Кроме того, при нажатии клавиши мыши также необходимо запоминать точку, чтобы затем использовать её при обработке сообщения WM_MOUSEMOVE. Добавьте в класс CSecondProgramView функцию для обработки сообщения WM_MOUSEMOVE и отредактируйте её: void CSecondProgramView::OnMouseMove(UINT nFlags, CPoint point) { CRect rect;
// TODO: Add your message handler code here and/or call default
// Выражение nFlags & MK_LBUTTON проверяет, что во время передвижения мыши была нажата левая клавиша мыши.
// Это надо именно с помощью поразрядной операции '&'!
// circle – это указатель на класс Circle для представления фигуры. Если он не равен NULL, это означает, что левая клавиша мыши была нажата,
// когда курсор находился внутри некоторой фигуры, и определяет фигуру, с которой надо работать. Этот указатель устанавливается в функциях,
// которые обрабатывают сообщения WM_LBUTTONDOWN – нажатие левой клавиши мыши и WM_RBUTTONDOWN – нажатие правой кнопки мыши if (nFlags & MK_LBUTTON && circle) { circle->Move(oldPoint, point, &rect); // Перемещаем фигуру. oldPoint – переменная, которая содержит предыдущую точку.
// Функция также устанавливает переменную rect, которая определяет координаты части окна,
// которую надо обновить InvalidateRect(&rect); oldPoint = point; // Запоминаем текущую точку } // Аналогично для случая, когда нажата правая кнопка мыши else if (nFlags & MK_RBUTTON && circle) { circle->Resize(oldPoint, point, &rect); InvalidateRect(&rect); resize = 1; // Запоминаем, что было изменение размера фигуры, чтобы в этом случае
// не появлялось всплывающее меню при отпускании кнопки мыши oldPoint = point; }
CView::OnMouseMove(nFlags, point); } В данный момент написанная функция ещё неработоспособна, т.к. не определены переменная circle – указатель на фигуру, где была нажата клавиша мыши, и переменная oldPoint. Добавьте в класс CSecondProgramView функции для обработки сообщений WM_LBUTTONDOWN и WM_RBUTTONDOWN – эти сообщения посылаются приложению, когда были нажаты (но не отпущены!) левая или правая кнопки мыши соответственно. Эти функции должны проверять, попадает ли точка, в которой находился курсор, когда была нажата клавиша мыши, в какую-либо фигуру, устанавливать эту фигуру как рабочую, занося указатель на неё в переменную circle, и запоминать точку, в которой была нажата клавиша мыши. Чтобы при нажатии правой кнопки мыши появлялось всплывающее меню, необходимо обрабатывать сообщение WM_RBUTTONUP. Добавьте в класс CSecondProgramView соответствующую функцию и отредактируйте её код: void CSecondProgramView::OnRButtonUp(UINT nFlags, CPoint point) { CMenu menu;
// TODO: Add your message handler code here and/or call default if (resize) // Если было изменение размера фигуры (см. функцию OnMouseMove), { resize = 0; return; } // то создавать меню не надо . . . // Проверяем, что попали в какую-либо фигуру if (circle) // Если попали { . . . // Создание и обработка меню (см. пункты 41-44 части 1) для фигуры,
// которое должно содержать пункты для изменения цвета фигуры, перемещения,
// изменения размера и удаления фигуры } else // Если не попали { . . . // Создание и обработка общего меню, которое должно содержать пункты «Создание новой
// фигуры» и «Удаление всех фигур» }
CView::OnRButtonUp(nFlags, point); } Добавьте в класс CSecondProgramView функцию OnCommand и отредактируйте её код: BOOL CSecondProgramView::OnCommand(WPARAM wParam, LPARAM lParam) { CColorDialog cd;
// TODO: Add your specialized code here and/or call the base class switch (LOWORD(wParam)) { case ID_CHANGE_COLOUR: // Если надо изменить цвет if (cd.DoModal() == IDOK) // Используем стандартный диалог выбора цвета { circle->SetColour(cd.GetColor()); InvalidateRect(NULL); } break; case ID_MOVE: // Это ещё не перемещение или изменение размера фигуры, move = 1; // а только переход в соответствующий режим. break; // Реально перемещение и изменение размера case ID_RESIZE: // будут производиться при нажатии клавиш на клавиатуре в функции, resize = 1; // которая обрабатывает соответствующее сообщение break; case ID_DELETE: // Если надо удалить фигуру if (MessageBox("Are you sure?", "Deleting a circle", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES) { . . . // Удаление InvalidateRect(NULL); } break; case ID_CREATE: // Если надо создать новую фигур . . . // Создание новой фигуры break; case ID_DELETE_ALL: // Если надо удалить все фигуры if (MessageBox("Are you sure that you want to delete all circles?", "Deleting circles", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES) { . . . // Удаление всех фигур InvalidateRect(NULL); } break; }
return CView::OnCommand(wParam, lParam); } Теперь осталось только написать функцию, которая будет перемещать фигуру или изменять её размер при нажатии клавиш на клавиатуре. Прежде всего, определите константы для кодов клавиш:
#define KEYLEFT 37
#define KEYUP 38
#define KEYRIGHT 39
#define KEYDOWN 40
#define ENTER 13
Существует два сообщения, передаваемых приложению при нажатии клавиш – WM_KEYUP и WM_KEYDOWN. Функции, обрабатывающие эти сообщения, получают три параметра – переменную nChar типа UINT, которая содержит код клавиши, переменную nRepCnt типа UINT, которая определяет количество повторений, если пользователь нажимает и не отпускает клавишу, и переменную nFlags типа UINT, которая содержит флаги. Однако для функции, которая обрабатывает сообщение WM_KEYUP, переменная nRepCnt всегда равна 1. Поэтому лучше использовать функцию, обрабатывающую сообщение WM_KEYDOWN, чтобы при удерживании клавиши происходило перемещение фигуры или изменение её размера. Добавьте в класс CSecondProgramView соответствующую функцию и отредактируйте её код: void CSecondProgramView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { CRect rect;
// TODO: Add your message handler code here and/or call default if (!circle) return; if (move) // Если надо переместить (см. функцию OnCommand) { switch (nChar) { case KEYLEFT:
circle->Move(-nRepCnt, 0, &rect);
InvalidateRect(&rect);
break; // В классе Circle существуют две функции с именем Move, но с разным набором параметров case KEYUP:
circle->Move( 0, -nRepCnt, &rect);
InvalidateRect(&rect);
break;
case KEYRIGHT:
circle->Move( nRepCnt, 0, &rect);
InvalidateRect(&rect);
break;
case KEYDOWN:
circle->Move( 0, nRepCnt, &rect);
InvalidateRect(&rect);
break;
case ENTER:
move = 0;
break;
}
// Выход из режима перемещения } else if (resize) // Если надо изменить размер (см. функцию OnCommand) { switch (nChar) { case KEYLEFT:
case KEYUP:
circle->Resize(-nRepCnt, &rect);
InvalidateRect(&rect);
break;
// При нажатии этих клавиш действия одинаковы
// В классе Circle также существуют две функции с именем Resize,
// но с разным набором параметров case KEYRIGHT:
case KEYDOWN:
circle->Resize(nRepCnt, &rect);
InvalidateRect(&rect);
break;
// При нажатии этих клавиш действия также одинаковы case ENTER:
resize = 0;
break;
}
// Выход из режима изменения размера }
CView::OnKeyDown(nChar, nRepCnt, nFlags); }
Файл в формате Word
Содержание