Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

VisualC_Studio / Lab / lb_05

.htm
Скачиваний:
6
Добавлен:
21.03.2015
Размер:
122.2 Кб
Скачать

Круглински Д Лабораторная работа № 5

 

Тема: Строка состояния. Перемещение мышью геометрических объектов. Устранение мерцания. Контекстное меню. Стандартное диалоговое окно класса CColorDialog.

 

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

 

Создаем новый проект с помощью редактора AppWizard

Создайте новый проект с именем MyPr05:

File > New > Project.

В качестве типа проекта Project types выберите MFC, в качестве Templates – MFC Application, укажите в разделе Location (Местоположение) свой директорий, заполните имя проекта Name: MyPr05 и щелкните ОК.

 

Затем

перелистните первую страницу, выбрав кнопку Next;

на странице Application type выберите Single document (однооконное приложение) и нажмите кнопку Finish.

 

Так как на странице Generated Classes  для класса с именем CMyPr05View мы не изменили базовый класс, AppWizard в качестве базового класса по умолчанию выбрал CView.

 

В свойствах проекта смените кодировку (см. лабораторную работу № 1).

 

Меняем информацию в строке состояния

            Окно строки состояния не принимает ввод от пользователя и не генерирует командных сообщений. Его задача  - просто показывать текст в соответствующих секциях. Заменим стандартную строку состояния

 

 

своей; пусть в ней отображаются текущие координаты курсора.

 

1.      Выполните цепочку команд:

Перейдите на вкладку Resource View окна проектов, раскройте список MyPr05.rs и щёлкните правой кнопкой мыши в любом месте этой вкладки. В открывшемся контекстном меню выберите команду  Resource Symbols.

Перед Вами открылось одноимённое окно.

Нажмите кнопку New.

В поле Name открывшегося окна задайте для строки состояния новый идентификатор ID_MY_STATUS_BAR и оставьте для него значение Value, предлагаемое по умолчанию. Нажмите OK.

Закройте окно Resource Symbols и сохраните файл ресурсов на диске.

 

            2. В заголовочном файле MainFrm.h перенесите объявление переменных m_wndStatusBar и m_wndToolBar из раздела protected (защищённая) в раздел public (открытая).

 

            3. Отредактируйте файл MainFrm.cpp.

            3.1 Замените старое содержимое массива indicators новым:

static UINT indicators[] =

{

            ID_SEPARATOR,      // секция № 0 строки сообщений (пустая)

            ID_SEPARATOR,      // секция № 1 строки сообщений (для вывода x)

            ID_SEPARATOR,      // секция № 2 строки сообщений (для вывода y)

};

            3.2 В функции OnCreate замените строку

if (!m_wndStatusBar.Create(this) ||

на строки

if (!m_wndStatusBar.Create(this,

                        WS_CHILD | WS_VISIBLE | CBRS_BOTTOM | ID_MY_STATUS_BAR) ||

           

3.3 После этого оператора if вставьте строки:

m_wndStatusBar.SetPaneInfo(0, 0,SBPS_STRETCH, 50);    // Секцию № 0 растянуть

m_wndStatusBar.SetPaneInfo(1, 0,0, 50);       // На секцию № 1 отвести 50 позиций

m_wndStatusBar.SetPaneInfo(2, 0,0, 50);       // На секцию № 2 отвести 50 позиций

 

Пояснения. Функция SetPaneInfo(int nIndex, UINT nID, UINT nStyle, int cxWidth) позволяет задать внешний вид каждой секции в строке состояния. Она имеет следующие аргументы: nIndex - № секции,  nID - № идентификатора,  nStyle - стиль секции (возможные стили см. в файле *\CPLUSPLUS\VC98\MFC\SRC\BARSTAT.CPP), cxWidth - ширина секции.

            4. Теперь необходимо связать новую строку состояния с уже имеющейся в пункте меню View командой StatusBar (её идентификатор ID_VIEW_STATUS_BAR).

 

            4.1. Создадим обработчик сообщения COMMAND.

            Откройте окно редактора ресурсов: вкладка ResourceView > список Menu > дважды щелкнуть левой кнопкой мыши IDR_MAINFRAME.

 

Щёлкните правой кнопкой мыши ячейку Status Bar (команда раздела View), a и в открывшемся контекстном меню выберите команду Add Event Handler (Добавить Событие).

Перед Вами открылось окно Event Handler Wizard. В списке Class List выберите класс CMainFrame. Убедитесь, что в списке Message type указано сообщение COMMAND и нажмите кнопку Add and Edit (Добавить и Редактировать).

            В текст функции OnViewStatusBar добавьте оператор:

 

m_wndStatusBar.ShowWindow((m_wndStatusBar.GetStyle() & WS_VISIBLE) == 0);

 

            4.2. Создайте обработчик сообщения UPDATE_COMMAND_UI (аналогично предыдущему пункту, только в списке Message type надо выбрать сообщение UPDATE_COMMAND_UI).

            В текст функции OnUpdateViewStatusBar добавьте оператор:

 

pCmdUI->SetCheck((m_wndStatusBar.GetStyle() & WS_VISIBLE) != 0);

 

            Пояснение. Функция SetCheck включает «галочку» слева от команды StatusBar меню View.

 

            5. Откройте файл MyPr05View.cpp и замените текст функции OnDraw оператором:

 

pDC->TextOut(10,10, "Перемещая мышь, следите за строкой состояния");

 

            6. В заголовке

void CMyPr05View::OnDraw(CDC* pDC)

процедуры OnDraw снимите комментарии с объявления переменной pDC.

 

            7. Создадим обработчик сообщения WM_MOUSEMOVE.

Откройте вкладку Class View окна проектов. В списке классов выделите CMyPr05View и откройте окно его свойств (правая кнопка мыши). В окне свойств перейдите на вкладку сообщений и выберите там сообщение WM_MOUSEMOVE.

            Текст функции OnMouseMove замените на:

 

CString str;

CMainFrame* pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;

CStatusBar* pStatus = &pFrame->m_wndStatusBar;

if (pStatus)

{

            str.Format("Координаты курсора");

            pStatus->SetPaneText(0, str);  // Обновляем секцию № 0

            str.Format("x = %d", point.x);

            pStatus->SetPaneText(1, str);  // Обновляем секцию № 1

            str.Format("y = %d", point.y);

            pStatus->SetPaneText(2, str);  // Обновляем секцию № 2

}

 

            8. В файл MyPr05View.cpp вставьте оператор include:

#include "MainFrm.h"

 

            9. Соберите своё приложение и запустите его на выполнение.

            Внимание!!! Возможно, Вы увидите следующее сообщение об ошибке:

 

           

            В этом случае замените тип соответствующей переменной со CStatusBar на CMFCStatusBar, а в появившемся при запуске окне с предупреждением дважды щёлкните кнопку «Пропустить».

 

Перемещая курсор мышью, убедитесь, что координаты курсора отображаются в строке состояния.

 

 

 

 

Меняем положение рисунка с помощью мыши

            Нарисуем эллипс и «научим» мышь перетаскивать этот эллипс с места на место. Вы уже знаете, что эллипс можно нарисовать, задавая четыре координаты.

            Можно поступить по-другому, нарисовав эллипс с помощью оператора

 

pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse));,

где

            переменная m_pointTopLeft(x1, y1), имеющая стандартный тип CPoint (Точка), задает координаты левой верхней вершины описанного прямоугольника

            переменная m_sizeEllipse(x2-x1, y2-y1) , имеющая стандартный тип CSize (Размер), задает размеры эллипса.

 

            1. Объявляем в разделе Implementation файла MyPr05View.h следующие переменные:

private:

            const CSize m_sizeEllipse;       // Размер эллипса

            // Координата левой верхней вершины описанного прямоугольника:

            CPoint m_pointTopLeft;

            // Разница между текущим положением мыши и старым положением эллипса:

            CSize m_sizeOffset;

            BOOL m_bCaptured; // (m_bCapture = TRUE) <=> ("Захват" мышью произведен)

 

2. Создадим обработчик сообщения WM_LBUTTONDOWN.

Откройте вкладку Class View окна проектов. В списке классов выделите CMyPr05View и откройте окно его свойств (правая кнопка мыши). В окне свойств перейдите на вкладку сообщений и выберите там сообщение WM_LBUTTONDOWN.

Текст функции OnLButtonDown замените на:

 

// Объявляем переменную rectEllipse типа CRect - прямоугольник

CRect rectEllipse(m_pointTopLeft, m_sizeEllipse);

// Объявляем переменную MyEllipse типа CRgn - район

CRgn MyEllipse;

MyEllipse.CreateEllipticRgnIndirect(rectEllipse);  // Создаем район MyEllipse

 

if (MyEllipse.PtInRegion(point))           //Если курсор point внутри эллипса

{

            SetCapture();               // Осуществить "захват"

            m_bCaptured=TRUE;

            m_sizeOffset=point-m_pointTopLeft;

            // На время "захвата" мыши меняем вид курсора

            ::SetCursor(::LoadCursor(NULL, IDC_CROSS));

}

            Пояснения. Переменная класса CRgn (Регион) - это область, которая в общем случае представляет собой многоугольник, эллипс или их комбинацию. Регионы можно использовать для закраски, обрезания выводимого изображения и проверки на попадание курсора мыши на определенные участки.

            Чтобы воспользоваться стандартной функцией PtInRegion (Курсор В Регионе) класса CRgn, мы и создали переменную MyEllipse, поместив в неё эллипс, находящийся внутри описывающего прямоугольника rectEllipse.

            Функция SetCapture класса CWnd «захватывает» мышь, после чего сообщение о перемещении мыши будет посылаться данному окну, - даже если указатель мыши выйдет за его пределы.

            Возможные виды курсора см. в файле

*\CPLUSPLUS\VC98\Include\WINUSER.H,

начиная со строки 7167.

 

3. Создайте обработчик сообщения WM_LBUTTONUP (вкладка Class View > класс выделите CMyPr05View > окно свойств (правая кнопка мыши) > вкладка сообщений).

Текст функции OnLButtonUp замените на:

 

if (m_bCaptured) // Если захват произведен

{

            ::ReleaseCapture();                  // Снять "захват"

            m_bCaptured=FALSE;

}

            Пояснения. Функция Win32 ReleaseCapture снимает «захват» мыши.

 

            4. Продолжаем редактировать файл MyPr05View.cpp. В конец функции OnMouseMove добавьте код:

 

if (m_bCaptured) // Если захват произведен

{

            CRect rectOld(m_pointTopLeft, m_sizeEllipse);

//InvalidateRect вызовет функцию OnDraw, которая нарисует эллипс на старом месте

            InvalidateRect(rectOld, TRUE);

 

            m_pointTopLeft=point-m_sizeOffset; // новое положение верхнего левого угла

 

            CRect rectNew(m_pointTopLeft, m_sizeEllipse);

//InvalidateRect вызовет функцию OnDraw, которая нарисует эллипс на новом месте

            InvalidateRect(rectNew, TRUE);

}

            Пояснения. Функция InvalidateRect заставляет функцию OnDraw перерисовывать не все изображение, а только описывающий прямоугольник нашего эллипса.

В функции OnMouseMove применён стандартный алгоритм:

4.1 создать предыдущий описывающий прямоугольник эллипса;       

4.2 сделать этот прямоугольник недействительным;

4.3 обновить координаты левого верхнего угла прямоугольника;

4.4  создать новый описывающий прямоугольник эллипса;

4.5 сделать этот прямоугольник недействительным.

 

            Совет.  Чтобы лучше понять, зачем дважды вызывается функция InvalidateRect (пункты 4.2 и 4.5), поочередно закомментируйте сначала первый, затем второй вызов, и в обоих случаях посмотрите результат.

 

            5. Отредактируйте конструктор СMyPr05View. Он должен принять вид:

 

CMyPr05View::CMyPr05View():

                                   m_sizeEllipse(150,100), m_pointTopLeft(50,50), m_sizeOffset(0,0)

{

            m_bCaptured = FALSE;         // Сначала захвата нет

}         

 

6. Отредактируйте функцию OnDraw, добавив в неё следующий код:

 

CPen*   pOldPen;       // Переменная pOldPen позволит отсоединить перо

CBrush* pOldBrush;   // Переменная pOldBrush позволит отсоединить кисть

 

CPen   MyPen(PS_SOLID,1,RGB(150,30,180));      // Объявляем сиреневое перо

CBrush MyBrushSolid(RGB(150,30,180));                // Объявляем сиреневую кисть

 

pOldPen = pDC->SelectObject(&MyPen);                // Присоединяем свое перо

pOldBrush=pDC->SelectObject(&MyBrushSolid); // Присоединяем свою кисть

 

pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse));           // Рисуем эллипс

 

pDC->SelectStockObject(BLACK_PEN);                // Отсоединяем текущее перо

pDC->SelectStockObject(WHITE_BRUSH);            // Отсоединяем текущую кисть

 

            7. Соберите своё приложение и запустите его на выполнение. Вы видите, что мышь исправно «таскает» эллипс следом за собой. Вот только изображение сильно мельтешит. Чтобы устранить мелькание, необходимо использовать растровое изображение, т.е., грубо говоря, рисовать картинку не на экране, а в памяти, после чего выводить эту картинку на экран.

 

Устраняем мелькание с помощью растрового изображения

            1. В группу private раздела Implementation (файл MyPr05View.h) добавьте объявление двух закрытых (private) переменных, одна из которых - m_pBitmap - указатель на наше растровое изображение, а вторая - m_pdcMemory - указатель на тот участок памяти, в котором будет храниться растр:

 

            CDC*        m_pdcMemory;

            CBitmap*    m_pBitmap;

 

            2. Откройте файл MyPr05View.cpp и отредактируйте конструктор СMyPr05View, добавив в него строки:

 

m_pdcMemory=new CDC;     // Создаём объект «контекст устройства в памяти»

m_pBitmap=new CBitmap;      // Создаём объект «GDI-растр»

 

            3. Отредактируйте деструктор ~СMyPr05View, добавив в него строки:

 

delete m_pdcMemory; // Уничтожаем объект «контекст устройства в памяти»

delete m_pBitmap;                   // Уничтожаем объект «GDI-растр»

 

4. Создайте обработчик сообщения WM_PAINT (вкладка Class View > класс выделите CMyPr05View > окно свойств (правая кнопка мыши) > вкладка сообщений).

Текст функции OnPaint замените на:

           

CPaintDC dc(this); // Контекст устройства для рисования

OnPrepareDC(&dc);

 

CBitmap* pOldBitmap;           // Переменная pOldBitmap позволит отсоединить рисунок

CBrush* pOldBrush;               // Переменная pOldBrush позволит отсоединить кисть

CRect MyR;                            // Объявляем переменную MyR типа CRect (Прямоугольник)

dc.GetClipBox(&MyR);          // Загружаем в переменную MyR размеры окна приложения

 

// Создаем ОБЪЕКТЫ Windows, соответствующие нашим

// контексту устройства в памяти и растровому изображению

if (m_pdcMemory->GetSafeHdc()==NULL)

{

            m_pdcMemory->CreateCompatibleDC(&dc);

            // Растр будет иметь ширину Width() и высоту Height() окна приложения

            m_pBitmap->CreateCompatibleBitmap(&dc, MyR.Width(), MyR.Height());

}

           

// По адресу m_pdcMemory размещаем растр m_pBitmap

pOldBitmap=m_pdcMemory->SelectObject(m_pBitmap);

 

// Устанавливаем минимальный размер памяти (прямоугольник MyR)

// для хранения картинки; это необходимо, чтобы программа работала быстрее

m_pdcMemory->SelectClipRgn(NULL);

m_pdcMemory->IntersectClipRect(&MyR);

 

// Объявляем белую кисть для заднего фона (background).

CBrush backgroundBrush(RGB(255,255,255));

// Присоединяем выбранную кисть для заднего фона

pOldBrush=m_pdcMemory->SelectObject(&backgroundBrush);

 

// Готовим картинку:

// 1. Закрашиваем фон в выбранный цвет

m_pdcMemory->PatBlt(MyR.left,MyR.top,MyR.Width(),MyR.Height(),PATCOPY);

// 2. Вызываем функцию OnDraw, которая рисует на картинке эллипс

OnDraw(m_pdcMemory);

           

// Готовую картинку копируем из памяти на экран

dc.BitBlt(MyR.left,MyR.top,MyR.Width(),MyR.Height(),

                          m_pdcMemory,         MyR.left,MyR.top, SRCCOPY);

           

m_pdcMemory->SelectObject(pOldBitmap);  // Отсоединяем растр

m_pdcMemory->SelectObject(pOldBrush);               // Отсоединяем кисть

Соседние файлы в папке Lab