Изображение пользовательского флажка MFC VC++
Как получить флажок с 3 состояниями, чтобы использовать другое растровое изображение для состояния Indeterminate?
Я хочу изменить изображение, используемое моими флажками с 3 состояниями, чтобы использовать другое; элементы управления выполнены в стиле Win98, и неопределенное состояние таких флажков трудно отличить от отключенных флажков (вероятно, поэтому они изменили это для элементов управления в стиле WinXP, но я не могу использовать их из-за других деталей в моем проекте),
Я использую Visual C++ 2010, и я определил растровое изображение 8x8 в редакторе ресурсов VS. Идентификатор растрового изображения IDB_INDET_CHECK
,
Я не совсем уверен, какова стандартная "техника" для чего-то подобного; Я только начал изучать элементы управления Windows и MFC.
Моей первой попыткой было создать класс, CTriButton
, что вытекает из CButton
переопределить DrawItem
и попробуй нарисовать сам. Я тогда использовал SubclassDlgItem
превратить один из флажков в моем окне в этот класс (я думаю?). Это... вроде работает? Флажок больше не появляется, и если я нажимаю на то, где он должен быть, появляется пустой фрейм флажка, но больше ничего не происходит (и отладочное сообщение в моем коде не отправляется).
Вот соответствующий код, хотя я не уверен, что все это правильно. Во-первых, код из моего окна OnInitDialog
,
BOOL CAffixFilterDlg::OnInitDialog() // CAffixFilterDlg is my CDialog-derived window
{
CDialog::OnInitDialog(); // call basic version
// subclass a CButton-derived control with CTriButton
if ( CBipedHead.SubclassDlgItem(IDC_HEAD, this) ) // CBipedHead is a CTriButton member of CAffixFilterDlg, IDC_HEAD is a checkbox
SetWindowLong(CBipedHead.m_hWnd, GWL_STYLE, CBipedHead.GetStyle() | BS_OWNERDRAW); // set the ownerdraw style
else // subclassing didn't work
_ERROR("Subclassing failed."); // I do not see this error message, so SubclassDlgItem worked?
// initialization continues, but is not relevant...
UpdateWindow();
Invalidate();
return TRUE;
}
Далее код для моих пользовательских кнопок DrawItem
,
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
_DMESSAGE("Drawing TriButton"); // never see this message
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //Get device context object
int nWidth = GetSystemMetrics(SM_CXMENUCHECK);
int nMargin = ( nWidth - 8 ) / 2;
CRect textRt = lpDrawItemStruct->rcItem;
textRt.right = textRt.right - nWidth - nMargin;
CString text;
GetWindowText(text);
UINT textDrawState = DST_TEXT;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
textDrawState |= DSS_DISABLED;
dc.DrawState(CPoint(textRt.left, textRt.top), textRt.Size(), text, textDrawState, TRUE, 0, (CBrush*)NULL);
CRect rt = lpDrawItemStruct->rcItem; // initial rect is for entire button
rt.left = rt.right - nWidth; // set left margin
LONG center = ( rt.bottom + rt.top ) / 2;
rt.top = center - nWidth/2;
rt.bottom = center + nWidth/2;
UINT checkDrawState = DFCS_BUTTONCHECK;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
checkDrawState |= DFCS_INACTIVE;
if ( lpDrawItemStruct->itemState & ODS_CHECKED )
checkDrawState |= DFCS_CHECKED;
else if ( GetCheck() == BST_INDETERMINATE ) {
_VMESSAGE("Indeterminate; custom draw.");
CBitmap indet_check = CBitmap();
indet_check.LoadBitmap(IDB_INDET_CHECK);
CPoint pt = CPoint(rt.left + nMargin, rt.top + nMargin);
CSize sz = CSize(8, 8);
dc.DrawState(pt, sz, &indet_check, DST_BITMAP|DSS_NORMAL);
}
dc.DrawFrameControl(rt, DFC_BUTTON, checkDrawState);
}
2 ответа
Не ответ, а ответ: это обычай CCheckBox
Я обнаружил, что более или менее позволяет то, что я хочу. По умолчанию он не допускает 3 состояния, но я исправил это с помощью некоторых собственных настроек. Я не уверен на 100%, что это работает из коробки (у меня были некоторые проблемы, которые, кажется, не из-за моих правок, но я не уверен), но это было решение, которое я используемый. Я не собираюсь называть это ответом, однако, в случае, если кто-то может подсмотреть, что случилось с моим кодом, и хочет осветить меня.
В OnInitDialog() вам нужно вызывать InvalidateRect() после изменения стиля окна, иначе он не знает, что его нужно перерисовать. Также полезно вызывать UpdateWindow() после изменения стилей окна. Некоторая информация обычно кэшируется общими элементами управления и не будет подтверждать изменение, пока не будет вызван UpdateWindow().
В DrawItem() вы несете ответственность за отображение всех состояний элемента управления. Вы не должны вызывать CButton::DrawItem(), так как он ничего не делает. Попробуйте что-то вроде следующего:
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CBitmap indet_check
_DMESSAGE("Drawing TriButton"); // I never see this message
int checkState = GetCheck();
if ( checkState == BST_CHECKED )
{
indet_check.LoadBitmap(IDB_INDET_CHECK);
}
else if ( checkState == BST_UNCHECKED )
{
indet_check.LoadBitmap(IDB_INDET_UNCHECKED);
}
else if ( checkState == BST_INDETERMINATE )
{
indet_check.LoadBitmap(IDB_INDET_INDETERMINATE);
}
// ... rest of your drawing code here ...
// don't forget to draw focus and push states too ;)
}
Приложение:
Я не могу поверить, что я пропустил этот первый раз, но ваш звонок SubclassDlgItem
вероятно, не имеет желаемого эффекта. Этот вызов приводит к тому, что сообщения, предназначенные для кнопки, сначала обрабатываются родительским окном управления. Поскольку реализация по умолчанию DrawItem
в CWnd
(суперкласс CDialog) ничего не делает, чтобы сообщение никогда не передавалось элементу управления.
Замените это следующим фрагментом, и все должно быть в порядке:
HWND hWndButton;
GetDlgItem(IDC_HEAD, &hWndButton);
CBipedHead.SubclassWindow(hWndButton);
Два примечания здесь:
- Обычно не рекомендуется использовать одно и то же соглашение об именах как для классов, так и для членов классов. Это делает для запутанного чтения.
- Я предполагаю, что вы всегда компилируете и работаете в режиме релиза. Если вы - нет. Это предотвращает выдачу утверждений и дает вам понять, что что-то не так.