Значок поиска в редакторе перекрывается областью ввода

Я пытаюсь создать элемент управления для редактирования поиска в MFC, значок которого постоянно отображается в окне элемента управления (независимо от состояния и текста элемента управления). Я написал что-то подобное много лет назад и работал очень хорошо, но код больше не работает на Windows 7 и новее (может быть, даже Vista, но не пробовал). В результате изображение, отображаемое в элементе управления, перекрывается с областью ввода (см. Рисунок ниже).

Идея кода:

  • иметь класс, полученный из CEdit (это обрабатывает рисование в OnPaint)
  • значок отображается справа, а область редактирования уменьшается в зависимости от размера значка
  • изменение размера выполняется по-разному для однострочного и многострочного редактирования. Для одной линии я звоню SetMargins и для многострочного редактирования я звоню SetRect.
  • это изменение размера применяется в PreSubclassWindow(), OnSize() а также OnSetFont()

Вот как применяется размер редактируемого ввода:

void CSymbolEdit::RecalcLayout()
{
int width = GetSystemMetrics( SM_CXSMICON );

if(m_hSymbolIcon)
{
if (GetStyle() & ES_MULTILINE)
{
CRect editRect;
GetRect(&editRect);

editRect.right -= (width + 6);

SetRect(&editRect);
}
else
{
DWORD dwMargins = GetMargins();
SetMargins(LOWORD(dwMargins), width + 6);
}
}
}

На следующем рисунке показана проблема с редактированием в одну строку (изображения были увеличены для лучшего обзора). Желтый фон предназначен только для подсветки, в реальном коде я использую COLOR_WINDOW Системный цвет. Вы можете видеть, что когда редактирование в одну строку содержит текст и ввод, изображение левой стороны закрашивается. Это не происходит с многострочным редактированием, где SetRect правильно устанавливает форматирование прямоугольника.

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

Я пытался использовать ExcludeClipRect удалить область редактирования, где отображается изображение.

CRect rc;
GetClientRect(rc);

CPaintDC dc(this);
ExcludeClipRect(dc.m_hDC, rc.right - width - 6, rc.top, rc.right, rc.bottom);

DWORD dwMargins = GetMargins();
SetMargins(LOWORD(dwMargins), width + 6);

Это, похоже, не влияет на результат.

Для справки: это метод рисования, написанный много лет назад и хорошо работающий в Windows XP, но больше не корректный.

void CSymbolEdit::OnPaint()
{
CPaintDC dc(this);

CRect rect;
GetClientRect( &rect );

// Clearing the background
dc.FillSolidRect( rect, GetSysColor(COLOR_WINDOW) );

DWORD dwMargins = GetMargins();

if( m_hSymbolIcon )
{
// Drawing the icon
int width = GetSystemMetrics( SM_CXSMICON );
int height = GetSystemMetrics( SM_CYSMICON );

::DrawIconEx(
dc.m_hDC,
rect.right - width - 1,
1,
m_hSymbolIcon,
width,
height,
0,
NULL,
DI_NORMAL);

rect.left += LOWORD(dwMargins) + 1;
rect.right -= (width + 7);
}
else
{
rect.left += (LOWORD(dwMargins) + 1);
rect.right -= (HIWORD(dwMargins) + 1);
}

CString text;
GetWindowText(text);
CFont* oldFont = NULL;

rect.top += 1;

if(text.GetLength() == 0)
{
if(this != GetFocus() && m_strPromptText.GetLength() > 0)
{
oldFont = dc.SelectObject(&m_fontPrompt);
COLORREF color = dc.GetTextColor();
dc.SetTextColor(m_colorPromptText);
dc.DrawText(m_strPromptText, rect, DT_LEFT|DT_SINGLELINE|DT_EDITCONTROL);
dc.SetTextColor(color);
dc.SelectObject(oldFont);
}
}
else
{
if(GetStyle() & ES_MULTILINE)
CEdit::OnPaint();
else
{
oldFont = dc.SelectObject(GetFont());
dc.DrawText(text, rect, DT_SINGLELINE | DT_INTERNAL | DT_EDITCONTROL);
dc.SelectObject(oldFont);
}
}
}

Я посмотрел на другие реализации подобных элементов управления редактирования, и все они теперь имеют ту же ошибку.

Очевидно, вопрос заключается в том, как исключить область изображения из области ввода элемента управления?

8

Решение

я считать что происходит в том, что CPaintDC звонки BeginPaint(), который отправляет WM_ERASEBKGND в поле редактирования. Я не мог игнорировать это, так что я думаю, что это отправлено, возможно, внутреннему STATIC окно? Точно сказать не могу.

призвание ExcludeClipRect() в вашем OnPaint() обработчик не будет ничего делать, потому что EDIT сбросит область отсечения на всю клиентскую область в любом BeginPaint() или свой WM_PAINT обработчик.

Тем не мение, EDIT отправляет WM_CTRCOLOREDIT его родителю непосредственно перед рисованием, но, по-видимому, после установки области отсечения. Так что вы можете позвонить ExcludeClipRect() там. Похоже на детали реализации, которые могут измениться в будущих версиях общих элементов управления. В самом деле, похоже, уже сделал это.

Я сделал быстрый тест без MFC на Windows 7, вот моя процедура окна:

LRESULT CALLBACK wnd_proc(HWND h, UINT m, WPARAM wp, LPARAM lp)
{
switch (m)
{
case WM_CTLCOLOREDIT:
{
const auto dc = (HDC)wp;
const auto hwnd = (HWND)lp;

RECT r;
GetClientRect(hwnd, &r);

// excluding the margin, but not the border; this assumes
// a one pixel wide border
r.left = r.right - some_margin;
--r.right;
++r.top;
--r.bottom;

ExcludeClipRect(dc, r.left, r.top, r.right, r.bottom);

return (LRESULT)GetStockObject(DC_BRUSH);
}
}

return ::DefWindowProc(h, m, wp, lp);
}

Затем я подкласс EDIT окно, чтобы нарисовать мою собственную иконку в WM_PAINT, а затем отправил сообщение, чтобы мне не пришлось рисовать все остальное самостоятельно.

LRESULT CALLBACK edit_wnd_proc(
HWND h, UINT m, WPARAM wp, LPARAM lp,
UINT_PTR  id, DWORD_PTR data)
{
switch (m)
{
case WM_PAINT:
{
const auto dc = GetDC(h);

// draw an icon

ReleaseDC(h, dc);
break;
}
}

return DefSubclassProc(h, m, wp, lp);
}

Обратите внимание, что я не мог позвонить BeginPaint() а также EndPaint() (эквивалент построения CPaintDC) в WM_PAINT потому что граница не будет нарисована. Я предполагаю, что это как-то связано с звонками BeginPaint() дважды (один раз вручную, один раз EDIT) и обработка WM_ERASEBKGND, YMMV, особенно с MFC.

Наконец, я установил поля сразу после создания EDIT:

SendMessage(
e, EM_SETMARGINS,
EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, margin));

Возможно, вам также придется обновить поля снова, если системный шрифт изменится.

0

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector