Нужно ли мне выбирать HBRUSH при замене фона в поле редактирования?

Моя цель - заменить фон для элемент управления для редактирования. Мой текущий код делает это:

HBITMAP hBmp = ::LoadBitmap(hInstance, MAKEINTRESOURCE(BKGND_ID));
HBRUSH hBkgndBrush = ::CreatePatternBrush(hBmp);
::DeleteObject(hBmp);


HBRUSH CDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    // TODO:  Change any attributes of the DC here

    if(pWnd->GetDlgCtrlID() == MY_CTRL_ID && hBkgndBrush)
    {
        hbr = hBkgndBrush;

        //Do I need to select it?
        //pDC->SelectObject(hbr);   //This line?

        pDC->SetBkMode(TRANSPARENT);
    }

    // TODO:  Return a different brush if the default is not desired
    return hbr;
}

Вопрос в том, нужно ли мне выбирать hbr перед его возвратом? (См. Закомментированную строку выше.) Мне кажется, что это делается в обоих направлениях в разных примерах в Интернете.

РЕДАКТИРОВАТЬ: Также забыл упомянуть, я переопределяю WM_ERASEBKGND как таковой:

HDC hDc = ::GetDC(hWnd);
if(hDc)
{
    RECT rc = {0};
    ::GetClientRect(hWnd, &rc);

    ::FillRect(hDc, &rc, hBkgndBrush);

    ::ReleaseDC(hWnd, hDc);
}

EDIT2: я создал небольшой образец проекта MFC, чтобы проиллюстрировать проблему. По сути, когда я быстро перемещаю приложение с экрана, а затем обратно, оно создает этот визуальный «сбой», но только если элемент управления не имеет стиля ES_MULTILINE:

введите здесь описание изображения


person c00000fd    schedule 13.06.2016    source источник
comment
Вам не нужно выбирать кисть. То, как оно у вас есть, правильно. Какие проблемы у вас сейчас? Это одно большое растровое изображение, которое покрывает весь диалог и элементы управления редактированием?   -  person Barmak Shemirani    schedule 13.06.2016
comment
@BarmakShemirani: Когда я очень быстро перемещаю свое окно с экрана и обратно, кажется, что на фоне появляются странные повторяющиеся артефакты. Мне нужно, чтобы эта кисть / растровое изображение было того же размера, что и элемент редактирования? Или больше?   -  person c00000fd    schedule 13.06.2016
comment
Я понимаю что ты имеешь ввиду. Я не знаю, как с этим справиться. Я думаю, вам нужно заставить дочерний элемент управления перерисоваться. Размер кисти изменить не поможет. --- В несвязанной проблеме вы можете поместить это во вторую строку: if (nCtlColor == CTLCOLOR_DLG) return hbr; или, если bitmapBrush покрывает весь диалог, используйте SetBrushOrg для выравнивания дочернего растрового изображения, которое будет выровнено с фоновым растровым изображением.   -  person Barmak Shemirani    schedule 13.06.2016
comment
Проиллюстрированный артефакт проистекает из противоречивого происхождения кисти. Вы можете прочитать об этом здесь: msdn.microsoft.com/en-us/library/windows/desktop/   -  person Adrian McCarthy    schedule 15.06.2016
comment
@AdrianMcCarthy: Да, согласен. Хотя как бы это помогло мне решить этот вопрос?   -  person c00000fd    schedule 15.06.2016
comment
Вот почему я оставил комментарий, а не ответ. Тщательное понимание проблемы может привести к ответу. В общем, попытка изменить отрисовку элемента управления редактированием чревата ловушками, поскольку он не соответствует всем передовым методам (например, он выполняет инкрементные обновления из других мест, кроме обработчика рисования) и не предоставляет достаточного количества крючков для получения все правильно в каждом случае.   -  person Adrian McCarthy    schedule 15.06.2016
comment
@AdrianMcCarthy: Да, я согласен с этим.   -  person c00000fd    schedule 15.06.2016


Ответы (1)


Когда фоновая кисть создается из растрового изображения с использованием CreatePatternBrush, некоторые «повторяющиеся артефакты» могут возникать во время изменения размера или перемещения диалогового окна.

Чтобы удалить эти артефакты, принудительно перерисовывайте дочерние элементы управления в ответ на сообщение ON_WM_WINDOWPOSCHANGED:

void CMyDialog::OnWindowPosChanged(WINDOWPOS *wndpos)
{
    CDialog::OnWindowPosChanged(wndpos);

    CWnd *wnd = GetWindow(GW_CHILD);
    while (wnd)
    {
        wnd->Invalidate(TRUE);
        wnd = wnd->GetWindow(GW_HWNDNEXT);
    }
}

or

void CMyDialog::OnWindowPosChanged(WINDOWPOS *wndpos)
{
    CDialog::OnWindowPosChanged(wndpos);
    edit1.Invalidate(FALSE);
    edit2.Invalidate(FALSE);
    ...
}

OnCtlColor переопределение будет следующим:

HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* wnd, UINT nCtlColor)
{
    if (nCtlColor == CTLCOLOR_DLG)
        return CDialogEx::OnCtlColor(pDC, wnd, nCtlColor); 
    pDC->SetBkMode(TRANSPARENT);
    return hBkgndBrush;
}

Вы можете добавить другие условия, основанные на wnd или nCtlColor, только для изменения фона элемента управления редактированием.

person Barmak Shemirani    schedule 13.06.2016
comment
Спасибо. Я обновил свой ответ более подробной информацией. Итак, возвращаясь к вашим предложениям, к сожалению, вызов SetBrushOrgEx(hDC, 0, 0, NULL); ничего не изменил, и аннулирование моего контроля при каждом перемещении окна, вероятно, решило бы это, за исключением того, что он также добавляет неприятное видимое мерцание. Если подумать, проблема с артефактом, похоже, возникла из-за моего переопределения WM_ERASEBKGND. Неужели это так? Делает ли FillRect что-то, о чем я не знаю? - person c00000fd; 13.06.2016
comment
Какую операционную систему ты используешь? Я тестировал свой код только в Win10. Не отменяйте WM_ERASEBKGND. Также OnEraseBkgnd(CDC* pDC) уже имеет ручку постоянного тока. - person Barmak Shemirani; 14.06.2016
comment
Спасибо. Ты прав. К сожалению, это все еще не работает. Я обновил свой ответ небольшим образцом проекта. Можете ли вы проверить, делает ли он то же самое с вашей стороны? Также каким-то образом, если я добавлю стиль ES_MULTILINE, проблема исчезнет. - person c00000fd; 14.06.2016
comment
Вы не добавили OnWindowPosChanged. Также вы не сказали, какую версию Windows используете (я не вижу мерцания в Windows 10 и 7). Попробуйте Invalidate(FALSE) См. Обновленный ответ. - person Barmak Shemirani; 15.06.2016
comment
Да, вызов Invalidate(FALSE) на моем элементе управления каждый раз при перемещении родительского окна исправит эту ошибку. Тем не менее, я не уверен, что это хорошее решение, потому что: 1) оно тратит циклы ЦП без причины и 2) не решает проблему. Он просто исправляет это. Вот почему я им не пользовался. Хотя да, я буду рассматривать это в крайнем случае ... - person c00000fd; 15.06.2016
comment
Что касается затронутых ОС, я провел несколько тестов. Интересно, что он отлично работает на XP, а затем перестает работать с Vista и до последней версии ОС. Так что я более склонен называть это ошибкой Windows. (Если вы, ребята, не покажете мне, что я здесь делаю не так.) Очевидно, многоуровневый контроль работает нормально. - person c00000fd; 15.06.2016
comment
Если элемент управления многострочным редактированием имеет вертикальную / горизонтальную прокрутку или автоматическую прокрутку, проблема усугубляется (используйте другое решение, например элемент HTML). Если это всего лишь однострочный элемент управления, используйте указанное выше решение. Это не тратит циклы процессора. - person Barmak Shemirani; 15.06.2016
comment
В вашем OnWindowPosChanged я бы, наверное, ограничился if(!(lpWndpos->flags & SWP_NOMOVE) && !(::GetWindowLongPtr(hWnd, GWL_STYLE) & ES_MULTILINE)){::InvalidateRect(hWnd, NULL, FALSE);} - person c00000fd; 16.06.2016
comment
Убедитесь, что элемент управления многострочным редактированием не имеет полосы прокрутки или функции автоматической прокрутки. Кажется, что прокручиваемые элементы управления редактированием поддерживают только сплошные цвета кисти. - person Barmak Shemirani; 19.06.2016