编辑控件中的搜索图标与输入区

时间:2016-08-11 08:39:22

标签: c++ visual-c++ mfc paint editcontrol

我正在尝试在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正确设置格式矩形的多行编辑,不会发生这种情况。

enter image description here

我尝试使用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);
      }
    }
}

我已经看过类似编辑控件的其他实现,现在它们都有相同的错误。

显然,问题是如何从控件的输入区域中排除图像区域?

2 个答案:

答案 0 :(得分:0)

认为正在发生的是CPaintDC调用BeginPaint(),它会向编辑框发送WM_ERASEBKGND。我无法忽略它,所以我猜它被发送到内部STATIC窗口?不确定。

ExcludeClipRect()处理程序中调用OnPaint()将无法执行任何操作,因为EDIT会将剪切区域重置为BeginPaint()或其自身{{{}中的整个客户区域1}}处理程序。

但是,WM_PAINT只是在绘制之前向其父级发送EDIT,但似乎在设置裁剪区域之后。所以你可以在那里打电话给WM_CTRCOLOREDIT。听起来像一个实现细节,可能会随着常见控件的未来版本而改变。事实上,它似乎已经这样做了。

我在Windows 7上没有使用MFC进行快速测试,这是我的窗口程序:

ExcludeClipRect()

然后我将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的处理有关。 YMMV,尤指MFC。

最后,我在创建WM_ERASEBKGND后设置了边距:

EDIT

如果系统字体发生变化,您可能还需要再次更新边距。

答案 1 :(得分:0)

从www.catch22.net看一下本教程...。它清楚地显示了如何在编辑控件中插入按钮。尽管这是Win32的示例,但由于MFC的基本结构是使用win32 api,因此可以将其即兴应用于MFC。

http://www.catch22.net/tuts/win32/2001-05-20-insert-buttons-into-an-edit-control/#

它使用WM_NCCALCSIZE来限制文本控件。