Win32 SDK Gui编程系列之--滚动条(ScrollBar)

滚动条(1)

说明窗口中标准配置的滚动条的操作方法。

1. 滚动条的生成

生成窗口时,在样式中添加WS_VSCROLL|WS_HSCROLL的话,如下图所示,画面的底部和右端会显示滚动条。

    HWND hwnd = CreateWindowEx(0, "Demo", "GridView", 
        WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL,
        100, 100, 250, 200, NULL, NULL, hInst, NULL);

因为设置在滚动条上的信息与部分窗口大小有关,所以在WM_SIZE消息处理中,使用SetScrollInfo函数进行设置。 垂直滚动条的情况,例如SetScrollInfo(hwnd, SB_VERT, &si, TRUE);。 对于水平滚动条,第二个参数是SB_HORZ。 si表示SCROLLINFO结构体变量。 这个定义如下所示。

typedef struct _SCROLLINFO {
        UINT cbSize;    // sizeof(SCROLLINFO)
        UINT fMask;
        int nMin;
        int nMax;
        UINT nPage;
        int nPos;
        int nTrackPos;
} SCROLLINFO;

在fMask中设定的值是以下组合。 一般用SIF_ALL就可以了。

 SIF_RANGE = 1;            // 获取和设置滚动范围。 nMin和nMax有效。
  SIF_PAGE = 2;            // 获取并设置拇指的大小。 nPage有效。
  SIF_POS = 4;              // 获取和设置拇指的位置。 nPos有效。
  SIF_DISABLENOSCROLL = 8;  // 设置禁止使用。
  SIF_TRACKPOS = 16;        // 获取拖曳中的拇指位置。 nTrackPos有效。
  SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS);

设定例子如下所示。 这是对应上面的画面,显示NROWS行的表格。 行的编号是0, 1, 2, ..., NROWS-1,所以假设siv.nMin = 0, siv.nMax = NROWS-1。 siv.nPage表示屏幕上可以显示几行。 把画面的高度除以行的高度就可以了。 因为先头行是以列名固定的,所以数据行是这个值减去1的值。

void onSize(HWND hwnd, WPARAM wp, LPARAM lp) {
    GetClientRect(hwnd, &rcTable);
    nPage = (rcTable.bottom - rcTable.top)/RowHeight - 1;
    SCROLLINFO siv = { sizeof(SCROLLINFO), SIF_ALL, 0, NROWS-1, nPage, nTopRow };
    SetScrollInfo(hwnd, SB_VERT, &siv, TRUE); 
}

在这个例子中,NROWS = 12。 因为画面上显示的是5行,所以滚动条的旋钮(拇指)的宽度是整体的5/12。

当旋钮在最上面时,作为拇指位置得到的值显然是siv.nMin,即0。 但是,如图所示,下旋钮的值不是11(siv.nMax),而是7。 注意不是表示最下行,而是指向最上行。 一般来说这个更感谢。

SetScrollInfo函数的最后一个参数设置为TRUE的话,会重新绘制滚动条,如果是FALSE的话就不会重新绘制。

因为柱子宽度一般不是恒定的,所以水平滚动和垂直滚动完全不一样。 nPage的值不能完全确定。 即使是垂直滚动,如果画面的高度不是行的整数倍的话,也会出现零头,但是水平滚动这个零头的比例比较大,所以很显眼。

经过试错的结果,发现水平滚动时,nMax不是列数,而是相当于全列宽度的像素数,nPage可以显示在画面上的像素数就可以了。

具体的例子如gridview05.c所示。

// gridview05.c 2013-03-29 M.Hatada

#include <stdlib.h>
#include <windows.h>

#define  GRAY	 	  0xf0f0f0
#define  FontHeight		20
#define  RowNumberWidth	        50	// 最左边的行号栏的宽度
#define	 RowHeight		25	// 行的高度
#define  NROWS   		12
#define  NCOLS   	 	 4

char  *colName[NCOLS] = { "No.", "名前", "続柄", "年齢" };
char  *cell[NROWS][NCOLS] = { 
	{"01","太郎","長男", "15"}, {"02","次郎","次男","11"}, {"03","三郎","三男","9"}, 
	{"04","太郎","長男", "15"}, {"05","次郎","次男","11"}, {"06","三郎","三男","9"}, 
	{"07","太郎","長男", "15"}, {"08","次郎","次男","11"}, {"09","三郎","三男","9"}, 
        {"10","太郎","長男", "15"}, {"11","次郎","次男","11"}, {"12","三郎","三男","9"}, 
};
int colWidth[NCOLS], colLeft[NCOLS+1];
RECT    rcTable;
HFONT   hfBold, hfData;
HBRUSH  hbBlue, hbGray, hbYellow;
HPEN    hpWhite, hpBold, hpGrid;
HBITMAP hBitmap;
HDC     hdcMem;
int	nTopRow=2, nTopCol=0;
int     nCurRow=3, nCurCol=2;
int     nPage;			 // 画面上的显示行数
int     nHPage;			// 画面上的显示宽度(单位:像素)

void drawLine(HDC hdc, int xFrom, int yFrom, int xTo, int yTo) {
    MoveToEx(hdc, xFrom, yFrom, NULL);
    LineTo(hdc, xTo, yTo);
}

RECT *getRect(int j, int i, RECT *prc) {
    SetRect(prc, RowNumberWidth+colLeft[i]-colLeft[nTopCol], (j+1-nTopRow)*RowHeight, 
		 RowNumberWidth+colLeft[i+1]-colLeft[nTopCol], (j+2-nTopRow)*RowHeight);
    return prc;
}

void paint(HDC hdc) {
    int i, j;
    char buf[32];
    RECT rc;

    FillRect(hdc, &rcTable, (HBRUSH)GetStockObject(WHITE_BRUSH));	// 屏幕清除

    // 列名行及纵划线
    SelectObject(hdc, hfBold);
    SelectObject(hdc, hpGrid);
    SetBkColor(hdc, GRAY);
    for (i = nTopCol; i < NCOLS; i++) {
	SetRect(&rc, RowNumberWidth+colLeft[i]+1-colLeft[nTopCol], 0, 
		     RowNumberWidth+colLeft[i+1]-colLeft[nTopCol], RowHeight);
        FillRect(hdc, &rc, hbGray);
	DrawText(hdc, colName[i], -1, &rc, DT_VCENTER|DT_SINGLELINE|DT_CENTER);
	drawLine(hdc, rc.right, RowHeight, rc.right, RowHeight*(NROWS+1-nTopRow));
    }
    SetRect(&rc, RowNumberWidth+colLeft[i]+1-colLeft[nTopCol], 0, rcTable.right, RowHeight);
    FillRect(hdc, &rc, hbGray);	 // 右端的空列
    // 行号和横划线
    for (j = nTopRow; j <= NROWS; j++) {
	SetRect(&rc, 0, (j-nTopRow)*RowHeight, RowNumberWidth-1, (j+1-nTopRow)*RowHeight-1);
        FillRect(hdc, &rc, hbGray);
	drawLine(hdc, RowNumberWidth, (j+1-nTopRow)*RowHeight-1, 
		      rcTable.right, (j+1-nTopRow)*RowHeight-1);
	if (j == nTopRow) continue;
	DrawText(hdc, itoa(j,buf,10), -1, &rc, DT_VCENTER|DT_SINGLELINE|DT_CENTER);
    }

    // 数据单元
    SelectObject(hdc, hfData);
    for (j = nTopRow; j < NROWS; j++) {
        SetBkColor(hdc, 0xffffff);
	for (i = nTopCol; i < NCOLS; i++) {
	    SetRect(&rc, RowNumberWidth+colLeft[i]+6-colLeft[nTopCol], 
			 (j+1-nTopRow)*RowHeight,
			 RowNumberWidth+colLeft[i+1]-6-colLeft[nTopCol], 
			 (j+2-nTopRow)*RowHeight-1);
	    DrawText(hdc, cell[j][i], -1, &rc, DT_VCENTER|DT_SINGLELINE|DT_LEFT);
        }
    }

    getRect(nCurRow, nCurCol, &rc);
    if (nCurRow >= nTopRow && nCurCol >= nTopCol) {
    	SelectObject(hdc, hpBold);
    	SelectObject(hdc, (HBRUSH)GetStockObject(NULL_BRUSH));
    	Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);   // 用粗框包围选择单元格
    }
    SelectObject(hdc, hbYellow);
    if (nCurRow >= nTopRow)
    	PatBlt(hdc, 0, rc.top, RowNumberWidth, RowHeight, PATINVERT);
    if (nCurCol >= nTopCol)
    	PatBlt(hdc, rc.left, 0, colWidth[nCurCol], RowHeight, PATINVERT);
}

void update(HWND hWnd) {
    HDC hdc = GetDC(hWnd);
    paint(hdcMem);
    BitBlt(hdc, 0, 0, rcTable.right, rcTable.bottom, hdcMem, 0, 0, SRCCOPY);
    ReleaseDC(hWnd, hdc);
}

void onCreate(HWND hWnd) {
    HDC hdc;
    int iCol, iRow, style;
    RECT rc;
    LOGFONT lf1 = { FontHeight, 0,0,0,0,0,0,0, SHIFTJIS_CHARSET, 0,0,0,0, "宋体" };
    LOGFONT lf2 = { 18, 0,0,0, FW_BOLD, 0,0,0, SHIFTJIS_CHARSET, 0,0,0,0, "宋体" };

    hfData = CreateFontIndirect(&lf1);   		 // 制作逻辑字体
    hfBold = CreateFontIndirect(&lf2); 
    hpWhite = CreatePen(PS_SOLID, 2, 0xffffff);	// 删除框架用的笔
    hpBold  = CreatePen(PS_SOLID, 2, 0);		// 框架绘图用的笔
    hpGrid  = CreatePen(PS_SOLID, 1, 0xd0d0d0);	 // 划线用笔
    hbBlue   = CreateSolidBrush(0xff0000);
    hbGray   = CreateSolidBrush(GRAY);
    hbYellow = CreateSolidBrush(0x0ff0f0)
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值