提问人:Marius Bancila 提问时间:8/11/2016 最后编辑:Marius Bancila 更新时间:11/22/2019 访问量:1144
编辑控件中的搜索图标与输入区域重叠
Search icon in edit control overlapped by input area
问:
我正在尝试在 MFC 中创建一个搜索编辑控件,该控件始终在控件窗口中显示一个图标(无论控件的状态和文本如何)。我多年前就写过这样的东西并且运行良好,但代码不再适用于 Windows 7 和更新版本(甚至可能是 Vista,但没有尝试过)。发生的情况是控件中显示的图像与输入区域重叠(见下图)。
代码背后的想法:
- 有一个派生自的类(在 OnPaint 中处理绘画)
CEdit
- 图标显示在右侧,编辑区域根据图标的大小缩小
- 对于单行和多行编辑,调整大小的方式不同。对于单行,我调用 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);
}
}
}
我查看了类似编辑控件的其他实现,它们现在都有相同的错误。
显然,问题是如何从控件的输入区域中排除图像区域?
答:
我认为正在发生的事情是调用,它会将 发送到编辑框。我无法忽略它,所以我想它正在被发送到一个内部窗口?不确定。CPaintDC
BeginPaint()
WM_ERASEBKGND
STATIC
调用处理程序不会执行任何操作,因为会将剪辑区域重置为任一处理程序或其自己的处理程序中的整个工作区。ExcludeClipRect()
OnPaint()
EDIT
BeginPaint()
WM_PAINT
但是,在绘制本身之前,但似乎是在设置剪切区域之后,将 a 发送到其父级。所以你可以打电话给那里。听起来像是一个实现细节,可能会随着公共控件的未来版本而改变。事实上,它似乎已经这样做了。EDIT
WM_CTRCOLOREDIT
ExcludeClipRect()
我在 Windows 7 上做了一个没有 MFC 的快速测试,这是我的窗口过程:
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);
}
请注意,我无法调用 and(相当于构造 ),因为边界不会被绘制。我猜这与调用两次(一次手动,一次)和处理有关。YMMV,尤其是 MFC。BeginPaint()
EndPaint()
CPaintDC
WM_PAINT
BeginPaint()
EDIT
WM_ERASEBKGND
最后,我在创建后立即设置边距:EDIT
SendMessage(
e, EM_SETMARGINS,
EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, margin));
如果系统字体发生更改,您可能还需要再次更新页边距。
看看这个教程...从 www.catch22.net。它清楚地说明了如何将按钮插入编辑控件。虽然这是一个 Win32 示例,但可以即兴使用 MFC,因为 MFC 的基本结构是使用 win32 api。
http://www.catch22.net/tuts/win32/2001-05-20-insert-buttons-into-an-edit-control/#
它使用 WM_NCCALCSIZE 来限制文本控件。
评论
OnPaint
CPaintDC
CEdit::OnPaint
CPaintDC
CEdit::OnPaint()
只要求进行多行编辑,这不是我关心的问题。我只使用单行编辑控件。我提到了多行编辑,因为在这种情况下,设置边界可以正常工作。CWnd::OnCtlColor()
ExcludeClipRect()
OnPaint()