#include <windows.h>
#include "sysmets.h"
static int cxChar,cyChar,cyClient;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
SCROLLINFO si;
PAINTSTRUCT ps;
static int vpos;
switch (msg)
{
case WM_CREATE:
{
hdc=GetDC(hWnd);
TEXTMETRIC tm;
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight+tm.tmExternalLeading;
ReleaseDC(hWnd,hdc);
return 0;
}
case WM_SIZE:
{
cyClient=HIWORD(lParam);
si.cbSize=sizeof(si);
si.fMask=SIF_RANGE|SIF_PAGE;
si.nMin=0;
si.nMax=NUMLINES-1;
si.nPage=cyClient/cyChar;
SetScrollInfo(hWnd,SB_VERT,&si,TRUE);
return 0;
}
case WM_VSCROLL:
{
si.cbSize=sizeof(si);///少了这句scrollbar就不响应消息!
//在第一次使用SCROLLINFO结构对象时,必须先设置si.cbSize属性!!!同wndclass的cbsize属性一样,不设置会出错!!!!(运行时错误!)
si.fMask=SIF_ALL;
GetScrollInfo(hWnd,SB_VERT,&si);
vpos=si.nPos;
switch(LOWORD(wParam))
{
case SB_LINEUP:
si.nPos-=1;
break;
case SB_LINEDOWN:
si.nPos+=1;
break;
case SB_PAGEUP:
si.nPos-=si.nPage;
break;
case SB_PAGEDOWN:
si.nPos+=si.nPage;
break;
case SB_THUMBPOSITION:
si.nPos=si.nTrackPos;
break;
default:
break;
}
si.fMask=SIF_POS;
SetScrollInfo(hWnd,SB_VERT,&si,TRUE);
GetScrollInfo(hWnd,SB_VERT,&si);
if(vpos!=si.nPos)
{
ScrollWindow(hWnd,0,(vpos-si.nPos)*cyChar,NULL,NULL);
UpdateWindow(hWnd);
}
return 0;
}
case WM_PAINT:
{
si.cbSize=sizeof(si);
si.fMask=SIF_ALL;
GetScrollInfo(hWnd,SB_VERT,&si);
hdc=BeginPaint(hWnd,&ps);
int istart=si.nPos+ps.rcPaint.top/cyChar;
int iend= min(NUMLINES-1,si.nPos+ps.rcPaint.bottom/cyChar);
for(int i=istart;i<=iend;i++)//如果写成i<iend,会出现点击linedown时,字符不能完整出现,只出现一大半
{
char szNum[10];
int y=(i-si.nPos)*cyChar;
TextOut(hdc,0,y,sysmetrics[i].szLabel,strlen(sysmetrics[i].szLabel));
TextOut(hdc,30*cxChar,y,sysmetrics[i].szDesc,lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc,TA_RIGHT|TA_TOP);
TextOut(hdc,80*cxChar,y,szNum,wsprintf(szNum,"%d",GetSystemMetrics(sysmetrics[i].index)));
SetTextAlign(hdc,TA_LEFT);
}
EndPaint(hWnd,&ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance,
__in LPSTR lpCmdLine, __in int nShowCmd )
{
WNDCLASSEX wc;
wc.cbSize=sizeof(wc);
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hCursor=LoadCursor(NULL,IDC_CROSS);
wc.hIcon=LoadIcon(NULL,IDI_HAND);
wc.hIconSm=NULL;
wc.hInstance=hInstance;
wc.lpfnWndProc=WndProc;
wc.lpszClassName="MyScrollBar";
wc.lpszMenuName=NULL;
wc.style=CS_HREDRAW|CS_VREDRAW;
RegisterClassEx(&wc);
HWND hWnd=CreateWindow("MyScrollBar","system sysmet style",WS_OVERLAPPEDWINDOW|WS_VSCROLL,
CW_USEDEFAULT,CW_USEDEFAULT,640,480,NULL,NULL,hInstance,0);
if(!hWnd) return 0;
ShowWindow(hWnd,SW_NORMAL);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return nShowCmd;
}
注意:
1:for(int i=istart;i<=iend;i++)如果写成i<iend,会出现产生SB_LINEDOWN消息时,字符可能不能完整出现,只出现一部分
原因:此窗口的最上面是严格对应sysmetric数组的某一行的,但是由于窗口的大小可以改变,窗口最下面可能会只显示某行的一部分。
当产生SB_LINEDOWN消息时,无效距形是从最下面向上推进的一个高度为cychar大小的矩形,但是此矩形不一定正好包含的是一个完整的sysmetric的一个元素,如上面所说.而for(int i=istart;i<=iend;i++)重绘的行数正好是无效区域的高度/cyChar+1(在一个有n行的无效区域内,可能会出现n+1行sysmetric数据(某两行各出现一部分,如上图))
2: 由于i<=iend的条件,所以i会NUMLINE,超出了sysmetric数组的边界,所以要用
int iend= min(NUMLINES-1,si.nPos+ps.rcPaint.bottom/cyChar);限制iend的范围
3:当创建SCROLLINFO,WNDCLASSEX等等对象时,首次使用前必须给.cbsize赋值,否则会出错(运行时错误)。
4:ScrollWindow()函数调用后,会给窗口关联的PAINTSTRUCT对象的无效区域初始化,同时此函数还会把窗口关联的scrollinfo中的数据npos设置为滚动后的新值。
5: