【问题标题】:Inherited CEdit Control (for vertical centered Text) behaves strange继承的 CEdit 控件(用于垂直居中的文本)行为奇怪
【发布时间】:2014-09-10 16:42:14
【问题描述】:

我正在使用此问题的第一个答案中的代码:How can we vertically align text in edit box? 将 CEdit 控件中的文本垂直居中。

这里是使用的类CEditVC

/// HEADER //////////////////////////////////////////

class CEditVC : public CEdit
{
public:
    CEditVC();

protected:
    CRect m_rectNCBottom;
    CRect m_rectNCTop;

public:
    virtual ~CEditVC();

protected:
    afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp);
    afx_msg void OnNcPaint();
    afx_msg UINT OnGetDlgCode();

    DECLARE_MESSAGE_MAP()
};

/// IMPLEMENTATION /////////////////////////////////////////

CEditVC::CEditVC()
    : m_rectNCBottom(0, 0, 0, 0)
    , m_rectNCTop(0, 0, 0, 0)
{
}

CEditVC::~CEditVC()
{
}

BEGIN_MESSAGE_MAP(CEditVC, CEdit)
    ON_WM_NCCALCSIZE()
    ON_WM_NCPAINT()
    ON_WM_GETDLGCODE()
END_MESSAGE_MAP()

void CEditVC::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) 
{
    CRect rectWnd, rectClient;

    //calculate client area height needed for a font
    CFont *pFont = GetFont();
    CRect rectText;
    rectText.SetRectEmpty();

    CDC *pDC = GetDC();

    CFont *pOld = pDC->SelectObject(pFont);
    pDC->DrawText("Ky", rectText, DT_CALCRECT | DT_LEFT);
    UINT uiVClientHeight = rectText.Height();

    pDC->SelectObject(pOld);
    ReleaseDC(pDC);

    //calculate NC area to center text.

    GetClientRect(rectClient);
    GetWindowRect(rectWnd);

    ClientToScreen(rectClient);

    UINT uiCenterOffset = (rectClient.Height() - uiVClientHeight) / 2;
    UINT uiCY = (rectWnd.Height() - rectClient.Height()) / 2;
    UINT uiCX = (rectWnd.Width() - rectClient.Width()) / 2;

    rectWnd.OffsetRect(-rectWnd.left, -rectWnd.top);
    m_rectNCTop = rectWnd;

    m_rectNCTop.DeflateRect(uiCX, uiCY, uiCX, uiCenterOffset + uiVClientHeight + uiCY);

    m_rectNCBottom = rectWnd;

    m_rectNCBottom.DeflateRect(uiCX, uiCenterOffset + uiVClientHeight + uiCY, uiCX, uiCY);

    lpncsp->rgrc[0].top +=uiCenterOffset;
    lpncsp->rgrc[0].bottom -= uiCenterOffset;

    lpncsp->rgrc[0].left +=uiCX;
    lpncsp->rgrc[0].right -= uiCY;

}

void CEditVC::OnNcPaint() 
{
    Default();

    CWindowDC dc(this);
    CBrush Brush(GetSysColor(COLOR_WINDOW));

    dc.FillRect(m_rectNCBottom, &Brush);
    dc.FillRect(m_rectNCTop, &Brush);
}

UINT CEditVC::OnGetDlgCode() 
{
    if(m_rectNCTop.IsRectEmpty())
    {
        SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
    }

    return CEdit::OnGetDlgCode();
}

我有这样创建的新 CEdit 继承控件:

// Header File
CEditVC *mp_inputUser;

// Source File
OnInitDialog()
{
mp_inputUser = new CEditVC();
mp_inputUser->Create(WS_VISIBLE | WS_CHILD, CRect(10,10,100,100), this, 1);
}

但是控件没有显示光标,如果我输入一个字符,它会分裂成两部分并且表现得很奇怪。

什么可能导致这种情况?是否有更高版本(更好的)这样做?

【问题讨论】:

  • top - bottom = 106。在对GetClientRectClientToScreen 的调用之间,您还对rectClient 执行了哪些其他操作?您确定这些调用是在正确/相同的窗口中进行的吗?
  • 我不想再次发布代码。我将答案与代码联系起来。在那里你可以看到 clientRect 发生了什么。
  • 这只是让人们寻找解决此问题或类似问题的方法变得不那么有用。它还要求每个查看您的问题的人都点击一个链接来检查代码。在你的问题中包含相关部分的代码,你可以减少其他人的工作量。
  • 好的,抱歉。我添加了完整的源代码

标签: c++ mfc cedit


【解决方案1】:

鉴于此:

ClientToScreen(rectClient);  // rectClient = {top=-2147483277 bottom=-2147483171 left=748 right=775}

你可以看到:

bottom-top = (-2147483171)-(-2147483277) = 106

right-left = (775)-(748) = 27

这些值看起来很熟悉:

GetClientRect(rectClient);   // rectClient = {top=0 bottom=106 left=0 right=27}

所以你有相同的矩形尺寸,左上角只是简单地移动到主显示器左上角的上方和右侧。

如果您有多个监视器,请记住主监视器上方的监视器具有负的top 偏移量。您的窗口是否位于主监视器上方的辅助监视器上?

【讨论】:

  • bottom-top = 106 和 right-left = 27 仍然很奇怪,因为我使用 rect CRect(10,10,100,100)...我认为。我有 2 台显示器,但它显示在主显示器上:/
【解决方案2】:

好的,我只想告诉你,经过大量调试后,我发现了 OnNcCalcSize 方法导致奇怪输出的问题。

OnNcCalcSize 方法在一个空的 VS 2013 MFC 项目中被调用了 3 次。前两个调用发生在甚至没有设置对话框或 CEditVC 控件时!这导致 ClientRect 为 0,0,0,0。代码UINT uiCenterOffset = (rectClient.Height() - uiVClientHeight) / 2; 将无法按预期工作,因为计算出的uiVClientHeight 大于0,并且正在从0 的clientRect 高度中减去。 UINT 将溢出,每次进一步使用都会导致未定义的行为。

所以要在我提出的最新 MFC 项目中解决这个问题:

void CEditVC::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
{
    CRect rectWnd, rectClient;

    GetClientRect(rectClient);

    // Workaround for calls with an empty Client Rect
    if (rectClient.Height() == 0)
    {
        CEdit::OnNcCalcSize(bCalcValidRects, lpncsp);
        return;
    }

    // Everything is back to default down here
    CFont *pFont = GetFont();
    CRect rectText;
    rectText.SetRectEmpty();

    CDC *pDC = GetDC();

    CFont *pOld = pDC->SelectObject(pFont);
    pDC->DrawText(L"Ky", rectText, DT_CALCRECT | DT_LEFT);
    UINT uiVClientHeight = rectText.Height();

    pDC->SelectObject(pOld);
    ReleaseDC(pDC);

    GetWindowRect(rectWnd);

    ClientToScreen(rectClient);

    UINT uiCenterOffset = (rectClient.Height() - uiVClientHeight) / 2;
    UINT uiCY = (rectWnd.Height() - rectClient.Height()) / 2;
    UINT uiCX = (rectWnd.Width() - rectClient.Width()) / 2;

    rectWnd.OffsetRect(-rectWnd.left, -rectWnd.top);
    m_rectNCTop = rectWnd;

    m_rectNCTop.DeflateRect(uiCX, uiCY, uiCX, uiCenterOffset + uiVClientHeight + uiCY);

    m_rectNCBottom = rectWnd;

    m_rectNCBottom.DeflateRect(uiCX, uiCenterOffset + uiVClientHeight + uiCY, uiCX, uiCY);

    lpncsp->rgrc[0].top += uiCenterOffset;
    lpncsp->rgrc[0].bottom -= uiCenterOffset;

    lpncsp->rgrc[0].left += uiCX;
    lpncsp->rgrc[0].right -= uiCY;
}

所以我只是在顶部添加了 if 语句来检查函数是否处理空 CRect,如果是这样,只需使用 CEdit 的基本方法。

我想这个类是为旧的 C++ MFC 环境(如 VS6)编写的,当时 MFC 或 WinApi 过程有点不同......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 2018-04-02
    • 2015-12-08
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    相关资源
    最近更新 更多