Programming Windows摘要

类别:VC语言 点击:0 评论:0 推荐:

第二章  输出文本

一、        WM_PAINT消息:
在应用程序进入WinMain函数后调用UpdateWindow函数,Windows利用这个机会就给窗体过程发送一个WM_PAINT消息,要求绘制无效区域。
在如下情况下窗体过程会收到WM_PAINT消息:
1、在用户移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见时

2、用户改变窗口大小时(但是必须具有CS_HREDRAW和CS_VREDRAW设置)

3、程序使用ScrollWindow或者ScrollDc函数滚动客户区一部分时

4、程序使用InvalidateRect或者InvalidateRect函数显式产生WM_PAINT消息。


如下情况下Windows可能发出WM_PAINT消息:
1、Windows擦除覆盖了部分窗口的对话框或者消息框
2、菜单下拉出来然后释放
3、显示工具提示


某些情况下Windows总是保存他所覆盖显示区域,然后自动恢复:
1、鼠标光标穿越客户区
2、图标拖过客户区

注意窗口过程接收WM_PAINT消息的唯一条件是:客户区的某一部分失效。
 

现对窗体过程对WM_PAINT消息的处理如下说明:
1、当窗口过程处理WM_PAINT之前另一个区域变无效,Windows就会去计算一个包围在两个区域的新无效区域,并将这些变化写入绘制信息结构中。Windows是不会,请注意,不会把多个WM_PAINT消息放入消息队列的。

 

2、Windows默认情况下对WM_PAINT消息的处理,也就是DefWindowProc对该消息的响应如下:

Case WM_PAINT:

BeginPaint(hwnd, &ps);

EndPaint(hwnd, &ps);

Return 0;



二、设备描述表
1、绘图信息结构
typedef struct tagPAINTSTRUCT {                                 // ps
    HDC  hdc;
    BOOL fErase;
    RECT rcPaint;
    BOOL fRestore;
    BOOL fIncUpdate;
    BYTE rgbReserved[32];
} PAINTSTRUCT;
对这个结构说明如下:

程序员所用到一般是前三个字段,其他的由Windows处理。
hdb设备描述表句柄。
fErase一般来说是FALSE(0),意味着Windows已经擦除了无效矩形背景。

如果使用InvalidRect(hwnd,NULL,FALSE)函数后,窗口过程处理完WM_PAINT消息后就将fErase字段置1。
第三个参数是Rect类型:
typedef struct _RECT {
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
} RECT;  
表示了无效区域的位置。

2、获取设备描述表的句柄
方法1:
处理WM_PAINT的时候使用这个方法:
PAINTSTRUCT ps;
HDC hdc;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
return 0;
注意这种方法错误:
case WM_PAINT:
return 0;
千万不要这样!!Windows将一个WM_Paint消息放入消息队列就是因为客户区一部分无效。如果不调用BeginPaint与EndPaint或者ValidateRect那么这个区域不会变成有效,然后Windows就会一直发WM_Paint消息。
另外,Windows通过一个InvalidateRect让整个客户区内的矩形无效,当收到WM_PAINT消息的时候可以通过GetUpdateRect在获得无效矩形的坐标。
在处理WM_PAINT消息期间,窗口过程调用了BeginPaint以后,(BeginPaint函数一般在准备绘制时导致无效区域的背景被当前的画刷擦除,该函数也填入ps结构的域)整个客户区变成有效,程序可以通过调用ValidateRect函数使客户区内的任意矩形区域变为有效。如果调用这条命令以后使得整个客户区有效,那么当前消息队列中的所有的WM_PAINT消息就被删除。

方法二:
一般调用GetDC和ReleaseDC对键盘消息或者鼠标消息作出反应。
hdc=GetDC(hwnd);
ReleaseDC(hwnd,hdc);
注意:必须在退出窗口过程之前调用ReleaseDC,不要在一个消息中调用GetDC,却在另一个消息中去调用ReleaseDC


与GetDC相似的还有GetwindowDC,这个函数返回整个窗口的设备描述句柄。

两种方式的不同:

GetDC返回的设备描述表句柄具有一个剪取区域,等于整个客户区,GetDC也不会使无效区域变成有效。
而BeginPaint开始的时候就会恢复无效区域。
如下调用
InvalidateRect(hwnd,NULL,TRUE)
可以使得整个客户区变为无效,并擦除背景,这是一种简单的重画客户区的方法。
而ValidateRect(hwnd,NULL)则相反。

三、TextOut细节问题:
TextOut(hdc,x,y,psText,iLength)
psText:指向要显示的字符的指针
iLength:所显示字符串的长度
与wsprintf如下综合使用:
int iLength;
TCHAR szBuffer[40];
iLength=wsprintf(szBuffer,TEXT(“The sum of %i and %i is %i”),iA,IB,IA+IB);
TextOut(hdc,x,y,szBuffer,iLength);




四、字体的问题
程序调用一个GetSystemMetrics函数来确定用户界面的大小
如下调用
TEXTMERIC tm;
hdc=GetDC(hwnd);
GetTextMetrics(hdc,&tm);
ReleaseDC(hwnd,hdc);

关于TEXTMETRIC结构如下:
typedef struct tagTEXTMETRIC { // tm
    LONG tmHeight;
    LONG tmAscent;
    LONG tmDescent;
    LONG tmInternalLeading;
    LONG tmExternalLeading;
    LONG tmAveCharWidth;
    LONG tmMaxCharWidth;
    LONG tmWeight;
    LONG tmOverhang;
    LONG tmDigitizedAspectX;
    LONG tmDigitizedAspectY;
    BCHAR tmFirstChar;
    BCHAR tmLastChar;
    BCHAR tmDefaultChar;
    BCHAR tmBreakChar;
    BYTE tmItalic;
    BYTE tmUnderlined;
    BYTE tmStruckOut;
    BYTE tmPitchAndFamily;
    BYTE tmCharSet;
} TEXTMETRIC;

2、格式化文本
首先使用两个变量存放平均的字符高度宽度:
static int cxChar,cyChar;
case WM_CREATE:
        hdc=GetDC(hwnd);
GetTextMerics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight+tm.tmExternalLeading;
ReleaseDC(hwnd,hdc);
return 0;

五、滚动条
1、客户区域的大小:
客户区与是否最大化可以通过
使用SM_CXFULLSCREEN和SM_CYFULLSCREEN为参数调用GetSystemMetrics获得。
另外可以通过GetClientRect函数来确定客户区的大小。这个函数应该在WM_SIZE消息发生的时候去调用。传给窗口过程的lParam参数的低位字中包含客户区的宽度,高位中包含客户区的高度。
使用如下两个变量保存:
static int cxClient,cyClient;
实际使用如下:
WM_SIZE:
        cxClient=LOWORD(lParam);
        cyClient=HIWORD(lParam);
return 0;


客户区文本的总行数:cyClient/cyChar
客户区水平方向可显示的小写字母数:cxClient/cxChar

另外大多数情况下,一个WM_SIZE消息后面总是跟着WM_PAINT消息,这是因为我们制定窗体风格:CS_HREDRAW|CS_VREDRAW
这类风格告诉Windows当水平或者垂直大小发生变化,强制刷新客户区。

2、滚动条生成:
滚动条的生成可以在CreateWindow中的窗体风格中标示:
WS_VSCROLL或者WS_HSCROLL


3、滚动条范围和位置:
默认情况下范围0~100
如下函数改变范围:
SetScrollRange(hwnd,iBar,iMin,iMax,bRedraw);
bRedraw:如果要windows根据新范围重画滚定条,则设置bRedraw为TRUE,如果在调用SetScrollRange以后调用了影响滚动条位置的其他函数,应该将bRedraw设置为FALSE,避免多次重画。
iBar:数值是SB_VERT或者SB_HORZ

设置滚动条位置:
SetScrollPos(hwnd,iBar,iPos,bRedraw);
另外Windows提供了GetScrollRange和GetScrollPos来获得滚动条的当前范围和当前的位置。

请注意这个函数GetScrollPos,该函数在用户拖动滚动条或者滚动块的时候并不改变,必须使用SetScrollPos函数才能改变其数值,这就是为什么拖动滚动块,如果不调用SetScrollPos来处理SB_THUMBTRACK或者SB_THUMPOSITION消息,在用户释放鼠标键后,滚动框会迅速跳回原来位置的原因。

4、滚动条消息
下面是windows对滚动条的处理:
处理所有滚动条鼠标时间
当用户在滚动条内单击鼠标时,提供一种“反向闪烁”
当用户在滚动条内拖动滚动框,移动滚动框
为包含滚动条窗口的窗口过程发送滚动条消息

以下是程序员要处理的:
初始化滚动条的范围和位置
处理窗口过程响应滚动条消息
更新滚动条内滚动框的位置
更改客户区的内容以响应对滚动条更改

windows给窗口过程发送的是WM_VSCROLL(上下移动),
WM_HSCROLL(供左右移动)消息。
滚动条的消息都是一对的,在鼠标按下和释放的时候。
与其他消息一样WM_VSCROLL和WM_HSCROLL也有wParam和lParam消息参数。对于来自作为窗口的一部分而创建的滚动条消息,可以忽略lParam,他只用于作为子窗口而创建的滚动条。
而wParam消息参数则被分为一个低字位和一个高字位。其中低字位是一个数值,指出了鼠标对滚动条进行的操作(见P91),高字位是用户在拖动滚动框时的当前位置。

 

鼠标拖动滚动框移动它的时候,应用程序将收到SB_THUMBTRACK消息。然而,如果不通过调用SetScrollPos来处理SB_THUMBTRACK或SB_THUMBPOSITION消息,用户释放释放鼠标键后,滚动框会迅速跳会原来的位置。

如果处理SB_THUMBTRACK消息,用户拖动滚动框时您需要移动客户区的内容。

如果处理SB_THUMBPOSITION消息,只要在用户停止滚动框时移动客户区的内容。

而WINUSER.H头文件还包括的SB_TOP、SB_BOTTOM、SB_LEFT和SB_RIGHT通知码之处滚动条已经移到了它的最大或者最小位置。但是作为应用程序窗口一部分而创建的滚动条来说,永远不会接受到这些通知码。

 

5、Win32API下的滚动条函数

Win32API提供了如下两个滚动条函数:

SetScrollInfo和GetScrollInfo函数

SetScrollInfo(hwnd, iBar, &si, bRedraw);

GetScrollInfo(hwnd, iBar, &si);

 

IBar: SB_VERT或SB_HORZ或者是滚动条控制的SB_CTL

 

SCROLLINFOR结构定义如下:

typedef struct tagSCROLLINFO

{

     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 ;

 

在程序中,可以定义如下的SCROLLINFO结构类型:SCROLLINFO si;

si.cbSize = sizeof(si);

si.cbSize = sizeof (SCROLLINFO);是一致的

 

fMask字段以SIZ前缀开头的一个或者多个标志,并可以采用C的位操作OR函数|组合这些标志。

SIFmask——nMin和nMax字段设置为滚动条所需的范围。

SIFPOS——nPos位置所需的位置

SIF_PAGE——获取页面的大小(行数)

fMask标志

含义

SIFmask

nMin和nMax字段设置为滚动条所需的范围。

SIFPOS

nPos位置所需的位置

SIF_PAGE

获取页面的大小(行数)

SIF_TRACKPOS

nTrackPos字段指出当前32位滚动条位置

SIFALL

以上所有参数均要设置

SIF_DISABLENOSCROLL

在程序试图隐藏滚动条时,禁用滚动条

 

 

 

 

 

6、Win32两个API函数设置滚动条的范围:

 

假设窗体大小50行,而NUMLINES等于75

那么滚动范围是从:0——50到25——75

因此可以通过如下参数设定:

si.cbSize = sizeof(SCROLLINFO);

si.cbMask = SIF_RANGE|SIF_PAGE;

si.nMin = 0;

si.nMax = NUMLINES –1;

si.nPage = cyClient/cyChar;

SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

这样设置以后,Windows会把最大滚动条位置限制为si.nMax – si.nPage +1,而不是si.nMax

也就是说:NUMLINES等于25(si.nMax等于74),

si.nPage等于50。于是最大的位置限制为74-50+1=25正是我们所需要的。

当页面大小与滚动条范围一样大的时候,也就是nPage大于等于75的时候,Windows通常就会隐藏滚动条,但是可以通过SIF_DISABLENOSCROLL标志来禁用滚动条而不是隐藏它。

 

6、ScrollWindow函数:

BOOL ScrollWindow(

  HWND hWnd,              // handle to window

  int XAmount,            // horizontal scrolling

  int YAmount,            // vertical scrolling

  CONST RECT *lpRect,     // client area

  CONST RECT *lpClipRect  // clipping rectangle

);

第二个参数指出了水平滚动客户区的数值。

第三个参数指出了垂直滚动客户区的数值,单位都是像素。

最后两个参数设置为NULL,指出了要滚动整个客户区。Windows自动把客户区中未被滚动操作覆盖的矩形设为无效。这样会产生WM_PAINT消息。再也不需要InvalidateRect了。

 

 

本文地址:http://com.8s8s.com/it/it1068.htm