對話框滾動條及滾動效果實現,用的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(); }