Меню сайта
Главная
Общие сведения
Архитектура Windows
Программируем на vs.net
Студенческие заметки
Статьи
Контакты
Интернет магазин
Visual Studio 2008 Standard
Переход на Visual Basic .NET
Тестирование и отладка приложений в VB.NET
Поддержка и оптимизация на VB.NET
Создание наборов данных ADO.NET
Сопровождение Web-приложений в VB.NET
Введение в ADO.NET
Editor.NET
MemPort.NET
Управление автономными данными

Весь каталог

Новости
Макроподстановка ASSERT_VALID в отладочной (Debug) версии проекта проверяет на осмысленность полученный указатель и дает сообщение об ошибке, в случае когда указатель равен нулю или в действительности не является адресом объекта класса, производного от класса CObject. Если вы просмотрите иерархию классов MFC, то увидите, что CObject является отцом-прародителем всех классов, потомки которых использованы в нашем приложении. Подробнее "Классы приложения"
При создании нового проекта Studio.Net автоматически создает рабочее пространство и помещает в него этот проект. Вот перечень шагов для создания нового проекта и нового рабочего пространства (solution), его содержащего. Подробнее "Создание нового проекта"
Откройте файл ChildView.cpp, который содержит коды реализации методов класса CChildView. Его имя содержит ложный намек на происхождение от CView. На самом деле он происходит от класса CWnd и инкапсулирует функциональность окна, оккупирующего клиентскую область окна рамки, которое управляется классом CMainFrame. Простое окно, как вы помните, для перерисовки своего содержимого, вместо метода OnDraw использует метод OnPaint. Найдите этот метод в классе CChildView и убедитесь, что в нем контекст устройства создается, а не приходит в качестве параметра от каркаса приложения, как это было в приложениях, поддерживающих архитектуру документ — представление. Подробнее "Класс окна для отображения графика"
Установка параметрпв освещения осуществляется подобно тому, как это делалось в предыдущем уроке. Но здесь мы храним все параметры для тога, чтобы можно было управлять освещенностью изображения. Немного позже разработаем диалог, с помощью которого пользователь программы сможет изменять настройки освещения, а сейчас введите коды функции SetLight. Подробнее "Параметры освещения"
Картинная галерея

Общие сведения

Программируем на vs.net

Архитектура программного комплекса

Результаты взаимодействия Visual Studio

Реализация

Архитектура Windows

Солирование

Использование материалов

Причины возникновения

Методы

Партнеры проекта

Взаимодействие классов

Класс CPropDlg должен обеспечить реакцию на изменение регулировок, а класс COpenGL должен учесть новые установки и перерисовать изображение. Общение классов, как мы уже отметили, происходит по законам СОМ, то есть с помощью указателя на интерфейс. Здесь нам на помощь приходит шаблон классов CComQiPtr. Литеры «QI» в имени шаблона означают Querylnterface, что обещает нам автоматизацию в реализации запроса указателя на этот интерфейс. В классе переопределены операции выбора (->), взятия адреса (&), разадресации (*) и некоторые другие, которые упрощают использование указателей на различные интерфейсы. При создании объекта класса CComQiPtr, например:

 

CComQIPtr<IOpenGL, &IID_IOpenGL> р(m_ppUnk[i]) ;

 

он настраивается на нужный нам интерфейс, и далее мы работаем с удобствами, не думая о функциях Querylnterface, AddRef и Release. При выходе из области действия объекта р класса CGomQiPtr<lOpenGL, &ilD_iOpenGL> освобождение интерфейса произойдет автоматически.

 

Для обмена с окном диалоговой вставки введите в protected-секцию класса CPropDlg массив текущих позиций регуляторов и переменную для хранения текущего режима изображения полигонов:

 

protected:

int m_Pos[11]; BOOL m_bQuad;

В конструктор класса добавьте код инициализации массива:

ZeroMemory (m_Pos, sizeof(m_Pos));

 

Другую переменную следует инициализировать при открытии диалога (вставки). Способом, который вы уже неоднократно применяли, введите в класс реакции на Windows-сообщения WM_INITDIALOG и WM_HSCROLL. Затем перейдите к созданной мастером заготовке метода Onl nit Dialog, которую найдете в файле PropDlg.cpp:

 

LRESULT CPropDlg::OnInitDialog(UINT uMsg, WPARAM wParam,

LPARAM IParam, BOOL& bHandled)

{

_super::OnInitDialog(uMsg, wParam, IParam, bHandled);

return 1;

}

 

Здесь вы увидите новое ключевое слово языка _ super, которое является спецификой Microsoft-реализации. Оно представляет собой не что иное, как явный вызов родительской версии функции метода базового или super-класса. Так как классы в ATL имеют много родителей, то _ super обеспечивает выбор наиболее подходящего из них. Теперь введите изменения, которые позволят при открытии вкладки привести наши регуляторы в соответствие со значениями переменных в классе COpenGL. Вы помните, что значения регулировок используются именно там. Там же они и хранятся:

 

LRESULT CPropDlg: :OnInitDialog (UINT uMsg, WPARAM wParam,

LPARAM IParam, BOOL& bHandled)

_super::OnInitDialog(uMsg, wParam, IParam, -bHandled);

//====== Кроим умный указатель по шаблону IQpenGL

CComQIPtr<IOpenGL> p(m_ppUnk[0]); 

//=== Пытаемся связаться с классом COpenGL и выяснить

//=== значение переменной m_FillMode

//=== В случае неудачи даем сообщение об ошибке

DWORD mode;

if FAILED (p->GetFillMode(&mode))

{

ShowError();

return 0;

}

//====== Работа с combobox по правилам API

//====== Получаем Windows-описатель окна

HWND hwnd = GetDlgItem(IDC_FILLMODE); 

//====== Наполняем список строками текста

SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Points"

SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Lines")

SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Fill"); 

// Выбираем текущую позицию списка в соответствии

// со значением, полученным из COpenGL WPARAM

w = mode == GL_POINT ? 0

: mode == GL_LINE ?1:2;

SendMessage(hwnd, CB_SETCURSEL, w, 0); 

// Повторяем сеанс связи, выясняя позиции ползунков

if FAILED (p->GetLightParams(m_Pos))

{

ShowError();

return 0;

}

// Мы не надеемся на упорядоченность идентификаторов

// элементов и поэтому заводим массив отображений

UINT IDs[] =

{

IDC_XPOS,

IDC_YPOS,

IDC_ZPOS,

IDC_AMBIENT,

IDC_DIFFUSE,

IDC_SPECULAR,

IDC_AMBMAT,

IDC_DIFFMAT,

IDC_SPECMAT,

IDC_SHINE,

IDC_EMISSION

}; 

//=== Пробег по всем регуляторам и их установка

for (int i=0;

Ksizeof (IDs)/sizeof (IDs [0] ) ; i++)

{

//====== Получаем описатель окна

hwnd = GetDlgItem(IDs[i]);

UINT nID; 

//====== Узнаем идентификатор элемента

int num = GetSliderNum(hwnd, nID);  

//====== Выставляем позицию

~ SendMessage(hwnd,TBM_SETPOS,TRUE,(LPARAM)m_Pos[i]

//=== Приводим в соответствие текстовый ярлык

char s [ 8 ] ;

sprintf (s,"%d",m_Pos[i]);

SetDlgltemText(nID, s);

} 

// Выясняем состояние режима изображения полигонов

if FAILED (p->GetQuad(&m_bQuad))

{

ShowError ();

return 0;

}

//====== Устанавливаем текст

SetDlgltemText (IDC_QUADS,m_bQuad ? '"Quads" : "Strips");

return 1 ;

}

 

В процессе обработки сообщения нам понадобились вспомогательные функции GetSliderNum и ShowError. Первая функция уже участвовала в проекте на основе MFC, поэтому мы лишь напомним, что она позволяет по известному Windows-описателю окна элемента управления получить его порядковый номер в массиве позиций регуляторов. Кроме этого, функция позволяет получить идентификатор элемента управления nio, который нужен для управления им, например: при вызове SetDlgltemText (nID, s);.

 

int CPropDlg: : GetSliderNum (HWND hwnd, UINT& nID)

{

// Получаем ID по известному описателю окна

switch (: :GetDlgCtrlI)(hwnd) )

{

case IDC_XPOS:

nID = IDC_XPOS_TEXT;

return 0; case IDC_YPOS:

nID = IDC_YPOS_TEXT;

return 1 ; case IDC_ZPOS:

nID = IDC_ZPOS_TEXT;

return 2; case IDC_AMBIENT:

nID = IDC_AMB_TEXT;

return 3; case IDC_DIFFUSE:

nID = IDC_DIFFUSE_TEXT;

return 4 ;

case IDC_SPECULAR:

nID = 1DC_SPECULAR_TEXT;

return 5; case IDC_AMBMAT:

nID = IDC_AMBMAT_TEXT;

return 6; case IDC_DIFFMAT:

nID = IDC_DIFFMAT_TEXT;

return 7; case IDC_SPECMAT:

nID = IDC_SPECMAT_TEXT;

return 8; case IDC_SHINE:

nID = IDC_SHINE_TEXT;

return 9; case IDC_EMISSION:

nID = IDC_EMISSION_TEXT;

return 10;

}

return 0;

}

 

Функция showError демонстрирует, как в условиях СОМ можно обработать исключительную ситуацию. Если мы хотим выявить причину ошибки, спрятанную в HRESULT, то следует воспользоваться методом GetDescription интерфейса lErrorinfо. Сначала мы получаем указатель на него с помощью объекта класса ccomPtr. Этот класс, так же как и CGomQiPtr, автоматизирует работу с методами главного интерфейса lUnknown, за исключением метода Queryinterface:

 

void CPropDlg::ShowError()

{

USES_CONVERSION; 

//====== Создаем инерфейсный указатель

CComPtr<IErrorInfo> pError;

//====== Класс для работы с Unicode-строками

CComBSTR sError; 

//====== Выясняем причину отказа

GetErrorlnfo (0, &pError);

pError->GetDescription(SsError); 

// Преобразуем тип строкового объекта для вывода в окно MessageBox(OLE2T(sError),_T("Error"),MB_ICONEXCLAMATION);

}

 

Если вы построите сервер в таком виде, то вас встретит неприятное сообщение о том, что ни один из явных или неявных родителей CPropDlg не имеет в своем составе функции OninitDialog. Обращаясь за справкой к документации (по классу CDialogimpl), мы убеждаемся, что это действительно так. Значит, инструмент Studio.Net, который создал заготовку функции обработки, не прав. Но как же будет вызвана наша функция OninitDialog, если она не является виртуальной функцией одного из базовых классов? Ответ на этот вопрос, как и на большинство других, можно получить в режиме отладки.

 

Закомментируйте строку вызова родительской версии, которая производится с помощью многообещающего ключевого слова _super (это и есть лекарство), поставьте точку останова на строке, следующей за ней, и нажмите F5. Если вы не допустили еще одной, весьма вероятной, ошибки, то тестовый контейнер сообщит, что он не помощник в процессе отладки, так как не содержит отладочной информации. Согласитесь с очевидным фактом, но не делайте поспешного вывода о том, что невозможно отлаживать все СОМ-серверы. В тот момент, когда вы инициируете новую страницу свойств, отладчик возьмет управление в свои руки и остановится на нужной строке программы. Теперь вызовите одно из самых полезных окон отладчика по имени Call stack, в нем вы увидите историю вызова функции OninitDialog, то есть цепочку вызовов функций. Для этого:

 

  • Дайте команду Debug > Windows > Call Stack (или Alt+7).

  • Внедрите это окно, если необходимо, в блок окон отладчика (внизу экрана).

  • Убедитесь, что вызов произошел из функции DialogРгос одного из базовых классов, точнее шаблонов классов, CDialoglmplBaseT.

 

Этот опыт иллюстрирует тот факт, что все необычно в мире ATL. Этот мир устроен совсем не так, как MFC. Шаблоны классов дают удивительную гибкость всей конструкции, способность приспосабливаться и подстраиваться. Теперь рассмотрим вторую, весьма вероятную, ошибку. Секцию protected в классе CPropDlg следует правильно разместить (странно, не правда ли?). Лучше это сделать так, чтобы сразу за ней шло объявление какой-либо из существующих секций public. Если поместить ее, например, перед макросом

 

DECLARE_REGISTRY_RESOURCEID(IDR__PROPDLG)

 

то макрос окажется безоружным против такой атаки, хотя по идее он должен сопротивляться и даже не замечать наскоков подобного рода. Возможно, этот феномен исчезнет в окончательной версии Studio.Net.

 

Сообщение о прокрутке в окне

Сообщение WM_HSCROLL приходит в окно диалога (читайте: объекту диалогового класса, связанного с окном) всякий раз, как пользователь изменяет положение одного из ползунков, расположенных на лице диалога. Это довольно удобно, так как мы можем в одной функции обработки (onHScroll) отследить изменения, произошедшие в любом из 11 регуляторов. Введите коды обработки этого сообщения, которые сходны с кодами, приведенными в приложении на основе MFC, за исключением СОМ-специфики общения между классами CPropDlg и COpenGL:

 

LRESULT CPropDlg::OnHScroll(UINT /*uMsg*/, WPARAM wParam,

LPARAM iParam, BOOL& /*bHandled*/)

{

//====== Информация о событии запакована в wParara

int nCode = LOWORD(wParam), nPos = HIWORD(wParam), delta, newPos;

HWND hwnd = (HWND) IParam; 

// Выясняем номер и идентификатор активного ползунка

UINT nID;

int num = GetSliderNum(hwnd, nID); 

//====== Выясняем суть события

switch (nCode)

{

case SB_THUMBTRACK:

case SBJTHUMBPOSITION:

m_Pos[num] = nPos;

break;  

//====== Сдвиг до упора влево (клавиша Home)

case SB_LEFT:

delta = -100;

goto New_Pos;  

//====== Сдвиг до упора вправо (клавиша End)

case SB_RIGHT:

delta = + 100;

goto New_Pos;

case SB_LINELEFT:

// И т.д.

delta = -1;

goto New_Pos;

case SB_LINERIGHT:

delta = +1;

goto New_Pos;

case SB_PAGELEFT:

delta = -20;

goto New_Pos;

case SB_PAGERIGHT:

delta = +20;

goto New_Pos;

New_Pos:

newPos = m_Pos[num] + delta;

m_Pos[num] = newPos<0 ? 0

: newPos>100 ? 100 : newPos;

break;

case SB_ENDSCROLL: default:

return 0;

} 

//=== Готовим текстовое выражение позиции ползунка

char s[8];

sprintf (s,"%d",m_Pos[num]);

SetDlgltemText(nID, (LPCTSTR)s);

//====== Цикл пробега по всем объектам типа PropDlg

for (UINT i = 0; i < m_nObjects; )

//====== Добываем интеофейсн:

//====== Добываем интерфейсный указатель

CComQIPtr<IOpenGL, &IID_IOpenGL> p (m_ppUnk[i] ) ;  

//====== Устанавливаем конкретный параметр

if FAILED (p->SetLightParam (num, m_Pos [num] ) )

ShowError();

return 0;

}

}

return 0;

}

 

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

 

Реакция на выбор в окне выпадающего списка

Теперь введем реакцию на выбор пользователем новой строки в окне выпадающего списка. Для этого выполните следующие действия:

 

  • Откройте в окне редактора Studio.Net шаблон окна диалога IDD_PROPDLG.

  • Поставьте фокус в окно выпадающего списка IDC_FILLMODE и переведите фокус окно Properties.

  • Нажмите кнопку Control Events, расположенную на инструментальной панели окна Properties.

  • Найдите строку с идентификатором уведомляющего сообщения CBN_SELCHANGE и в ячейке справа выберите действие <Add>, для того чтобы там появилось имя функции обработки OnSelchangeFillmode.

  • Перейдите в окно PropDlg.cpp и введите следующие коды в заготовку функции OnSelchangeFillmode.

 

LRESULT CPropDlg

::OnSelchangeFillmode(WORD/*wNotifyCode*/, WORD /*wID*/,

HWND hWndCtl, BOOL& bHandled)

{

//====== Цикл пробега по всем объектам типа PropDlg

for (UINT i = 0; i < m_nObjects; i++)

{

CComQIPtr<IOpenGL, &IID_IOpenGL> p(m_ppUnk[i]); 

// Выясняем индекс строки, выбранной в окне списка

DWORD sel = (DWORD)SendMessage(hWndCtl, CB_GETCURSEL,0,0); 

// Преобразуем индекс в режим отображения полигонов

sel = sel==0 ? GL_POINT

: sel==l ? GL_LINE : GL_FILL; 

//====== Устанавливаем режим в классе COpenGL

if FAILED (p->SetFillMode(sel))

{

ShowError();

return 0;

}

}

bHandled = TRUE;

return 0;

}

 

Обратите внимание на то, что нам пришлось убирать два комментария, чтобы сделать видимым параметры hWndCtl и bHandled.

 

Реакция на нажатия кнопок

При создании отклика на выбор режима изображения полигонов следует учесть попеременное изменение текста и состояния кнопки. Поставьте курсор на кнопку IDC_QUADS и в окне Properties нажмите кнопку Control Events. Затем найдите строку с идентификатором уведомляющего сообщения BN_CLICKED и в ячейке справа выберите действие <Add>. Текст в ячейке должен измениться и стать OnClickedQuads. Введите следующие коды в заготовку функции:

 

LRESULT CPropDlg::OnClickedQuads(WORD /*wNotifyCode*/,

WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)

{

//====== По всем объектам PropDlg

for (UINT i = 0; i < m_nObjects; i++)

{

//====== Добываем интерфейсный указатель

CComQIPtr<IOpenGL, &IID_IOpenGL> p(m_ppUnk[i]) ; 

//====== Переключаем режим

m_bQuad = !m_bQuad; 

//====== Устанавливаем текст на кнопке

SetDlgltemText(IDC_QUADS, m_bQuad ? "Quads" : "Strip");

if FAILED (p->SetQuad(m_bQuad))

{

ShowError();

return 0;

bHandled = TRUE;

return 0;

}

 

Аналогичные, но более простые действия следует произвести в реакции на нажатие кнопки Выбор файла. Введите функцию для обработки этого события и вставьте в нее следующий код:

 

LRESULT CPropDlg: rOnCl'ickedFilename (WORD /*wNotif yCode*/,

WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)

{

for (UINT i = 0; i < m_nObjects; i++)

{

CComQIPtr<IOpenGL, &IID_IOpenGL> p (m_ppUnk [i] ) ;  

//====== Вызываем функцию класса COpenGL

if FAILED (p->ReadData() )

{

ShowError () ;

return 0 ;

}

bHandled = TRUE;

return 0;

}

 

Постройте сервер и проверьте работу страницы свойств. Попробуйте прочесть другой файл, например тот, который был создан приложением, созданным в рамках MFC. Так как мы не изменяли формат данных, записываемых в файл, то все старые файлы должны читаться.

 

Управление объектом с помощью мыши

Алгоритм управления ориентацией объекта с помощью мыши мы разработали ранее. Вы помните, что перемещение курсора мыши при нажатой кнопке должно вращать изображение, причем горизонтальное перемещение вращает его вокруг вертикальной оси Y, а вертикальное — вокруг горизонтальной оси X. Если одновременно с мышью нажата клавиша Ctrl, то объект перемещается (glTranslatef) вдоль осей X и Y. Наконец, с помощью правой кнопки изображение перемещается вдоль оси Z, то есть приближается или отдаляется. Таймер помогает нам в том, что продолжает вращение, если очередной квант перемещения мышью стал выше порога чувствительности. Скорость вращения имеет два пространственных компонента, которые пропорциональны разности двух последовательных во времени координат курсора. Чем быстрее движется курсор при нажатой левой кнопке, тем большая разность координат будет обнаружена в обработчике сообщения WM_MOUSEMOVE. Именно в этой функции оценивается желаемая скорость вращения.

 

Описанный алгоритм обеспечивает гибкое и довольно естественное управление ориентацией объекта, но, как вы помните, он имеет недостаток, который проявляется, когда модуль угла поворота вдоль первой из вращаемых (с помощью glRotate) осей, в нашем случае — это ось X, превышает 90 градусов. Вам, читатель, я рекомендовал самостоятельно решить эту проблему и устранить недостаток. Ниже приводится одно из возможных решений. Если вы, читатель, найдете более изящное, буду рад получить его от вас. Для начала следует ввести в состав класса COpenGL функцию нормировки углов вращения, которая, учитывая периодичность процесса, ограничивает их так, чтобы они не выходили из диапазона (-360°, 360°):

 

void COpenGL::LimitAngles()

{

//====== Нормирование углов поворота так,

//====== чтобы они были в диапазоне (-360°, +360°)

while (m_AngleX < -360.f)

m_AngleX += 360.f;

while (m_AngleX > 360.f)

m_AngleX -= 360.f;

while (m_AngleY < -360.f)

m_AngleY += 360.f;

while (m_AngleY > 360.f)

m_AngleY -= 360.f;

}

 

Затем следует вставить вызовы этой функции в те точки программы, где изменяются значения углов. Кроме того, надо менять знак приращение m_dx, если абсолютная величина угла m_AngleX попадает в диапазон (90°, 270°). Это надо делать при обработке сообщения WM_MOUSEMOVE. Ниже приведена новая версия функции обработки этого сообщения, а также сообщения WM_TIMER, в которое также следует ввести вызов функции нормировки:

 

LRESULT COpenGL::OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM IParam, BOOL& bHandled)

{

//====== Если был захват

if (m_bCaptured)

{

//====== Вычисляем желаемую скорость вращения

short xPos = (short)LOWORD(IParam);

short yPos = (short)HIWORD(1Param);

m_dy = float(yPos - m_yPos)/20.f;

m_dx = float(xPos - m_xPos)/20.f; 

//====== Если одновременно была нажата Ctrl,

if (wParam & MK_CONTROL)

{

//=== Изменяем коэффициенты сдвига изображения

m_xTrans += m_dx;

m_yTrans -= m_dy;

}

else

{ 

//====== Если была нажата правая кнопка

if (m_bRightButton)

//====== Усредняем величину сдвига

m_zTrans += (m_dx + m_dy)/2.f;

else

{

//====== Иначе, изменяем углы поворота

//====== Сначала нормируем оба угла

LiraitAngles(); 

//=== Затем вычисляем модуль одного из них

double a = fabs(m_AngleX);

// и изменяем знак приращения(если надо)

if (90. < а && а < 270.) m_dx = -m_dx;

m_AngleX += m_dy;

m_AngleY += m_dx;

}

}

// В любом случае запоминаем новое положение мыши

m_xPos = xPos;

m_yPos = yPos;

FireViewChange();

}

bHandled = TRUE; return 0;

}

LRESULT COpenGL: :OnTimer (UINT /*uMsg*/, WPARAM

/*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)

{

//====== Нормировка углов поворота

LimitAngles () ; 

//====== Увеличиваем эти углы

m_AngleX += m_dy; m_AngleY += m_dx;

//====== Просим перерисовать окно

FireViewChange();

bHandled = TRUE;

return 0;

}

 

Ниже приведены функции обработки других сообщений мыши. Они сходны с теми, которые мы разработали для MFC-приложения, за исключением прототипов и возвращаемых значений. Начнем с обработки нажатия левой кнопки. Оно, очевидно, должно всегда останавливать таймер, запоминать факт нажатия кнопки и текущие координаты курсора мыши:

 

LRESULT COpenGL::OnLButtonDown(UINT /*uMsg*/, WPARAM

/*wParam*/, LPARAM IParam, BOOL& bHandled)

{

//====== Останавливаем таймер

KillTimer(1); 

//====== Обнуляем кванты перемещения

m_dx = O.f;

m_dy = 0.f; 

//====== Захватываем сообщения мыши,

//====== направляя их в свое окно

SetCapture(); 

//====== Запоминаем факт захвата

m_bCaptured = true;  

//====== Запоминаем координаты курсора

m_xPos = (short)LOWORD(IParam);

m_yPos = (short)HIWORD(IParam);

bHandled = TRUE; return 0;

}

 

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

 

LRESULT COpenGL::OnLButtonUp(UINT /*uMsg*/, WPARAM

/*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)

{

//====== Если был захват,

if (m_bCaptured)

{

//=== то анализируем желаемый квант перемещения

//=== на превышение порога чувствительности

if (fabs(m_dx) > 0.5f || fabs(m_dy) > 0.5f)

//====== Включаем режим постоянного вращения

SetTimer(1,33) ;

else

//====== Выключаем режим постоянного вращения

KillTimer(1); 

//====== Снимаем флаг захвата мыши

m_bCaptured = false; 

//====== Отпускаем сообщения мыши

ReleaseCapture();

}

bHandled = TRUE;

return 0;

}

 

При нажатии на правую кнопку выполняются те же действия, что и при нажатии на левую, но дополнительно запоминается факт нажатия правой кнопки, с тем чтобы можно было правильно интерпретировать последующие сообщения о перемещении указателя мыши и вместо вращения изображения производить его сдвиг вдоль оси Z. Отметьте тот факт, что мы должны убрать символы комментариев вокруг параметров:

 

LRESULT COpenGL::OnRButtonDown(UINT uMsg, WPARAM wParam,

LPARAM IParam, BOOL& bHandled)

{

//====== Запоминаем факт нажатия правой кнопки

m_bRightButton = true; 

//====== Воспроизводим реакцию на левую кнопку

OnLButtonDown(uMsg, wParam, IParam, bHandled);

return 0;

}

 

Отпускание правой кнопки просто отмечает факт прекращения перемещения вдоль оси Z и отпускает сообщения мыши (ReleaseCapture), для того чтобы они могли правильно обрабатываться другими окнами, в том числе и нашим окном-рамкой. Если этого не сделать, то будет невозможно использоваться меню:

 

LRESULT COpenGL::OnRButtonUp(UINT /*uMsg*/, WPARAM

/*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)

{

m_bRightButton = false;

m_bCaptured = false;

ReleaseCapture();

bHandled = TRUE;

return 0;

}

 

Запустите и проверьте управляемость объекта. Введите коррективы чувствительности мыши. В заключение отметим, что при выборе параметров заготовки ATL мы могли на вкладке Miscellaneous (Разное) поднять не только флажок Insertable, но и windowed Only. Это действие сэкономило бы те усилия, которые были потрачены на поиск неполадок, вызванных отсутствием флага m bWindowOnly.

Интернет магазин

 

1510 руб.

Переход на Visual Basic .NET

Слушатели курса познакомятся с различиями между Visual Basic 6.0 и Visual Basic .NET, а также с новыми возможностями Visual Studio .NET. Целевая аудитория: разработчики, имеющие опыт работы с предыдущими версиями Microsoft Visual Basic и желающие перейти на Microsoft Visual Basic .NET. 

Список версий:

Переход на Visual Basic .NET


1510 руб.

Тестирование и отладка приложений в VB.NET

На курсе обсуждаются методы тестирования и отладки программ, использование классов Debug и Trace из библиотеки классов .NET Framework, и работа с отладчиками из Visual Studio .NET и .NET Framework SDK. Целевая аудитория: Web-разработчики, желающие обновить свои знания и навыки в области тестирования и отладки приложений в среде Visual Studio .NET или подготовиться к сертификации MCAD или MCSD .NET. 

Список версий:

Тестирование и отладка приложений в VB.NET


 

  Все права защищены.
  Копирование запрещено.

Rambler's Top100
  Студия профессионального дизайна
  Дизайн: Студия Onta.ru