在状态栏实现如编辑控件的光标移动 (VC/MFC)

来源:互联网 发布:leaflet.js api 编辑:程序博客网 时间:2024/06/01 08:52

背景:

 

MFC提供给我们一个基础状态栏类:CStatusBar,当我们在状态栏内显示的内容比较长,比较多时,我们可以把某一个PANE得宽度扩大,以便可以显示长的内容,但是如果我们的每一个PANE都要显示很多内容,而且有5个以上的PANE,问题就出来了,我们很难在中间做一个平衡。

 

方法:

可以在状态栏的PANE上,实现一个光标移动功能, 这样当内容较长时,在这个PANE上面做光标左右移动来显示出不完整的内容。

 

双击某个PANE显示光标, 左右键实现左右移动并显示不完整内容,状态栏失去焦点和大小调整后PANE内容恢复原状光标消失。我们继承基础类CStatusBar来完成我们的功能。

 

代码:

class MStatusBar : public CStatusBar

{

    ...


    int m_nActiveIndex;  // current active pane's index
    int m_nPreActIndex;  // previous active pane's index
    CString m_szActPaneText;  // current active pane's text content 
    CString m_szPreActPaneText;  // prevous active pane's text content
    int m_nTextLeft;  // left position of the displayed pane text
    int m_nTextRight;  // right position of the displayed pane text


    ...

};


void MStatusBar::OnLButtonDblClk(UINT nFlags, CPoint point)
{

    CStatusBarCtrl& statusBar = GetStatusBarCtrl();

    int i = -1;
    CRect rectPane;
    CPoint ptTmp(point);
    //ScreenToClient(&ptTmp);
    int nCount = GetCount();

    while (++i < nCount)
    {
        statusBar.GetRect(i, rectPane);
        if (rectPane.PtInRect(ptTmp) )
        {
            m_nActiveIndex = i;
            if( m_nPreActIndex != m_nActiveIndex )
            {
                // restore the text of previous active pane
                if( m_nPreActIndex != -1 )
                {
                    SetText(m_nPreActIndex, m_szPreActPaneText);
                    SetPaneText(m_nPreActIndex, m_szPreActPaneText);
                }
                // update previous by now active
                m_nPreActIndex = m_nActiveIndex;
                m_szActPaneText = GetText(i);
                if( m_szActPaneText.IsEmpty() ) m_szActPaneText = GetPaneText(i);
                if( m_szActPaneText.IsEmpty() )
                {
                    m_szPreActPaneText=m_szActPaneText;
                    return;
                }
                m_szPreActPaneText = m_szActPaneText;
                // get indices of two ends of a string
                m_nTextRight = m_szActPaneText.GetLength();
                m_nTextLeft = 0;


                // create caret
                ::CreateCaret(this->m_hWnd, (HBITMAP)NULL, 2, rectPane.Height());
                ::SetCaretPos(rectPane.left, 2);
                ::ShowCaret(this->m_hWnd);
                SetFocus();
            }
        }
    }

    CStatusBar::OnLButtonDblClk(nFlags, point);
}


void MStatusBar::OnKillFocus( CWnd* pNewWnd )
{
    RestorePanes();
}



BOOL MStatusBar::PreTranslateMessage(MSG* pMsg )
{
    if( pMsg->message == WM_KEYDOWN )
    {
        if( pMsg->wParam == VK_LEFT )
        {
            SetCaretPosEx(CARET_DIR_LEFT);
            return TRUE;
        }
        else if( pMsg->wParam == VK_RIGHT )
        {
            SetCaretPosEx(CARET_DIR_RIGHT);
            return TRUE;
        }
    }

    return CStatusBar::PreTranslateMessage(pMsg);
}


当然用OnChar来处理是最合理的, 但是我的项目比较大,好像之前的一些窗口把这个消息给劫走了, 我抓不住这个消息, 所以只有用PreTranslateMessage了。

 

#define CARET_DIR_LEFT            1
#define CARET_DIR_RIGHT            2

/******************************************************
*Params: nDirection: 1:Left, 2:Right
******************************************************/
BOOL MStatusBar::SetCaretPosEx(UINT nDirection)
{
    CWindowDC wdc(this);
    // Getting font dimension
    TEXTMETRIC tm;
    wdc.GetTextMetrics(&tm);
    int cxChar = tm.tmAveCharWidth;
    // Moving string infos
    /*SIZE cDim;
    CString strOpt_1 = GetPaneText(m_nActiveIndex);
    CString strOpt_2 = GetPaneText(m_nActiveIndex);
    int nLength = strOpt_1.GetLength();
    ::GetTextExtentPoint32(wdc.m_hDC, strOpt_1, nLength, &cDim);*/
    //Getting rect of active pane
    CRect rectAPane;
    GetItemRect(m_nActiveIndex, &rectAPane);

    int nDisLgt = rectAPane.Width()/cxChar;
    CString strOpt;

    POINT pt;
    int xAPane = rectAPane.left;

    if( m_nActiveIndex != -1 )
    {
        switch( nDirection )
        {
        case CARET_DIR_LEFT:
        {
            ::GetCaretPos(&pt);
            xAPane = pt.x - cxChar;
            if( xAPane > rectAPane.left)
            {
                ::SetCaretPos(xAPane, pt.y);

            }
            else
            {
                xAPane = rectAPane.left+1;
                ::SetCaretPos(xAPane, pt.y);
               
                m_nTextLeft--;
                if( m_nTextLeft < 0) m_nTextLeft = 0;
                strOpt = m_szActPaneText.Mid(m_nTextLeft);
                SetText(m_nActiveIndex, strOpt);
                SetPaneText(m_nActiveIndex, strOpt);
               
            }
            break;
        }
        case CARET_DIR_RIGHT:
        {
            ::GetCaretPos(&pt);
            xAPane = pt.x + cxChar;
            if( xAPane < rectAPane.right )
            {
                ::SetCaretPos(xAPane, pt.y);
            }
            else
            {
                xAPane = rectAPane.right-1;
                ::SetCaretPos(xAPane, pt.y);
               
                if( (m_nTextRight-m_nTextLeft) > nDisLgt )
                    m_nTextLeft++;
                if( m_nTextLeft > m_nTextRight) m_nTextLeft = m_nTextRight;
                strOpt = m_szActPaneText.Mid(m_nTextLeft);
                SetText(m_nActiveIndex, strOpt);
                SetPaneText(m_nActiveIndex, strOpt);
               
            }
            break;
        }
        default:
            break;
        }
        return TRUE;
    }

    return FALSE;
}

void MStatusBar::RestorePanes()
{
    ::DestroyCaret();
    if( m_nActiveIndex != -1 )
    {
        SetText(m_nActiveIndex, m_szActPaneText);
        SetPaneText(m_nActiveIndex, m_szActPaneText);
    }
    if( m_nPreActIndex != -1 )
    {
        SetText(m_nPreActIndex, m_szPreActPaneText);
        SetPaneText(m_nPreActIndex, m_szPreActPaneText);
    }
    m_nActiveIndex = -1;
    m_nPreActIndex = -1;
    m_szActPaneText = "";
    m_szPreActPaneText = "";
}

void MStatusBar::OnSize( UINT nType, int cx, int cy )
{
    RestorePanes();

    CStatusBar::OnSize(nType, cx, cy);
}


总结:

不是很难, 关键在状态栏获取焦点, 得到字体尺寸,然后左右移动一个简单算法。