0

I am using the code in the first answer of this question: How can we vertically align text in edit box? to center the text in a CEdit control vertically.

Here is the used Class 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();
}

I have the new CEdit inherited control created like this:

// 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);
}

But the control shows no cursor and if I type a character it divorces itself into 2 pieces and acts very strange.

What could possibly cause this? Is there a later (better) version of doing this?

Community
  • 1
  • 1
Vinz
  • 3,030
  • 4
  • 31
  • 52
  • `top` - `bottom` = `106`. What other operations are you performing on `rectClient` between the call to `GetClientRect` and `ClientToScreen`? Are you sure these calls are being made on the correct/same window? – Captain Obvlious Sep 04 '14 at 22:44
  • I didn't want to post the code again. I linked the answer with the code. There you can see what happens with the clientRect. – Vinz Sep 05 '14 at 07:42
  • That just makes it less useful to people searching for a solution to this or similar problems. It also requires everyone that looks at your question to follow a link just to examine the code. Include the _relevant_ portions of code in your question and you reduce the amount of effort for _everyone else_. – Captain Obvlious Sep 05 '14 at 08:02
  • Alright, sorry. I added the complete source – Vinz Sep 05 '14 at 09:30

2 Answers2

0

Given this:

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

You can see that:

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

And

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

Those values look familiar:

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

So you have the same rectangle dimensions, the upper-left corner has simply been moved above and to the right of the top-left corner of the primary monitor.

If you have multiple monitors, remember that a monitor above the primary monitor has a negative top offset. Is your window located on a secondary monitor that is above the primary monitor?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • bottom-top = 106 and right-left = 27 still is strange as I created the CEditVC with the rect `CRect(10,10,100,100)`... The dimensions would be 90 for bottom-top and right-left, I think. And I have 2 monitors but its shown on the primary one :/ – Vinz Sep 05 '14 at 07:45
0

Okay so I just want to let you know that after lots of debugging I got behind the issue why the OnNcCalcSize method results in a strange output.

The OnNcCalcSize method gets called 3 times in an empty VS 2013 MFC project. The first two calls occur when there isnt even a dialog or CEditVC Control set up! That results in the ClientRect being 0,0,0,0. The code UINT uiCenterOffset = (rectClient.Height() - uiVClientHeight) / 2; will NOT work as expected as the calculated uiVClientHeight is something above 0 and is being subtracted from the clientRect height which is 0. The UINT will overflow and every further use will result in undefined behaviour.

So to work around this issue in latest MFC projects I came up with:

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;
}

So I just added the if-statement on top to check if the function deals with an empty CRect and if that is the case, just use the base method of CEdit.

I suppose the class was written for old C++ MFC environments like VS6 when MFC or WinApi procedures were a little different...

Vinz
  • 3,030
  • 4
  • 31
  • 52