代码之家  ›  专栏  ›  技术社区  ›  Vinz

继承的CEdit控件(用于垂直居中的文本)行为异常

  •  0
  • Vinz  · 技术社区  · 10 年前

    我在这个问题的第一个答案中使用了代码: 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);
    }
    

    但是控件没有显示光标,如果我键入一个字符,它会将自己分割成两部分,并且动作非常奇怪。

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

    2 回复  |  直到 7 年前
        1
  •  0
  •   Remy Lebeau    10 年前

    鉴于此:

    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 抵消窗口是否位于主监视器上方的辅助监视器上?

        2
  •  0
  •   Vinz    10 年前

    好的,我只是想让你知道,经过大量的调试,我了解了为什么 OnNcCalcSize 方法会导致奇怪的输出。

    OnNcCalcSize方法在空VS 2013 MFC项目中被调用3次。前两个调用发生在没有对话框或CEditVC控件设置的情况下!这导致ClientRect为0,0,0,0。代码 UINT uiCenterOffset = (rectClient.Height() - uiVClientHeight) / 2; 将无法按预期工作 uiVClientHeight 大于0,并从clientRect高度中减去 0 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过程稍有不同。。。