Демонстрационное приложение DragWin
Пример - приложение DragWin (диалоговое окошко, MFC) иллюстрирует
два способа осуществить перемещение окна с захватом его не только
за заголовок, но и за любую точку на клиентской области. Идея
первого способа проста - при получении сообщения о перемещении
мыши передвигаем наше окно в соответствии с новыми координатами.
Второй способ поизящнее, и заключается в некотором "обмане"
Windows, после которого она считает, что мышь находится над
заголовоком окна, даже если реально это уже клиентсткая часть.
Способ 1
Реализован для главного окна приложения. Заключается в написании
собственных обработчиков нажатия (WM_LBUTTONDOWN), перемещения
(WM_MOUSEMOVE) и отпускания (WM_LBUTTONUP) левой кнопки мыши.
Обработчики на данные события устанавливаются стандартным образом
- через MFC ClassWizard.
Код:
void CDragWinDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// выставим флажок - пошло перетаскивание
m_bMoveWindow = TRUE;
// все сообщения от мыши - к нашему окну, независимо от координат
// чтобы мышь не улетала с окна при быстром движении
SetCapture();
// сохраняем координаты окна
GetWindowRect(m_RectDlg);
// сохраняем положение мышки внутри окна программы
ClientToScreen(&point);
m_MouseInDlg = point - m_RectDlg.TopLeft();
// меняем курсор, чтоб веселее было тащить
m_hCursor = m_hCursorDown;
::SetCursor(m_hCursor);
// вызываем обработчик по умолчанию
CDialog::OnLButtonDown(nFlags, point);
}
void CDragWinDlg::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bMoveWindow) // надо тащить
{
// преобразуем координаты мыши в экранные
// именно они нужны будут для SetWindowPos()
ClientToScreen(&point);
// двигаем окно в соответствии с новыми координатами мыши
SetWindowPos(&wndTop,
point.x - m_MouseInDlg.x, point.y - m_MouseInDlg.y,
m_RectDlg.right - m_RectDlg.left,
m_RectDlg.bottom - m_RectDlg.top,
SWP_SHOWWINDOW);
// поскольку обработчик по умолчанию все равно будет использовать
// первоначальные параметры сообщения
// обратное преобразование ScreenToClient(&point);
// можно не вызывать
}
// вызываем обработчик по умолчанию
CDialog::OnMouseMove(nFlags, point);
}
void CDragWinDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// перетаскивание закончилось
m_bMoveWindow = FALSE;
// "отпускаем" мышку
ReleaseCapture();
// меняем курсор на исходный
m_hCursor = m_hCursorUp;
// вызываем обработчик по умолчанию
CDialog::OnLButtonUp(nFlags, point);
}
BOOL CDragWinDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// заменяем курсор на свой
::SetCursor(m_hCursor);
return TRUE;
// !!! было return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
Замена курсора естесственно не является критичной для собственно
перетаскивания, а добавлена исключительно для визуализации
процесса захвата окошка.
Способ 2
Реализован для окна About этого же приложения. Заключается в
замене обработчика события WM_NCHITTEST, которое информирует об
области, над которой в данный момент находится мышка. Обработчик
этого сообщения также можно добавить через MFC ClassWizard.
Предварительно на закладке ClassInfo для класса CAboutDlg нужно
установить для Message Filter значение Window.
Переписываем функцию - обработчик следующим образом:
Код:
UINT CAboutDlg::OnNcHitTest(CPoint point)
{
UINT ret = CDialog::OnNcHitTest(point);
// если обработчик по умолчанию говорит нам что мышка
// над клиентской областью окна, заменяем возвращаемое
// значение на HTCAPTION - мышка над заголовком окна,
// а за заголовок перемещать окно можно!
if (ret == HTCLIENT)
return HTCAPTION;
return ret;
}
Второй способ проще, первый потенциально гибче, используйте тот,
что лучше подходит к вашему конкретному случаю.