8. 输出文本:
客户区是整个应用程序窗口中未被标题栏、串口边框以及可选菜单栏、工具栏、状态栏和滚动条占据部分。简言之,客户区是窗口中可以由任意书写和传递可是信息的部分。
尽管可以创建固定大小的窗口,但是多数情况下用户应该能够改变应用程序窗口的大小。您的程序必须能够接受指定给它的大小,并且合理地利用这一空间。
两种可能:一种是程序只有仅能显示“hello”的客户区,另一种是程序在一个大屏幕,高分辨率的系统上,客户区大到足够显示两整页的文本。灵活处理两种极端是Windows程序设计的要点之一。
本章看似是讨论绘图的方法,其实是讨论与设备无关的程序设计基础。
绘制和刷新:
字符模式环境下,程序可以在显示器的任意部分输出,程序输出到屏幕上的内容会停留在原处。因此程序可以丢掉重新生成屏幕显示时所需要的信息。
Windows中,只能在窗口的客户区绘制文本和图形,而且不能确保在客户区内显示的内容会一直保留到程序有意地改写它。即:用户可能在屏幕上移动另一个窗口程序,这样覆盖掉您的应用程序窗口的一部分,Windows不会保存窗口中被其他程序覆盖的区域。一旦覆盖的程序移开,Windows就要求程序刷新客户区的被覆盖部分。
Windows是一个消息驱动系统,通过把消息投入应用程序消息队列中或者把消息发给合适的窗口过程,将发生的各种事件通知给应用程序。Windows通过发送WM_PAINT消息过程窗口的部分客户区需要重绘。
WinMain进入消息循环之前初始化时会调用UpdateWindow,利用这个机会发送第一个WM_PAINT消息。其他的事件激起WM_PAINT消息的有:
1.在用户移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见。
2.用户改变了窗口的大小(如果窗口类风格具有CS_HREDRAW和CS_VERDRAW位设置)
3.程序使用ScrollWindow或ScrollDC函数滚动客户区的一部分
4.InvalidateRect或InvalidateRgn函数显示产生WM_PAINT消息。
客户区的一部分被临时覆盖,Windows无法保存被覆盖的区域的情况:
a.Windows擦出覆盖了部分窗口的对话框或消息框
b.菜单下拉出来,然后被释放
c.显示工具提示
在一些情况下,Windows保存被覆盖区域,然后可以成功恢复的情况:
1.鼠标光标穿越客户区
2.图标拖过客户区
程序应该组织成为可以保留绘制客户区需要的所有信息,并且仅当“有需求”——即Windows给窗口过程发送WM_PAINT消息是才进行绘制。如果程序在其他时间需要更新其客户端,可以强制Windows产生一个WM_PAINT消息。
有效矩形和无效矩形:
接收到WM_PAINT消息之后,会更新整个客户区,但是只需要更新较小的区域,即当客户端覆盖了部分客户区时。
这个被覆盖的区域对于被覆盖的程序而言,就是“无效区域”或“更新区域”。正是客户区域内无效区域的存在才提示Windows将WM_PAINT消息放在应用程序的消息队列。
Windows为每个窗口保存“绘图信息结构”,包含了无效区域的最小矩形的坐标以及其他信息,这个矩形即“无效矩形”。窗口过程可以通过调用InvalidateRect使得客户区内的矩形无效。如果消息队列中已包含了一个WM_PAINT消息,Windows重新计算新的无效矩形,否则将一个新的WM_PAINT消息放入消息队列。
在处理WM_PAINT消息期间,窗口过程在调用了BeginPaint之后,整个客户区即变为有效,可续可以通过调用ValidateRect函数使客户区内的任意矩形变为有效。
GDI简介:
在客户区绘图,可以使用Windows的图形设备接口(GDI)函数。Windows提供了几个GDI函数,用于文本串输出到窗口客户区内。上一章的DrawText函数,最为普遍的输出函数TextOut:
TextOut(hdc, x, y, psText, iLength);
hdc参数是“设备描述表句柄”,它是GDI的重要部分。
句柄只不过是一个数值,Windows用它来引用对象,从Windows获取句柄,然后在其他函数中用该句柄。设备描述表句柄是GDI函数的窗口“密码”,有了设备描述表句柄,程序员就能在客户区上绘图。
设备描述表(简称DC)实际上是GDI内部保存的数据结构,设备描述表与特定的显示设备相关。当需要绘图时,必须首先获取设备描述表句柄,在获取了句柄之后,Windows用默认的属性值填充内部设备描述表结构。可以通过其他的GDI函数改变默认值。 当程序在客户区绘制图形完毕后,必须释放设备描述表句柄,句柄被程序释放后就不再有效,且不能再被使用。程序必须在处理单个消息期间获取和释放句柄。不能在两个消息之间保存设备描述表句柄,CreateDC创建的设备描述表除外。
获取设备描述表句柄方法一:
在处理WM_PAINT消息时,窗口过程首先调用BeginPaint,BeginPaint函数一般在准备绘制时导致无效区域的背景被擦除。该函数也填入ps结构的域,BeginPaint返回的值是设备描述表句柄,这一返回值通常被保存在被叫做hdc的变量中,窗口过程中的定义如下:
hdc= BeginPaint(hwnd, &ps);
[useGDI functions]
EndPaint(hwnd,&ps);
如果窗口过程不处理WM_PAINT消息,则它必须将WM_PAINT消息传递给Windows中DefWindowProc(默认窗口过程)。
绘图信息结构
Windows为每个窗口保存一个“绘图信息结构”,这就是PAINTSTRUCT,定义如下:
typedefstruct tagPAINTSTRUCT
{
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fincUpdate;
BYTE rgbReserved[32];
}PAINTSTRUCT;
调用BeginPaint时,Windows填充该结构的各个字段。用户程序只使用前三个字段,其他字段由Windows内部使用。hdc字段是设备描述表句柄。fErase被标志位0,则意味着Windows已经擦除了无效矩形的背景,Windows使用WNDCLASS结构的hbrBackground字段指定的刷子擦除背景。
程序调用Windows函数InvalidateRect使客户区中的矩形失效,则该函数的最后一个参数会指定是否擦除北京。参数为FALSE,则Windows不会擦除背景。
InvalidateRect(hwnd, NULL, TRUE);
使得整个客户区变为无效,并擦除背景。最后一个参数为FALSE则不擦除背景。
获取设备描述表句柄方法二:
在处理非WM_PAINT消息期间绘制客户区的某个部分也非常有用。要得到窗口客户区的设备描述表句柄,可以调用GetDC来获取句柄,在使用完之后调用ReleaseDC释放句柄。
hdc= GetDC( hwnd);
使用GDI 函数
ReleaseDC(hwnd, hdc);
与BeginPaint和EndPaint一样,GetDC和ReleaseDC函数必须成对使用。且不能跨消息使用。
GetDC返回的设备描述表句柄具有一个剪取矩形,等于整个客户区。与BeginPaint不同,GetDC不会使任何无效区域变为有效,如果要使得整个区域有效,可以调用ValidateRect(hwnd, NULL);
与GetDC相似的是GetWindowDC,GetDC返回用于写入窗口客户区的设备描述句柄,而GetWindowDC返回写入整个窗口的设备描述表句柄。可以从GetWindowDC返回的设备描述表句柄在窗口的标题栏上写入文字。程序也需要处理WM_NCPAINT(非客户区绘图)消息。
TextOut函数细节:
TextOut是用于显示文本的最常用的GDI函数。语法是:
TextOut(hdc,x, y, psText, iLength);
设备描述表的属性控制了被显示的文本串的特征。例如设备描述表有一个属性指定文本颜色,默认颜色为褐色,默认设备描述表还定义了白色的背景,在向显示器输出时,Windows使用这个背景色填充字符周围的矩形空间(称为“字符框”)。
当阅读GDI绘图函数文档时,TextOut传递给函数的坐标被称为“逻辑坐标”。Windows有多种不同的“映射方式”,他们用来烤那个值GDI绘图函数指定的逻辑坐标转换为显示器的物理像素坐标的方式。映射方式在设备描述表中定义,默认的映射方式是MM_TEXT。在MM_TEXT映射方式下,逻辑单位与物理单位相同,都是相对于客户区左上角的像素数;x的值从左向右递增,y的值从上到下递增。MM_TEXT坐标系与Windows在PAINTSTRUCT结构中定义的无效矩形时使用坐标系相同。其他的映射方式并非如此。
设备描述表中还定义了一个剪取区域,从GetDC获取的设备描述表句柄,默认的剪取区域是整个客户区;而对于BeginPaint获取的设备描述表句柄,默认的剪取区域则为无效区域。Windows不会再剪取区域之外的任何位置显示字符串。
系统字体:
设备描述表中还需要定义TextOut显示文本时Windows使用的字体。默认字体是“系统字体”或Windows头文件中标识符即SYSTEM_FONT,系统字体是Windows用来在标题栏、菜单和对话框中显示文本串的默认字体。
早期Windows使用等宽字体,3.0之后出现变宽字体。系统字体是一种“点阵字体”,意味着字符被定义为像素块。后面还会讲到TrueType字体,由轮廓定义。
系统字体的字符高度和平均宽度是多少取决于显示器的像素大小。程序可以调用GetSystemMetrics函数确定关于用户界面构件大小的信息,调用GetTextMetrics来确定字体大小。GetTextMetrics返回设备描述表中当前选定字体信息,因此它需要设备描述表句柄。结构体TEXTMETRIC描述了字体信息:
typedefstruct tag_TEXTMETRIC { LONGtmHeight; LONGtmAscent; LONGtmDescent; LONGtmInternalLeading; LONG tmExternalLeading; LONGtmAveCharWidth; LONGtmMaxCharWidth; [otherstructure fields] }TEXTMETRIC,*PTEXTMETRIC;
使用GetTextMetrics函数,首先定义TEXTMETRIC 结构体变量。
首先获取设备描述表句柄,在调用GetTextMetrics:
hdc= GetDC(hwnd); GetTextMetrics(hdc, &tm); ReleaseDC(hwnd, hdc);
关于字体信息的解释:最重要的有5个值确定,tmHeight,是tmAscent和tmDescent的和。这两个值表示基线上下字符的最大纵向高度。间距(leading)指打印机在两行文本间插入的空间,在TEXTMETRIC结构中,内部的间距包括在tmAscent中,它经常是重音符号出现的地方,tmInternalLeading字段被设置为0后,加重音的字母会稍稍缩短,以容纳重音符号。TEXTMETRIC结构还包含一个不含在tmHeight中的字段tmExternalLeading,它是字体设计者建议加在横向字符之间的空间大小。系统字体中tmExternalLeading可以为0。描述字符宽度的两个字段:tmAveCharWidth(小写字母加权平均宽度)和tmMaxCharWidth(字体中最宽字符的宽度)。对等宽字体,这两个值相等。大写的平均宽度可以用tmAveCharWidth乘以150%大致计算。
系统字体的大小取决于Windows所运行的视频显示器的分辨率。当然,有些情况下也取决于用户选取的系统字体的大小。
编写程序时,在客户区显示几行文本,需要获取字符宽度和高度,定义两个变量cxChar和cyChar保存平均字符宽度和总的字符高度。
staticint cxChar, cyChar; GetTextMetrics(hdc,&tm); cxChar= tm.tmAveCharWidth; cyChar= tm.tmHeight + tm.tmExternalLeading;
在输出文本时,每隔cyChar宽度显示一行文本。
输出从GetSystemMetrics可以获取的信息:
/*---------------------------------------------------------
SYSMETS.H -- System metrics displaystructure
-----------------------------------------------------------*/
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
int Index ;
TCHAR* szLabel;
TCHAR* szDesc;
}
sysmetrics[] =
{
SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("Screenwidth in pixels"),
SM_CYSCREEN, TEXT("SM_CYSCREEN"), TEXT("Screenheight in pixels"),
SM_CXVSCROLL, TEXT("SM_CXVSCROLL"), TEXT("Verticalscroll width"),
SM_CYHSCROLL, TEXT("SM_CYHSCROLL"), TEXT("Horizontalscroll height"),
SM_CYCAPTION, TEXT("SM_CYCAPTION"), TEXT("Captionbar height"),
SM_CXBORDER, TEXT("SM_CXBORDER"), TEXT("Windowborder width"),
SM_CYBORDER, TEXT("SM_CYBORDER"), TEXT("Windowborder height"),
SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"), TEXT("Dialogwindow frame width"),
SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"), TEXT("Dialogwindow frame height"),
SM_CYVTHUMB, TEXT("SM_CYVTHUMB"), TEXT("Verticalscroll thumb height"),
SM_CXHTHUMB, TEXT("SM_CXHTHUMB"), TEXT("Horizontalscroll thumb width"),
SM_CXICON, TEXT("SM_CXICON"), TEXT("Iconwidth"),
SM_CYICON, TEXT("SM_CYICON"), TEXT("Iconheight"),
SM_CXCURSOR, TEXT("SM_CXCURSOR"), TEXT("Cursorwidth"),
SM_CYCURSOR, TEXT("SM_CYCURSOR"), TEXT("Cursorheight"),
SM_CYMENU, TEXT("SM_CYMENU"), TEXT("Menubar height"),
SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"), TEXT("Fullscreen client area width"),
SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"), TEXT("Fullscreen client area height"),
SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"), TEXT("Kanjiwindow height"),
SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"), TEXT("Mousepresent flag"),
SM_CYVSCROLL, TEXT("SM_CYVSCROLL"), TEXT("Verticalscroll arrow height"),
SM_CXHSCROLL, TEXT("SM_CXHSCROLL"), TEXT("Horizontalscroll arrow width"),
SM_DEBUG, TEXT("SM_DEBUG"), TEXT("Debugversion flag"),
SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"), TEXT("Mousebuttons swapped flag"),
SM_CXMIN, TEXT("SM_CXMIN"), TEXT("Minimumwindow width"),
SM_CYMIN, TEXT("SM_CYMIN"), TEXT("Minimumwindow height"),
SM_CXSIZE, TEXT("SM_CXSIZE"), TEXT("Min/Max/Closebutton width"),
SM_CYSIZE, TEXT("SM_CYSIZE"), TEXT("Min/Max/Closebutton height"),
SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"), TEXT("Windowsizing frame width"),
SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"), TEXT("Windowsizing frame height"),
SM_CXMINTRACK, TEXT("SM_CXMINTRACK"), TEXT("Minimum window tracking width"),
SM_CYMINTRACK, TEXT("SM_CYMINTRACK"), TEXT("Minimumwindow tracking height"),
SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"), TEXT("Doubleclick x tolerance"),
SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"), TEXT("Doubleclick y tolerance"),
SM_CXICONSPACING, TEXT("SM_CXICONSPACING"), TEXT("Horizontalicon spacing"),
SM_CYICONSPACING, TEXT("SM_CYICONSPACING"), TEXT("Verticalicon spacing"),
SM_MENUDROPALIGNMENT,TEXT("SM_MENUDROPALIGNMENT"),TEXT("Left or right menu drop"),
SM_PENWINDOWS, TEXT("SM_PENWINDOWS"), TEXT("Penextensions installed"),
SM_DBCSENABLED, TEXT("SM_DBCSENABLED"), TEXT("Double-ByteChar Set enabled"),
SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"), TEXT("Numberof mouse buttons"),
SM_SECURE, TEXT("SM_SECURE"), TEXT("Securitypresent flag"),
SM_CXEDGE, TEXT("SM_CXEDGE"), TEXT("3-Dborder width"),
SM_CYEDGE, TEXT("SM_CYEDGE"), TEXT("3-Dborder height"),
SM_CXMINSPACING, TEXT("SM_CXMINSPACING"), TEXT("Minimizedwindow spacing width"),
SM_CYMINSPACING, TEXT("SM_CYMINSPACING"), TEXT("Minimizedwindow spacing height"),
SM_CXSMICON, TEXT("SM_CXSMICON"), TEXT("Smallicon width"),
SM_CYSMICON, TEXT("SM_CYSMICON"), TEXT("Smallicon height"),
SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"), TEXT("Smallcaption height"),
SM_CXSMSIZE, TEXT("SM_CXSMSIZE"), TEXT("Smallcaption button width"),
SM_CYSMSIZE, TEXT("SM_CYSMSIZE"), TEXT("Smallcaption button height"),
SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"), TEXT("Menubar button width"),
SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"), TEXT("Menubar button height"),
SM_ARRANGE, TEXT("SM_ARRANGE"), TEXT("Howminimized windows arranged"),
SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"), TEXT("Minimizedwindow width"),
SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"), TEXT("Minimizedwindow height"),
SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"), TEXT("Maximumdraggable width"),
SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"), TEXT("Maximumdraggable height"),
SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"), TEXT("Widthof maximized window"),
SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"), TEXT("Heightof maximized window"),
SM_NETWORK, TEXT("SM_NETWORK"), TEXT("Networkpresent flag"),
SM_CLEANBOOT, TEXT("SM_CLEANBOOT"), TEXT("Howsystem was booted"),
SM_CXDRAG, TEXT("SM_CXDRAG"), TEXT("Avoiddrag x tolerance"),
SM_CYDRAG, TEXT("SM_CYDRAG"), TEXT("Avoiddrag y tolerance"),
SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"), TEXT("Presentsounds visually"),
SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"), TEXT("Menucheck-mark width"),
SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"), TEXT("Menucheck-mark height"),
SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"), TEXT("Slowprocessor flag"),
SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"), TEXT("Hebrewand Arabic enabled flag"),
SM_MOUSEWHEELPRESENT,TEXT("SM_MOUSEWHEELPRESENT"),TEXT("Mouse wheel present flag"),
SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"), TEXT("Virtualscreen x origin"),
SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"), TEXT("Virtualscreen y origin"),
SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"), TEXT("Virtualscreen width"),
SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"), TEXT("Virtualscreen height"),
SM_CMONITORS, TEXT("SM_CMONITORS"), TEXT("Numberof monitors"),
SM_SAMEDISPLAYFORMAT,TEXT("SM_SAMEDISPLAYFORMAT"),TEXT("Same color format flag")
};
/*--------------------------------------------------------------------------
SYSMETS.CPP—— System Metrics Display Program No.1
-------------------------------------------------------------------------*/
#include <windows.h>
#include "SysMets.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCEhPreInstance, PSTRszCmdLine, intiCmdShow)
{
static TCHAR szAppName[] =TEXT("SystemMetrics");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW| CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if(!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Thisprogram requires Windows NT!"), szAppName,MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("GetSystem Metrics No.1"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while ( GetMessage(&msg,NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxChar, cyChar, cxCaps;
HDC hdc;
PAINTSTRUCTps;
CHAR szBuffer[10];
TEXTMETRIC tm;
switch (message)
{
case 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);
return 0;
case WM_PAINT:
hdc = BeginPaint( hwnd,&ps);
for( int i = 0; i < NUMLINES; i++)
{
TextOut(hdc, 0, cyChar*i, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));
TextOut(hdc, 22*cxCaps,cyChar*i, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT|TA_TOP);
TextOut(hdc, 22*cxCaps+ 40 * cxChar, cyChar*i, szBuffer, wsprintf(szBuffer, TEXT("%5d"), GetSystemMetrics(sysmetrics[i].Index)));
SetTextAlign(hdc, TA_LEFT|TA_TOP);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,message, wParam,lParam);
}
从上述代码中,第三条TextOut语句使得其输出的值右对齐,使用SetTextAlign函数实现指定字符串结束的像素点位置。
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
那么TextOut中x,y的值指定了字符串的右上角的坐标值。
问题:除非屏幕足够大,分辨率足够高,否则很难看到系统尺度列表的最后几行。Windows应用程序的窗口尺寸变化极大。
我们使用GetClientRect函数确定客户区的大小,但是这个函数的效率很低。更好的方法是在窗口过程中处理WM_SIZE消息,在窗口大小改变时,Windows给窗口过程发送一个WM_SIZE消息,窗口过程的lParam参数的低字节中包含了客户区的宽度,高位字中包含了客户区的高度。可以使用了两个变量来保存。
staticint cxClient, cyClient; caseWM_SIZE: cxClient= LOWORD(lParam); cyClient= HIWORD(lParam); return0; #defineLOWORD(i) ((WORD)(i)) #defineLOWORD(i) ((WORD)(((DWORD)(i) >> 16) & 0xFFFF))
9. 滚动条使用
滚动条时图形用户界面中最好的功能之一,容易使用,而且提供了很好的视觉反馈。对于滚动条的概念很难理解:用户的观点和程序员的观点不同,用户向下滚动是想看到文档稍下的部分,程序实际上是将文档相对于显示窗口向上移动。向上滚动意味着朝文档的开头移动,向下滚动意味着超文档尾部移动。
要使程序包含滚动条,需要在CreateWindow的第三个参数中加入WS_VSCROLL和WS_HSCROLL参数即可。 滚动条通常放在窗口的右部和底部,客户区不包含滚动条所占据的空间。滚动条的宽度和水平滚动条高度是恒定的,可以使用GetSystemMetrics调用来获取。
每个滚动条有一个相关的“范围”。当滚动框在滚动条顶部时,滚动框的位置是范围的最小值。在滚动条底部时,滚动框位置是范围的最大值。默认情况下滚动条的范围是从0-100,该值可以通过SetScrollRange(hwnd,iBar, iMin, iMax, bRedraw);进行修改。
参数iBar可以为SB_VERT或SB_HORZ,iMin和iMax分别为范围的最小值和最大值。bRedraw标识是否进行重绘。
使用SetScrollPos(hwnd, iBar, iPos,bRedraw); 在滚动条范围内设置新的滚动框位置。在程序内使用滚动条时,程序员与Windows共同负责维护滚动条以及更新滚动框的位置。
Windows对滚动条的处理:
1.处理所有滚动条鼠标事件
2.当用户在滚动条内单击鼠标时,提供一种“反相视频”闪烁
3.当用户在滚动条内拖动滚动框时,移动滚动框。
4.为包含滚动条窗口的窗口过程发送滚动条消息
程序员应完成的工作:
1.初始化滚动条范围和位置
2.处理窗口过程的滚动条消息
3.更新滚动条内滚动框的位置
4.更改客户区的内容以相应对滚动条的更改
在用户鼠标单击滚动条或者拖动滚动框时,Windows给窗口过程发送WM_VSCROLL和WM_HSCROLL消息,滚动条上的每个鼠标动作至少产生两个消息,一个是鼠标键按下,一个是释放按键时产生。附带参数中,lParam值针对滚动条作为子窗口创建时使用。wParam消息参数被分为一个低字节和一个高字节,低字节是一个数值,指出鼠标滚动条进行的操作,即“通知码”,以SB_开头的标识符。
#defineSB_LINEUP 0 #defineSB_LINELEFT 0 #defineSB_LINEDOWN 1 #defineSB_LINERIGHT 1 #defineSB_PAGEUP 2 #defineSB_PAGELEFT 2 #defineSB_PAGEDOWN 3 #define SB_PAGERIGHT 3 #defineSB_THUMBPOSITION 4 #defineSB_THUMBTRACK 5 #defineSB_TOP 6 #defineSB_LEFT 6 #defineSB_BOTTOM 7 #defineSB_RIGHT 7 #defineSB_ENDSCROLL 8
鼠标在不同区域单击所产生的通知码如下图所示。
用鼠标移动滚动框时,会产生SB_THUMBTRACK和SB_THUMBPOSITION通知码的滚动条消息。在wParam的低位字节是SB_THUMBTRACK时,wParam的高位字是用户在滚动框是当前位置,该位置位于滚动条范围的最小值和最大值之间,在wParam的低位字是SB_THUMBPOSITION时,wParam的高位字是用户释放鼠标后滚动框的最终位置。
在出现SB_THUMBTRACK或SB_THUMBPOSITION消息时,需要调用SetScrollPos来进行相应处理,否则用户释放鼠标键后会跳回原来的位置。通常不需要同时处理两个消息,处理SB_THUMBTRACK更好些,但是也更加复杂。
在CreateWindow的第三个参数加入WS_VSCROLL窗口风格,在创建窗口时对滚动条进行初始化:
SetScrollRange(hwnd, SB_VERT, 0, NUMLINES-1, FALSE);
SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
滚动框的范围设置为0-NUMLINES-1, 也就是在处于NUMLINES-1时,最后一行文本出现在客户区的顶部。
/*--------------------------------------------------------- SYSMETS.H-- System metrics display structure -----------------------------------------------------------*/ #define NUMLINES ((int) (sizeof sysmetrics/ sizeof sysmetrics [0])) struct { intIndex ; TCHAR* szLabel ; TCHAR* szDesc ; } sysmetrics[] = { SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("Screen width inpixels"), SM_CYSCREEN, TEXT("SM_CYSCREEN"), TEXT("Screen height inpixels"), SM_CXVSCROLL, TEXT("SM_CXVSCROLL"), TEXT("Vertical scrollwidth"), SM_CYHSCROLL, TEXT("SM_CYHSCROLL"), TEXT("Horizontal scrollheight"), SM_CYCAPTION, TEXT("SM_CYCAPTION"), TEXT("Caption barheight"), SM_CXBORDER, TEXT("SM_CXBORDER"), TEXT("Window border width"), SM_CYBORDER, TEXT("SM_CYBORDER"), TEXT("Window border height"), SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"), TEXT("Dialog window framewidth"), SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"), TEXT("Dialog window frameheight"), SM_CYVTHUMB, TEXT("SM_CYVTHUMB"), TEXT("Vertical scroll thumbheight"), SM_CXHTHUMB, TEXT("SM_CXHTHUMB"), TEXT("Horizontal scroll thumbwidth"), SM_CXICON, TEXT("SM_CXICON"), TEXT("Iconwidth"), SM_CYICON, TEXT("SM_CYICON"), TEXT("Iconheight"), SM_CXCURSOR, TEXT("SM_CXCURSOR"), TEXT("Cursor width"), SM_CYCURSOR, TEXT("SM_CYCURSOR"), TEXT("Cursor height"), SM_CYMENU, TEXT("SM_CYMENU"), TEXT("Menubar height"), SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"), TEXT("Full screen client areawidth"), SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"), TEXT("Full screen client areaheight"), SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"), TEXT("Kanji window height"), SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"), TEXT("Mouse present flag"), SM_CYVSCROLL, TEXT("SM_CYVSCROLL"), TEXT("Vertical scroll arrowheight"), SM_CXHSCROLL, TEXT("SM_CXHSCROLL"), TEXT("Horizontal scroll arrowwidth"), SM_DEBUG, TEXT("SM_DEBUG"), TEXT("Debug versionflag"), SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"), TEXT("Mouse buttons swappedflag"), SM_CXMIN, TEXT("SM_CXMIN"), TEXT("Minimum windowwidth"), SM_CYMIN, TEXT("SM_CYMIN"), TEXT("Minimum windowheight"), SM_CXSIZE, TEXT("SM_CXSIZE"), TEXT("Min/Max/Close buttonwidth"), SM_CYSIZE, TEXT("SM_CYSIZE"), TEXT("Min/Max/Close buttonheight"), SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"), TEXT("Window sizing framewidth"), SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"), TEXT("Window sizing frameheight"), SM_CXMINTRACK, TEXT("SM_CXMINTRACK"), TEXT("Minimum window trackingwidth"), SM_CYMINTRACK, TEXT("SM_CYMINTRACK"), TEXT("Minimum window trackingheight"), SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"), TEXT("Double click xtolerance"), SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"), TEXT("Double click ytolerance"), SM_CXICONSPACING, TEXT("SM_CXICONSPACING"), TEXT("Horizontal icon spacing"), SM_CYICONSPACING, TEXT("SM_CYICONSPACING"), TEXT("Vertical icon spacing"), SM_MENUDROPALIGNMENT,TEXT("SM_MENUDROPALIGNMENT"),TEXT("Leftor right menu drop"), SM_PENWINDOWS, TEXT("SM_PENWINDOWS"), TEXT("Pen extensionsinstalled"), SM_DBCSENABLED, TEXT("SM_DBCSENABLED"), TEXT("Double-Byte Char Setenabled"), SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"), TEXT("Number of mouse buttons"), SM_SECURE, TEXT("SM_SECURE"), TEXT("Security presentflag"), SM_CXEDGE, TEXT("SM_CXEDGE"), TEXT("3-D borderwidth"), SM_CYEDGE, TEXT("SM_CYEDGE"), TEXT("3-D borderheight"), SM_CXMINSPACING, TEXT("SM_CXMINSPACING"), TEXT("Minimized window spacingwidth"), SM_CYMINSPACING, TEXT("SM_CYMINSPACING"), TEXT("Minimized window spacingheight"), SM_CXSMICON, TEXT("SM_CXSMICON"), TEXT("Small icon width"), SM_CYSMICON, TEXT("SM_CYSMICON"), TEXT("Small icon height"), SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"), TEXT("Small caption height"), SM_CXSMSIZE, TEXT("SM_CXSMSIZE"), TEXT("Small caption buttonwidth"), SM_CYSMSIZE, TEXT("SM_CYSMSIZE"), TEXT("Small caption buttonheight"), SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"), TEXT("Menu bar buttonwidth"), SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"), TEXT("Menu bar buttonheight"), SM_ARRANGE, TEXT("SM_ARRANGE"), TEXT("How minimized windowsarranged"), SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"), TEXT("Minimized windowwidth"), SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"), TEXT("Minimized windowheight"), SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"), TEXT("Maximum draggablewidth"), SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"), TEXT("Maximum draggableheight"), SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"), TEXT("Width of maximizedwindow"), SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"), TEXT("Height of maximizedwindow"), SM_NETWORK, TEXT("SM_NETWORK"), TEXT("Network presentflag"), SM_CLEANBOOT, TEXT("SM_CLEANBOOT"), TEXT("How system wasbooted"), SM_CXDRAG, TEXT("SM_CXDRAG"), TEXT("Avoid drag xtolerance"), SM_CYDRAG, TEXT("SM_CYDRAG"), TEXT("Avoid drag ytolerance"), SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"), TEXT("Present soundsvisually"), SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"), TEXT("Menu check-markwidth"), SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"), TEXT("Menu check-markheight"), SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"), TEXT("Slow processor flag"), SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"), TEXT("Hebrew and Arabic enabledflag"), SM_MOUSEWHEELPRESENT,TEXT("SM_MOUSEWHEELPRESENT"),TEXT("Mousewheel present flag"), SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"), TEXT("Virtual screen x origin"), SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"), TEXT("Virtual screen y origin"), SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"), TEXT("Virtual screen width"), SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"), TEXT("Virtual screen height"), SM_CMONITORS, TEXT("SM_CMONITORS"), TEXT("Number ofmonitors"), SM_SAMEDISPLAYFORMAT,TEXT("SM_SAMEDISPLAYFORMAT"),TEXT("Samecolor format flag") } ; /*-------------------------------------------------------------------------- SYSMETS.CPP—— System Metrics Display ProgramNo.1 --------------------------------------------------------------------------*/ #include <windows.h> #include "SysMets.h" LRESULT CALLBACK WndProc(HWND, UINT,WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance, PSTR szCmdLine, int iCmdShow) { staticTCHAR szAppName[] = TEXT("SystemMetrics"); HWNDhwnd; MSGmsg; WNDCLASSwndclass; wndclass.style= CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc= WndProc; wndclass.cbClsExtra= 0; wndclass.cbWndExtra= 0; wndclass.hInstance= hInstance; wndclass.hIcon= LoadIcon( NULL, IDI_APPLICATION); wndclass.hCursor= LoadCursor( NULL, IDC_ARROW); wndclass.hbrBackground= (HBRUSH)GetStockObject( WHITE_BRUSH); wndclass.lpszMenuName= NULL; wndclass.lpszClassName= szAppName; if(!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName,MB_ICONERROR); return0; } hwnd= CreateWindow(szAppName, TEXT("GetSystem Metrics No.1"), WS_OVERLAPPEDWINDOW| WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd,iCmdShow); UpdateWindow(hwnd); while( GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return(int)msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINTmessage, WPARAM wParam, LPARAM lParam) { staticint cxChar, cyChar, cxCaps; staticint cyClient, iVscrollPos; HDChdc; inti, y; PAINTSTRUCTps; CHARszBuffer[10]; TEXTMETRICtm; switch(message) { caseWM_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); //初始化滚动条,设置范围和初始位置 SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, FALSE); SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE); return0; caseWM_SIZE: cyClient= HIWORD( lParam); return0; caseWM_VSCROLL: switch(LOWORD(wParam)) { caseSB_LINEUP: iVscrollPos-= 1; break; caseSB_LINEDOWN: iVscrollPos+= 1; break; caseSB_PAGEUP: iVscrollPos-= cyClient / cyChar; break; caseSB_PAGEDOWN: iVscrollPos+= cyClient / cyChar; break; caseSB_THUMBPOSITION: iVscrollPos= HIWORD(wParam); break; default: break; } iVscrollPos= max(0, min(iVscrollPos, NUMLINES-1)); if(iVscrollPos!= GetScrollPos(hwnd, SB_VERT)) { SetScrollPos(hwnd,SB_VERT, iVscrollPos, TRUE); InvalidateRect(hwnd,NULL, TRUE); } return0; caseWM_PAINT: hdc= BeginPaint( hwnd, &ps); for(int i = 0; i < NUMLINES; i++) { y= cyChar * ( i - iVscrollPos); TextOut(hdc, 0, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel)); TextOut(hdc, 22*cxCaps, y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc)); SetTextAlign(hdc, TA_RIGHT|TA_TOP); TextOut(hdc, 22*cxCaps + 40 * cxChar, y, szBuffer, wsprintf(szBuffer,TEXT("%5d"), GetSystemMetrics(sysmetrics[i].Index))); SetTextAlign(hdc, TA_LEFT|TA_TOP); } EndPaint(hwnd, &ps); return0; caseWM_DESTROY: PostQuitMessage(0); return0; } returnDefWindowProc(hwnd, message, wParam, lParam); }
优化:
前面的不刷新客户区,而是调用InvalidateRect函数使客户区失效,导致Windows产生一个WM_PAINT消息。最好能使得Windows程序在相应WM_PAINT消息时完成所有的客户区绘制功能,因为一旦接收了WM_PAINT消息就会刷新整个客户区,如果其他地方也刷新将使得代码重复。
程序需要处理非WM_PAINT消息时刷新特定的显示区域,使用InvalidateRect就很方便,可以使用它使得客户区内的特定矩形或整个客户端区失效。WM_PAINT消息的优先级较低,系统有其他的动作需要处理,则需要等待很长时间才能刷新,对话框消失将出现空白的“洞”。如果希望立即刷新无效区域,可以调用InvalidateRect之后调用UpdateWindows。这时产生的WM_PAINT消息不进入消息队列,直接由Windows调用窗口过程。
更好的滚动:
上面的程序效果很好,但只是在模仿其他程序中的滚动条,效率很低。这里会使用新的Win32 API。
Win32API的两个滚动条函数为SetScrollInfo和GetScrollInfo,这两个函数可以完成以前的SetScrollRange,SetScrollPos,GetScrollRange和GetScrollPos函数的所有功能。
滚动框的大小:在使用到的一些Windows应用程序中,滚动条大小与窗口中显示的文档大小成比例。显示的大小称作页面大小:
滚动框大小/滚动长度 = 页面大小/ 范围 = 显示的文档数量/ 文档的总大小
可以使用SetScrollInfo来设置页面大小(从而设置了滚动框的大小)。
SetScrollInfo(hwnd, iBar, &si, bRedraw);
GetScrollInfo(hwnd, iBar, &si);
iBar的值可以是SB_VERT或SB_HORZ,还可以是SB_CTL用于滚动条控制
typedefstruct tagSCROLLINFO
{
UINTcbSize; // set tosizeof(SCROLLINFO)
UINTfMask; // value to set or get
intnMin; // minimum range value
intnMax; // maximum rangevalue
UINTnPage; // page size
intnPos; // current position
intnTrackPos; // current trackingposition
}SCROLLINFO,*PSCROLLINFO;
fMask字段设置为以SIF前缀开头的一个或多个标志,可以使用位操作OR函数组合。
通过SetScrollInfo函数使用SIF_RANGE标志时,必须将nMin和nMax设置为所需的滚动条范围。SIF_POS标志也是一样,使用SetScrollInfo函数时要设置nPos字段。SIF_PAGE标志能够获取页面大小。但处理带有SB_THUMBTRACK或SB_THUMBPOSITION通知码的WM_VSCROLL或WM_HSCROLL消息时,通过GetScrollInfo只使用SIF_TRACKPOS标志,结构体nTrackInfo字段指出滚动框位置。
SIF_DISABLENOSCROLL标志指定新的滚动条参数使得滚动条不可见,禁用滚动条。
SIF_ALL标志是SIF_RANGE,SIF_POS,SIF_PAGE和SIF_TRACKPOS组合,WM_SIZE消息中设置滚动条参数时,很方便。
在上一个中,滚动范围最小值为0,最大值为NUMLINES-1,滚动条位置是0时,第一行信息显示在客户区顶部,滚动条位置为NUMLINES-1时,最后一行显示在客户区的顶部。事实上滚动范围过大,只需要把信息的最后一行显示在客户区的底部即可,不用显示到顶部。作出特殊处理:在收到WM_SIZE消息时才设置滚动条范围:
iVscrollMax= max( 0, NUMLINES – cyClient / cyChar);
SetScrollRange(hwnd, SB_VERT, 0, iVscrollMax, TRUE);
即,如果NUMLINES为75时,假定窗口大小为50,75行信息但是只有50行可以显示在客户区,按上述代码将滚动条范围设置为0-25,当滚动条位置为0时,程序显示0到49行,滚动条为1是显示1到50行,滚动条为25时,显示25-74行。
/*--------------------------------------------------------- SYSMETS.H-- System metrics display structure -----------------------------------------------------------*/ #define NUMLINES ((int) (sizeof sysmetrics/ sizeof sysmetrics [0])) struct { intIndex ; TCHAR* szLabel ; TCHAR* szDesc ; } sysmetrics[] = { SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("Screen width inpixels"), SM_CYSCREEN, TEXT("SM_CYSCREEN"), TEXT("Screen height inpixels"), SM_CXVSCROLL, TEXT("SM_CXVSCROLL"), TEXT("Vertical scrollwidth"), SM_CYHSCROLL, TEXT("SM_CYHSCROLL"), TEXT("Horizontal scrollheight"), SM_CYCAPTION, TEXT("SM_CYCAPTION"), TEXT("Caption barheight"), SM_CXBORDER, TEXT("SM_CXBORDER"), TEXT("Window border width"), SM_CYBORDER, TEXT("SM_CYBORDER"), TEXT("Window border height"), SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"), TEXT("Dialog window framewidth"), SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"), TEXT("Dialog window frameheight"), SM_CYVTHUMB, TEXT("SM_CYVTHUMB"), TEXT("Vertical scroll thumbheight"), SM_CXHTHUMB, TEXT("SM_CXHTHUMB"), TEXT("Horizontal scroll thumbwidth"), SM_CXICON, TEXT("SM_CXICON"), TEXT("Iconwidth"), SM_CYICON, TEXT("SM_CYICON"), TEXT("Iconheight"), SM_CXCURSOR, TEXT("SM_CXCURSOR"), TEXT("Cursor width"), SM_CYCURSOR, TEXT("SM_CYCURSOR"), TEXT("Cursor height"), SM_CYMENU, TEXT("SM_CYMENU"), TEXT("Menu barheight"), SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"), TEXT("Full screen client areawidth"), SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"), TEXT("Full screen client areaheight"), SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"), TEXT("Kanji window height"), SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"), TEXT("Mouse present flag"), SM_CYVSCROLL, TEXT("SM_CYVSCROLL"), TEXT("Vertical scroll arrowheight"), SM_CXHSCROLL, TEXT("SM_CXHSCROLL"), TEXT("Horizontal scroll arrowwidth"), SM_DEBUG, TEXT("SM_DEBUG"), TEXT("Debug versionflag"), SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"), TEXT("Mouse buttons swappedflag"), SM_CXMIN, TEXT("SM_CXMIN"), TEXT("Minimum windowwidth"), SM_CYMIN, TEXT("SM_CYMIN"), TEXT("Minimum windowheight"), SM_CXSIZE, TEXT("SM_CXSIZE"), TEXT("Min/Max/Close buttonwidth"), SM_CYSIZE, TEXT("SM_CYSIZE"), TEXT("Min/Max/Close buttonheight"), SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"), TEXT("Window sizing framewidth"), SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"), TEXT("Window sizing frameheight"), SM_CXMINTRACK, TEXT("SM_CXMINTRACK"), TEXT("Minimum window trackingwidth"), SM_CYMINTRACK, TEXT("SM_CYMINTRACK"), TEXT("Minimum window trackingheight"), SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"), TEXT("Double click xtolerance"), SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"), TEXT("Double click ytolerance"), SM_CXICONSPACING, TEXT("SM_CXICONSPACING"), TEXT("Horizontal icon spacing"), SM_CYICONSPACING, TEXT("SM_CYICONSPACING"), TEXT("Vertical icon spacing"), SM_MENUDROPALIGNMENT,TEXT("SM_MENUDROPALIGNMENT"),TEXT("Leftor right menu drop"), SM_PENWINDOWS, TEXT("SM_PENWINDOWS"), TEXT("Pen extensionsinstalled"), SM_DBCSENABLED, TEXT("SM_DBCSENABLED"), TEXT("Double-Byte Char Setenabled"), SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"), TEXT("Number of mouse buttons"), SM_SECURE, TEXT("SM_SECURE"), TEXT("Security presentflag"), SM_CXEDGE, TEXT("SM_CXEDGE"), TEXT("3-D borderwidth"), SM_CYEDGE, TEXT("SM_CYEDGE"), TEXT("3-D borderheight"), SM_CXMINSPACING, TEXT("SM_CXMINSPACING"), TEXT("Minimized window spacingwidth"), SM_CYMINSPACING, TEXT("SM_CYMINSPACING"), TEXT("Minimized window spacingheight"), SM_CXSMICON, TEXT("SM_CXSMICON"), TEXT("Small icon width"), SM_CYSMICON, TEXT("SM_CYSMICON"), TEXT("Small icon height"), SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"), TEXT("Small caption height"), SM_CXSMSIZE, TEXT("SM_CXSMSIZE"), TEXT("Small caption buttonwidth"), SM_CYSMSIZE, TEXT("SM_CYSMSIZE"), TEXT("Small caption buttonheight"), SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"), TEXT("Menu bar button width"), SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"), TEXT("Menu bar buttonheight"), SM_ARRANGE, TEXT("SM_ARRANGE"), TEXT("How minimizedwindows arranged"), SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"), TEXT("Minimized windowwidth"), SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"), TEXT("Minimized windowheight"), SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"), TEXT("Maximum draggablewidth"), SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"), TEXT("Maximum draggableheight"), SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"), TEXT("Width of maximizedwindow"), SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"), TEXT("Height of maximizedwindow"), SM_NETWORK, TEXT("SM_NETWORK"), TEXT("Network presentflag"), SM_CLEANBOOT, TEXT("SM_CLEANBOOT"), TEXT("How system wasbooted"), SM_CXDRAG, TEXT("SM_CXDRAG"), TEXT("Avoid drag xtolerance"), SM_CYDRAG, TEXT("SM_CYDRAG"), TEXT("Avoid drag ytolerance"), SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"), TEXT("Present soundsvisually"), SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"), TEXT("Menu check-mark width"), SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"), TEXT("Menu check-markheight"), SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"), TEXT("Slow processor flag"), SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"), TEXT("Hebrew and Arabic enabledflag"), SM_MOUSEWHEELPRESENT,TEXT("SM_MOUSEWHEELPRESENT"),TEXT("Mousewheel present flag"), SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"), TEXT("Virtual screen x origin"), SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"), TEXT("Virtual screen y origin"), SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"), TEXT("Virtual screen width"), SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"), TEXT("Virtual screen height"), SM_CMONITORS, TEXT("SM_CMONITORS"), TEXT("Number ofmonitors"), SM_SAMEDISPLAYFORMAT,TEXT("SM_SAMEDISPLAYFORMAT"),TEXT("Samecolor format flag") } ; /*-------------------------------------------------------------------------- SYSMETS.CPP—— System Metrics Display ProgramNo.1 --------------------------------------------------------------------------*/ #include <windows.h> #include "SysMets.h" LRESULT CALLBACK WndProc(HWND, UINT,WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance, PSTR szCmdLine, int iCmdShow) { staticTCHAR szAppName[] = TEXT("SystemMetrics"); HWNDhwnd; MSGmsg; WNDCLASSwndclass; wndclass.style= CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc= WndProc; wndclass.cbClsExtra= 0; wndclass.cbWndExtra= 0; wndclass.hInstance= hInstance; wndclass.hIcon= LoadIcon( NULL, IDI_APPLICATION); wndclass.hCursor= LoadCursor( NULL, IDC_ARROW); wndclass.hbrBackground= (HBRUSH)GetStockObject( WHITE_BRUSH); wndclass.lpszMenuName= NULL; wndclass.lpszClassName= szAppName; if(!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName,MB_ICONERROR); return0; } hwnd= CreateWindow(szAppName, TEXT("GetSystem Metrics No.3"), WS_OVERLAPPEDWINDOW| WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,NULL, hInstance, NULL); ShowWindow(hwnd,iCmdShow); UpdateWindow(hwnd); while( GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return(int)msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam) { staticint cxChar, cyChar, cxCaps; staticint cxClient, cyClient, iMaxWidth; HDChdc; intx, y, iVerPos, iHorPos, iPaintBeg, iPaintEnd; PAINTSTRUCTps; SCROLLINFOscrollInfo; CHARszBuffer[10]; TEXTMETRICtm; switch(message) { caseWM_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); //初始化滚动条,设置范围和初始位置 放在WM_SIZE消息响应的地方 //SetScrollRange( hwnd, SB_VERT, 0, NUMLINES - 1, FALSE); //SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE); iMaxWidth= 40 * cxChar + 22 * cxCaps; return0; caseWM_SIZE: cxClient= LOWORD( lParam); cyClient= HIWORD( lParam); //Set Vertical scroll bar range and page size scrollInfo.cbSize= sizeof(SCROLLINFO); scrollInfo.fMask= SIF_RANGE | SIF_PAGE; scrollInfo.nMin= 0; scrollInfo.nMax= NUMLINES - 1; scrollInfo.nPage= cyClient / cyChar; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); //Set Horizontal scroll bar range and page size scrollInfo.cbSize= sizeof(SCROLLINFO); scrollInfo.fMask= SIF_RANGE | SIF_PAGE; scrollInfo.nMin= 0; scrollInfo.nMax= 2 + iMaxWidth / cxChar; scrollInfo.nPage= cxClient / cxChar; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); return0; caseWM_VSCROLL: //Get all the vertical scroll bar information scrollInfo.cbSize= sizeof(SCROLLINFO); scrollInfo.fMask= SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &scrollInfo); iVerPos= scrollInfo.nPos; switch(LOWORD(wParam)) { caseSB_TOP: scrollInfo.nPos= scrollInfo.nMin; break; caseSB_BOTTOM: scrollInfo.nPos= scrollInfo.nMax; break; caseSB_LINEUP: scrollInfo.nPos-= 1; break; caseSB_LINEDOWN: scrollInfo.nPos+= 1; break; caseSB_PAGEUP: scrollInfo.nPos-= scrollInfo.nPage; break; caseSB_PAGEDOWN: scrollInfo.nPos+= scrollInfo.nPage; break; caseSB_THUMBPOSITION: scrollInfo.nPos= scrollInfo.nTrackPos; break; default: break; } scrollInfo.fMask= SIF_POS; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); GetScrollInfo(hwnd, SB_VERT, &scrollInfo); //if the position has changed, scroll the window and update it if(scrollInfo.nPos!= iVerPos) { ScrollWindow(hwnd, 0, cyChar*(iVerPos - scrollInfo.nPos), NULL, NULL); UpdateWindow(hwnd); } return0; caseWM_HSCROLL: //Get all the vertical scroll bar information scrollInfo.cbSize= sizeof(SCROLLINFO); scrollInfo.fMask= SIF_ALL; GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); iHorPos= scrollInfo.nPos; switch(LOWORD(wParam)) { caseSB_LINELEFT: scrollInfo.nPos= scrollInfo.nMin; break; caseSB_LINERIGHT: scrollInfo.nPos= scrollInfo.nMax; break; caseSB_PAGELEFT: scrollInfo.nPos-= scrollInfo.nPage; break; caseSB_PAGERIGHT: scrollInfo.nPos+= scrollInfo.nPage; break; caseSB_THUMBPOSITION: scrollInfo.nPos= scrollInfo.nTrackPos; break; default: break; } scrollInfo.fMask= SIF_POS; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); //if the position has changed, scroll the window and update it if(scrollInfo.nPos!= iHorPos) { ScrollWindow(hwnd, cxChar*(iHorPos - scrollInfo.nPos), 0, NULL, NULL); //UpdateWindow( hwnd); } return0; caseWM_PAINT: hdc= BeginPaint( hwnd, &ps); scrollInfo.cbSize= sizeof(SCROLLINFO); scrollInfo.fMask= SIF_POS; GetScrollInfo(hwnd, SB_VERT, &scrollInfo); iVerPos= scrollInfo.nPos; GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); iHorPos= scrollInfo.nPos; //Find Painting limits iPaintBeg= max( 0, iVerPos + ps.rcPaint.top/ cyChar); iPaintEnd= min( NUMLINES - 1, iVerPos + ps.rcPaint.bottom / cyChar); for(int i = iPaintBeg; i <= iPaintEnd; i++) { x= cxChar * ( 1 - iHorPos); y= cyChar * ( i - iVerPos); TextOut(hdc, x, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel)); TextOut(hdc, x + 22*cxCaps, y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc)); SetTextAlign(hdc, TA_RIGHT|TA_TOP); TextOut(hdc, x + 22*cxCaps + 40 * cxChar, y, szBuffer, wsprintf(szBuffer,TEXT("%5d"), GetSystemMetrics(sysmetrics[i].Index))); SetTextAlign(hdc, TA_LEFT|TA_TOP); } EndPaint(hwnd, &ps); return0; caseWM_DESTROY: PostQuitMessage(0); return0; } returnDefWindowProc(hwnd, message, wParam, lParam); }
这个版本中使用ScrollWidow函数在窗口的客户区滚动信息而不是重画它。第二个参数给出水平滚动客户区的数值,第三个参数是垂直滚动客户区的值,单位都是像素。
WM_HSCROLL处理捕获SB_THUMBPOSITION通知码而忽略了SB_THUMBTRACK,因而用户在水平滚动条上拖动滚动框,用户释放鼠标之前程序不会水平滚动窗口的内容。而WM_VSCROLL与之不同,捕获SB_THUMBTRACK消息,而忽略了SB_THUMBPOSITION消息,因此拖动滚动条时内容也会随着滚动条进行滚动。
加快WM_PAINT处理的一个方法:WM_PAINT代码确定无效区域中的行并仅仅重绘这些行。
By Andy @ 2013-11-27