1. 程式人生 > >VC對話方塊實現新增滾動條實現滾動效果

VC對話方塊實現新增滾動條實現滾動效果

對話方塊滾動條及滾動效果實現,用的api主要有: ScrollWindow, SetScrollInfo, GetScrollInfo, SetWindowOrgEx。涉及的資料結構為SCROLLINFO。

實現的原理為:設定視窗顯示的滾動條;響應滾動條的訊息,在其中記錄視窗原點的x和y座標,並滾動視窗;在OnPaint中設定視窗原點,並繪圖。

SCROLLINFO

複製程式碼
typedef struct tagSCROLLINFO
{
    UINT    cbSize;
    UINT    fMask;
    int     nMin;
    int     nMax;
    UINT    nPage;
    
int nPos; int nTrackPos; } SCROLLINFO, FAR *LPSCROLLINFO;
複製程式碼

cbSize: SCROLLINFO結構長度位元組數,該值在設定和查詢引數時都必須填寫。
fMask: 指定結構中的哪些成員是有效,該值共有如下5種選擇,可以選擇多種用“OR”組合起來,該值在設定和查詢引數時都必須填寫。

SIF_ALL : 整個結構都有效
SIF_DISABLENOSCROLL : 該值僅在設定引數時使用,視控制元件引數設定的需要來對本結構的成員進行取捨。
SIF_PAGE : nPage成員有效
SIF_POS : nPos成員有效
SIF_RANGE : nMin和nMax成員有效

nMin: 滾動範圍最小值
nMax: 滾動範圍最大值
nPage: 頁尺寸,用來確定比例滾動框的大小,一般設定為視窗在寬或高,分別對應用於橫向滾動條和豎向滾動條
nPos: 滾動框的位置
nTrackPos: 拖動時滾動框的位置,該引數只能查詢,不能設定。

API 及其引數解釋

BOOL SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint);

該函式用指定的座標設定裝置環境的視窗原點。
hdc:指向裝置環境的控制代碼。
X:指定新視窗原點的邏輯X座標。
Y:指定新視窗原點的邏輯Y座標。
lpPoint:指向POINT結構的指標,先前的視窗原點存放在此結構中,如果lpPoint的值為NULL,則什麼也沒返回。
返回值:如果函式呼叫成功,返回值為非零值,否則為零。

void ScrollWindow(int xAmount, int yAmount, LPCRECT lpRect = NULL, LPCRECT lpClipRect = NULL);

XAmount: [in]指定水平滾動的距離,以裝置單位計。如果視窗類風格為CS_OWNDC或CS_CLASSDC,則此引數則使用邏輯單位而非裝置單位。當向左滾動窗體內容時,引數值必須為負。
YAmount: [in]指定垂直滾動的距離,以裝置單位計。如果視窗類風格為CS_OWNDC或CS_CLASSDC,則此引數則使用邏輯單位而非裝置單位。當向上滾動窗體內容時,引數值必須為負。
lpRect: [in]指向RECT結構的指標,該結構指定了將要滾動的客戶區範圍。若此引數為NULL,則整個客戶區域將被滾動。
lpClipRect: [in]指向RECT結構的指標,該結構指定了要滾動的裁剪區域。只有這個矩形中的位才會被滾動。在矩形之外的位不會被影響,即使它們是在lpRect矩形之內。假如lpClipRect為NULL,則不會在滾動矩形上進行裁剪。

BOOL SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE);

fnBar:指定被設定引數的滾動條的型別。這個引數可以是下面值,含義如下:
SB_CTL:設定滾動條控制元件。而引數hwnd必須是滾動條控制元件的控制代碼。
SB_HORZ:設定所給定的窗體上標準水平滾動條引數。
SB_VERT:設定所給定的窗體上標準垂直滾動條引數。
lpsi:指向SCROLLINFO結構。在呼叫SetScrollInfo之前,設定SCROLLINFO結構中cbSize成員以標識結構大小,設定成員fMask以說明待設定的滾動條引數,並且在適當的成員中制定新的引數值。成員fMask可以為下面所列複合值,含義如下:
SIF_DISABLENOSCROLL:如果滾動條的新引數使其為沒必要,則使滾動條無效而不再移動它。
SIF_PAGE:設定滾動頁碼值到由Ipsi指向的SCROLLINFO結構的nPage成員中。
SIF_POS:設定滾動位置值到由lpsi指向的SCROLLINFO結構的nPos成員中。
SIF_RANGE:設定滾動範圍值到由lpsl指向的SCROLLINFO結構的nMin和nMax成員中。
fRedraw:指定滾動條是否重畫以反映滾動條的變化。如果這個引數為TRUE,滾動條將被重畫,否則不被重畫。

BOOL GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, UINT nMask = SIF_ALL);
  讀取滾動條的資訊,資料從lpScrollInfo中返回。各引數意義同SetScrollInfo

例項

1.在初始化等需要新增滾動條時,使用下面的程式碼新增滾動條。注意:不限於初始化,程式需要時都可以重新設定

複製程式碼
    // 給對話方塊新增滾動條 SB_VERT:垂直方向,SB_HORZ:水平方向
    SCROLLINFO si;                                          
    si.cbSize = sizeof(SCROLLINFO);                          
    si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;              
    si.nPos = 0;
    si.nMin = 0;  
    si.nMax = rc.Height() * 5;  
    si.nPage = rc.Height();  
    SetScrollInfo(SB_VERT, &si, FALSE);
    si.nMax = rc.Width() * 5;  
    si.nPage = rc.Width();  
    SetScrollInfo(SB_HORZ, &si, FALSE);
複製程式碼

2.響應WM_VSCROLL 和 WM_HSCROLL 訊息,在訊息響應函式中滾動視窗,並記錄需要顯示的視窗原點。

  ON_WM_VSCROLL()
  ON_WM_HSCROLL()

複製程式碼
void CPrintDlgAppDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    SCROLLINFO si = {sizeof(si)};
    si.fMask = SIF_ALL;
    GetScrollInfo(SB_VERT, &si);

    int nPrevPos = si.nPos;
    switch(nSBCode)
    {
    case SB_TOP:
        si.nPos = si.nMin;
        break;
    case SB_BOTTOM:
        si.nPos = si.nMax;
        break;
    case SB_LINEUP:
        si.nPos -= 15;
        break;
    case SB_LINEDOWN:
        si.nPos += 15;
        break;
    case SB_PAGEUP:
        si.nPos -= si.nPage;
        break;
    case SB_PAGEDOWN:
        si.nPos += si.nPage;
        break;
    case SB_THUMBTRACK:
        si.nPos = si.nTrackPos;
        break;
    default:
        break;
    }
    si.fMask = SIF_POS;
    SetScrollInfo(SB_VERT, &si, TRUE);
    if (si.nPos != nPrevPos)
    {
        m_nYPos += si.nPos - nPrevPos;
        ScrollWindow(0, nPrevPos - si.nPos, NULL, NULL);
        Invalidate(FALSE);
        UpdateWindow();
    }

    CDialog::OnVScroll(nSBCode, nPrevPos, pScrollBar);
}

void CPrintDlgAppDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    SCROLLINFO si = {sizeof(si)};
    si.fMask = SIF_ALL;
    GetScrollInfo(SB_HORZ, &si);

    int nPrevPos = si.nPos;
    switch(nSBCode)
    {
    case SB_LEFT:
        si.nPos = si.nMin;
        break;
    case SB_RIGHT:
        si.nPos = si.nMax;
        break;
    case SB_LINELEFT:
        si.nPos -= 15;
        break;
    case SB_LINERIGHT:
        si.nPos += 15;
        break;
    case SB_PAGELEFT:
        si.nPos -= si.nPage;
        break;
    case SB_PAGERIGHT:
        si.nPos += si.nPage;
        break;
    case SB_THUMBTRACK:
        si.nPos = si.nTrackPos;
        break;
    default:
        break;
    }
    si.fMask = SIF_POS;
    SetScrollInfo(SB_HORZ, &si, TRUE);
    if (si.nPos != nPrevPos)
    {
        m_nXPos += si.nPos - nPrevPos;
        ScrollWindow(nPrevPos - si.nPos, 0, NULL, NULL);
        Invalidate(FALSE);
        UpdateWindow();
    }

    CDialog::OnHScroll(nSBCode, nPrevPos, pScrollBar);
}
複製程式碼

3.在OnPaint訊息中,設定視窗原點並繪圖。需要注意的時,為了避免滾動時圖形疊加,需要建立一塊記憶體DC,其大小應為視窗滾動區域的大小。

複製程式碼
void CPrintDlgAppDlg::OnPaint()
{
    CPaintDC dc(this); // device context for painting

    // 用指定的座標設定裝置環境的視窗原點
    SetWindowOrgEx(dc.m_hDC, m_nXPos, m_nYPos, NULL);

    // 建立一塊和視窗滑動區域一樣大的記憶體DC,這樣才不會出現視窗圖形疊加的問題
    CRect rc(0, 0, 0, 0);
    SCROLLINFO si = {sizeof(SCROLLINFO)};
    si.fMask = SIF_RANGE;
    GetScrollInfo(SB_VERT, &si);
    rc.bottom = si.nMax;
    GetScrollInfo(SB_HORZ, &si);
    rc.right = si.nMax;
    

    CDC memDC;
    memDC.CreateCompatibleDC(&dc);
    CBitmap memBitmap;
    memBitmap.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());
    CBitmap* pOldBmp = memDC.SelectObject(&memBitmap);
    memDC.FillSolidRect(&rc, RGB(0, 0, 0));
    CFont font;
    font.CreateFont(20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
        DEFAULT_PITCH | FF_SWISS, _T("Arial")); 
    memDC.SetBkMode(TRANSPARENT);
    memDC.SetTextColor(RGB(255, 255, 255));
    CFont* pOldFont = memDC.SelectObject(&font);
    int nH = 40;
    CRect rcText(0, rc.top, rc.right, rc.top + nH);
    CString str;
    CTime time;
    for (int i = 0; i < 200; i++)
    {
        ::OffsetRect(&rcText, 0, nH);
        if (rcText.bottom > rc.bottom)
        {
            break;
        }
        time = CTime::GetCurrentTime();
        str.Format(_T("當前行:[%d] "), i);
        str += time.Format(_T("%Y年%m%d日 %H時%M分%S秒 <%c>"));
        str += _T(" -------------------- ");
        str += str;
        str += str;
        memDC.DrawText(str, rcText, DT_LEFT);
    }

    dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);

    memDC.SelectObject(pOldFont);
    memDC.SelectObject(pOldBmp);
    memDC.DeleteDC();
    memBitmap.DeleteObject();
}
複製程式碼