На вопрос из выпуска №27 о пунктирной рамке вокруг кнопки:
Предложенный Александром Шаргиным вариант с тулбаром врядли можно признать удовлетворительным. Диалог не получит сообщение от тулбара да и программное создание кнопки… Можно, конечно, но… :-( . Наиболее приемлемый выход – использование самопрорисовывающихся элементов управления. Достоинство этого метода – нарисовать можно всё, что угодно! :-))). А в вопросе Максима Чучуйко есть ещё подвопрос: А должна ли кнопка вообще получать фокус?.
В общем, плоскую кнопку, не получающую фокус совсем сделать достаточно просто:
1) Создаём класс
CFlatButton: public CButton;
2) Добавляем переменные:
protected:
BOOL bMouseCaptured;
CWnd* pOldFocus;
В конструкторе инициализируем:
bMouseCaptured = FALSE;
pOldFocus = NULL;
3) Добавляем методы:
protected:
void CFlatButton::SetOldFocus {
// Закомментировать тело метода, если кнопка может получать фокус.
if (pOldFocus) pOldFocus->SetFocus;
pOldFocus =NULL;
}
Добавляем обработчики сообщений:
void CFlatButton::OnSetFocus(CWnd* pOldWnd) {
CButton::OnSetFocus(pOldWnd);
if (!pOldFocus) // Дабы не было проблем с модальными окнами, вызываемыми по нажатию этой кнопки.
pOldFocus = pOldWnd;
}
void CFlatButton::OnLButtonUp(UINT nFlags, CPoint point) {
CButton::OnLButtonUp(nFlags, point);
CRect rectBtn;
GetClientRect(rectBtn);
if (rectBtn.PtInRect(point) && GetCapture != this) {
bMouseCaptured = TRUE;
SetCapture;
Invalidate(FALSE);
}
SetOldFocus;
}
void CFlatButton::OnMouseMove(UINT nFlags, CPoint point) {
CRect rectBtn;
GetClientRect(rectBtn);
if (rectBtn.PtInRect(point)) {
BOOL bNeedUpdate =FALSE;
if (!bMouseCaptured) bNeedUpdate = TRUE;
bMouseCaptured = TRUE;
SetCapture;
if (bNeedUpdate) Invalidate(FALSE);
} else {
bMouseCaptured = FALSE;
ReleaseCapture;
SetOldFocus;
Invalidate(FALSE);
}
CButton::OnMouseMove(nFlags, point);
}
И, самое интересное… :-))) Перекрываем виртуальный метод:
void CFlatButton::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
// Test WS_TABSTOP
ASSERT(!(GetStyle & WS_TABSTOP));
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rectAll;
GetClientRect(rectAll);
CString text;
GetWindowText(text);
int save = pDC->SaveDC;
CRect rectText(rectAll);
rectText.DeflateRect(2,2);
CBrush bkBr(GetSysColor(COLOR_3DFACE));
pDC->FillRect(rectAll,&bkBr);
UINT state = lpDIS->itemState;
if (state & ODS_SELECTED) {
// Нажатое состояние
rectText.OffsetRect(1,1);
pDC->DrawEdge(rectAll, BDR_SUNKENOUTER, BF_RECT);
} else {
if (bMouseCaptured) {
pDC->DrawEdge(rectAll, BDR_RAISEDINNER, BF_RECT);
}
}
pDC->DrawText(text, rectText, DT_SINGLELINE|DT_VCENTER|DT_CENTER|DT_TOP);
pDC->RestoreDC(save);
}
Использование: очень просто. Ставим на шаблоне диалога кнопку, убираем стиль WS_TABSTOP, ставим стиль WS_OWNERDRAW. В ClassWizard'е сопоставляем ей переменную типа CButton, затем тип переменной вручную меняем на CFlatButton. И всё. Далее – как с обычной кнопкой. У меня (VC++ 5.0) – работает.