【问题标题】:Search icon in edit control overlapped by input area编辑控件中的搜索图标与输入区域重叠
【发布时间】:2016-12-17 21:31:33
【问题描述】:

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

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

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

【问题讨论】:

  • 您的OnPaint 覆盖正在与编辑控件的绘制例程作斗争。它使用CPaintDC 手动绘制控件,有时它调用CEdit::OnPaint,然后又调用CPaintDC,然后是重新绘制客户区的默认处理。当编辑控件进入和失去焦点时,或者收到任何绘制消息时,这将失败。
  • CEdit::OnPaint() 仅用于多行编辑,这不是我关心的问题。我只使用单行编辑控件。我提到了多行编辑,因为在这种情况下设置边界可以正常工作。
  • 如果覆盖CWnd::OnCtlColor() 并从那里调用ExcludeClipRect() 会怎样?通过快速测试似乎可以正常工作,但没有 MFC。您可能需要再次更改 OnPaint() 中的剪辑以显示您自己的内容。
  • 我的回答有帮助吗?你收到了一个“提请注意”的悬赏通知,但从那以后你什么也没说。
  • 我很抱歉忽略了这一点,它最近完全不在我的关注范围内。我会检查你发布的内容,如果它有效,我会给你奖励积分(必须创建一个新的,希望有效)。

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


【解决方案1】:

认为发生了什么是CPaintDC 调用BeginPaint(),它发送一个WM_ERASEBKGND 到编辑框。我无法忽略它,所以我猜它可能被发送到内部STATIC 窗口?不确定。

OnPaint() 处理程序中调用ExcludeClipRect() 不会做任何事情,因为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);
}

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

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

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

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

【讨论】:

    【解决方案2】:

    看看这个教程......来自 www.catch22.net。它清楚地说明了如何将按钮插入到编辑控件中。虽然是一个Win32的例子,但是MFC的基本结构是使用win32 apis,所以可以即兴为MFC。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-13
      • 1970-01-01
      • 2021-06-26
      • 1970-01-01
      • 2012-11-14
      • 2019-01-31
      相关资源
      最近更新 更多