Приложения, даже если они на первый взгляд функционируют корректно, надо тщательно тестировать, с тем чтобы проверить максимальное число состояний и ситуаций, в которых оно может оказаться. Так, тестируя настоящее приложение, я обнаружил два дефекта (не удивлюсь, если вы найдете еще больше). Первый состоит в том, что, если до выполнения команды Refresh изменить вручную позицию полос прокрутки, после выполнения команды происходит рассинхронизация полос. Лекарство оказалось простым — вставить в нужное место строку с вызовом:
ScrollToPosition(CPoint(0,0));
Эта функция является методом класса CScrollview, она устанавливает обе полосы прокрутки в исходные состояния. Определение места вставки мы оставляем читателю.
Кроме того, логика приложения нарушается, если окна мини-чертежей (cwndGeom) видны не полностью, а только частично. Курсор в этом случае не изменяет свою форму, несмотря на то что он выходит за границы окна CRightView, то есть за границы клиентской области окна CTreeFrame. Этот эффект наблюдается только при выходе в сторону гипотетического продолжения окна cwndGeom. Объяснение в том, что мы захватили мышиные сообщения (SetCapture) и направляем их в частично скрытое окно типа CWndGeom, которое не отпускает мышь, так как справедливо считает, что курсор находится над его прямоугольником. Окно не знает, что та его часть, которая находится под курсором, в данный момент скрыта окном-рамкой или полосами прокрутки. Вы помните, что полосы прокрутки являются частью клиентской области окна? Если диагноз поставлен точно, то и лечение будет эффективным. Ниже приведена новая версия обработки сообщения
WM_MOUSEMOVE В Классе CWndGeom:
void CWndGeom::OnMouseMove(UINT nFlags, CPoint point)
{
//====== Два прямоугольника (CWndGeom и CRightView)
CRect rChild, rParent;
//=== Определяем экранные координаты (не клиентские!)
GetWindowRect(rChild) ;
m_pView->GetWindowRect (rParent) ;
//=== Если есть полосы прокрутки, то уменьшаем
//=== прямоугольник окна на толщину полос
if (m_pView->m_szScroll.cx - m_j>View->m_szView.cx > 0)
rParent . right -= SM_CXHSCROLL;
if (m_pView->m_szScroll.cy - m_pView->m_szView.cy > 0)
rParent. bottom -= SM_CYVSCROLL ;
//=== Ищем пересечение прямоугольников, обрезая rChild
rChild.IntersectRect (rChild, rParent);
//=== Приводим к экранным координаты указателя мыши
ClientToScreen (Spoint) ;
//=== Если мышь попала в усеченный прямоугольник,
if ( rChild. PtlnRect (point))
{
//=== то демонстрируем активное состояние,
// изображая рамку внутри прямоугольника CWndGeom
if (GetCaptureO != this)
{
SetCapture() ;
//=== Координаты относительные (клиентские)
CRect r (mJRect) ;
r.DeflateRect (4, 4);
CClientDC do (this) ;
//====== Обрамляем выбранный рисунок
dc.FrameRect (Sr, SCBrush (RGB (192, 192, 255) ) ) ;
}
else
{
//=== Это происходит один раз при выходе из окна
ReleaseCapture () ;
Invalidate () ;
}
}
Здесь я решил применить другой способ обрамления — с помощью функции FrameRect. Она хороша тем, что не закрашивает внутренность прямоугольника, но обладает тем недостатком, что рамка не может иметь толщину более одной логической единицы. Приведем еще один вариант обрамления, использующий толстое перо и прозрачную кисть. Здесь приведен только тот фрагмент, в котором произошли изменения:
if (rChild. PtlnRect (point) )
{
if (GetCaptureO != this)
{
SetCapture () ;
CPen pen (PS_SOLID, 4, RGB (192, 192, 255) );
CClientDC dc(this) ;
dc. SelectObject (&pen) ;
CRect r (m_Rect) ;
//====== Уменьшаем прямоугольник
r .DeflateRect (4,4) ;
//=== Выбираем прозрачную кисть для того, чтобы
//=== не закрасить его содержимое
dc. SelectObject (GetStockObject (NULL_BRUSH) ) ;
dc. Rectangle (r) ;
}
}
На Рисунок 5.4 приведен вид приложения в момент, когда курсор мыши расположен над окном, отображающим данные неактивного документа. Рамка и курсор обозначают состояние готовности к произведению выбора.