8

我正在尝试在 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);
      }
    }
}

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

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

4

2 回答 2

0

认为正在发生的事情是CPaintDC调用BeginPaint(),它将 a 发送WM_ERASEBKGND到编辑框。我无法忽略它,所以我猜它可能被发送到内部STATIC窗口?没有把握。

调用ExcludeClipRect()您的OnPaint()处理程序不会做任何事情,因为EDIT会将剪辑区域重置为任一BeginPaint()或它自己的WM_PAINT处理程序中的整个客户区。

但是,在绘制自身之前向其父级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);
}

请注意,我无法调用BeginPaint()and EndPaint()(相当于构造 a CPaintDC),WM_PAINT因为不会绘制边框。我猜这与调用BeginPaint()两次(一次手动,一次通过EDIT)和处理WM_ERASEBKGND. YMMV,尤其是 MFC。

最后,我在创建后立即设置边距EDIT

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

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

于 2016-08-29T18:45:15.573 回答
0

看看这个教程......来自 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 来限制文本控件。

于 2019-11-22T05:26:44.450 回答