以下全是本人自学Windows编程第5版的见解,不一定全对
WM_PAINT
当你用一个窗口覆盖另一个窗口,再移开那个窗口,原来被覆盖的窗口就会被重绘,被重绘意味着需要接受到WM_PAINT消息。
那么产生WM_PAINT消息的方式有:
(1)一个先前隐藏的区域被重新出现(2)一个窗口被resize(3)调用ScrollWindow滚动窗口 (4)调用InvalidateRect or InvalidateRgn无效化区域来产生WM_PAINT
这里注意下:光标问题,光标(那个箭头吧)盖住了Client区域的一个文字,再移开,那个文字再出现这个过程呢是没WM_PAINT消息的,Windows总是保存这样的被覆盖的区域被自动恢复它:Windows always saves the area of the display it overwrites and then restores it.
产生WM_PAINT消息的本质:无效区域的存在
上面提到的产生的各种方式其实都会产生无效区域,例如被覆盖的区域重新出现,那么被覆盖的区域就是一个无效区了,InvalidateRect函数就是产生无效矩形了。
每个窗口都维护一个paint information struct来保存重绘客户区所需要的信息,这个paint information struct就是那个PAINTSTRUCT结构体了,在SYSMETS3程序里面就用到了这个结构体,这个结构体保存了窗口目前的无效矩形的坐标,要是消息队列里有一个WM_PAINT消息还没来得及处理就又改变了无效矩形,那么Windows会计算出一个新矩形来包含新旧的无效区域,所以不会产生新的WM_PAINT消息,只是把PAINTSTRUCT结构体的无效矩形的坐标改一改。
InvalidateRect函数会看下消息队列有没WM_PAINT,有的话就修改一下PAINTSTRUCT结构体的无效矩形坐标,没就插入新的WM_PAINT消息。在BeginPaint函数会把无效矩形有效化,之后再运行后面的程序代码(我个人觉得这里把无效矩形有效化了重绘的时候仍然没把内容画到无效矩形以外的原因就是存在Clipping 区域的原因了)。
GDI函数与DC
To paint the client area of your window, you use Windows' Graphics Device Interface (GDI) functions. Windows provides several GDI functions for writing text strings to the client area of the window. 由此可见paint somethings in the client area,we need GDI functions.
The device context (also called simply the "DC") is really just a data structure maintained internally by GDI. DC又叫设备描述表,里面有很多piant时用到的资料,例如文字的颜色,字体等等的参数。
获取DC的方式:
(1)hdc=BeginPaint(hwnd,&ps); EndPaint(hwnd,&ps); (2)hdc=GetDC(hwnd); ReleaseDC(hwnd,hdc);
注意:BeginPaint会用窗口类注册时提供的画刷把无效区域刷一遍,再把无效区域有效化。而GetDC单纯是获取DC,不会把区域有效化的,也因此它能在没有WM_PAINT消息的情况下就画东西在Client上
SCROLL BAR:
添加scroll bar:
在CreateWindow函数定义windows stycle的参数加上WS_VSCROLL(加入竖直滚动条)WS_HSCROLL(加入水平滚动条);
scroll bar的范围:
thumb的位置:
修改thumb的位置,SetScrollPos (hwnd, iBar, iPos, bRedraw) ;
The iPos argument is the new position and must be within the range of iMin and iMax . Windows provides similar functions (GetScrollRange and GetScrollPos ) to obtain the current range and position of a scroll bar.
Scroll Bar Message:
SCROLLINFO结构体:
{
UINT cbSize ; // set to sizeof (SCROLLINFO)
UINT fMask ; // values to set or get
int nMin ; // minimum range value
int nMax ; // maximum range value
UINT nPage ; // page size
int nPos ; // current position
int nTrackPos ; // current tracking position
}
SCROLLINFO, * PSCROLLINFO ;
综合:
#include<Windows.h>
#include<tchar.h>
#include"SYSMET.h"
LRESULT CALLBACK WinProc(HWND hwnd,UINT uimsg,WPARAM wparam,LPARAM lparam)
{
static int cxChar,cxCaps,cyChar,cxClient,cyClient,iMaxWidth;
TEXTMETRIC tm;
PAINTSTRUCT ps;
SCROLLINFO si;
HDC hdc;
if(uimsg==WM_CREATE)//在WM_CREATE消息中保存字体信息
{
hdc=GetDC(hwnd);
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cxCaps=(tm.tmPitchAndFamily&1?3:2)*cxChar/2;
cyChar=tm.tmHeight+tm.tmExternalLeading;
ReleaseDC(hwnd,hdc);
iMaxWidth=22*cxCaps+40*cxChar;
return 0;
}
else if(uimsg==WM_SIZE)//在WM_SIZE消息中初始化scroll bar的范围与nPage的大小(根据WM_SIZE消息的lparam参数可以知道Client的大小)
{
cxClient=LOWORD(lparam);
cyClient=HIWORD(lparam);
//Set Vert Scroll bar
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);
//Set Horz Scroll bar
si.nMin=0;
si.nMax=iMaxWidth/cxChar+2;//范围是0到一共有多少个小写字符,+2是为了留一点空白
si.nPage=cxClient/cxChar;
SetScrollInfo(hwnd,SB_HORZ,&si,true);
return 0;
}
else if(uimsg==WM_VSCROLL)
{
si.cbSize=sizeof(si);
si.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&si);
int iPrevPos=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_THUMBTRACK: si.nPos=si.nTrackPos;break;
default: break;
}
si.fMask=SIF_POS;
SetScrollInfo(hwnd,SB_VERT,&si,true);
GetScrollInfo(hwnd,SB_VERT,&si);
if(si.nPos!=iPrevPos)
{
ScrollWindow(hwnd,0,(iPrevPos-si.nPos)*cyChar,NULL,NULL);//ScrollWindow函数所产生的无效区域一定要理解好才能,例如向上移一行,那么无效区域就是需要更新的Client最后的一行了,之前的行数直接把原有的上移一行就行。
UpdateWindow(hwnd);
}
return 0;
}
else if(uimsg==WM_HSCROLL)
{
si.cbSize=sizeof(si);
si.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_HORZ,&si);
int iPrevPos=si.nPos;
switch(LOWORD(wparam))
{
case SB_LINELEFT: si.nPos-=1;break;
case SB_LINERIGHT: si.nPos+=1;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(hwnd,SB_HORZ,&si,true);
GetScrollInfo(hwnd,SB_HORZ,&si);
if(si.nPos!=iPrevPos)
{
ScrollWindow(hwnd,(iPrevPos-si.nPos)*cxChar,0,NULL,NULL);
UpdateWindow(hwnd);
}
return 0;
}
else if(uimsg==WM_PAINT)
{
TCHAR szbuf[1024];
hdc=BeginPaint(hwnd,&ps);
int iVPos,iHPos;
si.cbSize=sizeof(si);
si.fMask=SIF_POS;
GetScrollInfo(hwnd,SB_VERT,&si);
iVPos=si.nPos;
GetScrollInfo(hwnd,SB_HORZ,&si);
iHPos=si.nPos;
//这里是把整个Client重绘的方式
/*for(int i=iVPos;i<NUMLINES;i++)
{
int x=(0-iHPos)*cxChar;
TextOut(hdc,x,(i-iVPos)*cyChar,Sysmets[i].szLabel,wcslen(Sysmets[i].szLabel));
TextOut(hdc,x+22*cxCaps,(i-iVPos)*cyChar,Sysmets[i].szDesc,wcslen(Sysmets[i].szDesc));
SetTextAlign(hdc,TA_RIGHT | TA_TOP);
TextOut(hdc,x+iMaxWidth,(i-iVPos)*cyChar,szbuf,wsprintf(szbuf,TEXT("%5d"),
GetSystemMetrics(Sysmets[i].index)));
SetTextAlign(hdc,TA_TOP | TA_LEFT);
}*/
//这里是只把无效区重绘的方式
int iPaintBeg=max(0,iVPos+ps.rcPaint.top/cyChar);//计算出无效区top坐标下一行是打印第几个
int iPaintEnd=min(NUMLINES-1,iVPos+ps.rcPaint.bottom/cyChar););//计算出无效区bottom坐标下一行是打印第几个
for(int i=iPaintBeg;i<=iPaintEnd;i++)//打印多一行是很重要的,因为ps.rcPaint.top/cyChar求出来的可能是取整之后的结果,会留有一点空白在最后那里,之后把新的一行打印Client的时候,原本留有的那丁点空白却不在Clipping区域,没法画上去,新的无效区又没新的内容画上去,导致空白会越来越大的
{
int y=(i-iVPos)*cyChar;
int x=(0-iHPos)*cxChar;
TextOut(hdc,x,y,Sysmets[i].szLabel,wcslen(Sysmets[i].szLabel));
TextOut(hdc,x+22*cxCaps,y,Sysmets[i].szDesc,wcslen(Sysmets[i].szDesc));
SetTextAlign(hdc,TA_RIGHT | TA_TOP);
TextOut(hdc,x+iMaxWidth,y,szbuf,wsprintf(szbuf,TEXT("%5d"),
GetSystemMetrics(Sysmets[i].index)));
SetTextAlign(hdc,TA_TOP | TA_LEFT);
}
}
else if(uimsg==WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,uimsg,wparam,lparam);
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR szCmdLine,int iCmdShow)
{//还是设计,注册,创建与更新窗口,还有就是消息循环
TCHAR szAPPNAME[]=TEXT("SYSMETS3");
TCHAR szbuf[1024];
MSG msg;
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=CreateSolidBrush(RGB(255,255,255));
wndcls.hCursor=LoadCursor(NULL,IDC_ARROW);
wndcls.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinProc;
wndcls.lpszClassName=szAPPNAME;
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndcls);//创建窗口处指定要求增加水平与竖直的scroll bar
HWND hwnd=CreateWindow(szAPPNAME,TEXT("title"),WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(1)
{
bool bGetmsg=GetMessage(&msg,NULL,0,0);
if(bGetmsg)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
break;
}
}