第一百一十六个SetViewportOrgEx将窗口原点(0,0)映射到设备坐标点X,Y
函数定义:BOOL SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint);
hdc是要映射的设备上下文,X,Y是要映射的坐标,lpPoint是窗口原点原来所映射的坐标,不需获取可为NULL。
比如这样一个语句:(假设hdc的设备模式为MM_TEXT)那么调用下面语句后
SetViewportOrgEx(hdc,200,200,NULL);
窗口的坐标就变成下面这样了:
看例子:
#include<windows.h>
int main()
{
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
HDC hdc=GetDC(wnd);
::SetViewportOrgEx(hdc,200,200,NULL);//设置坐标点200,200为窗口原坐标点0,0
while(1)
{
Rectangle(hdc,0,0,100,100);
Rectangle(hdc,0,0,-100,-100);
Sleep(300);
}
return 0;
}
第一百一十七个SetWindowOrgEx将设备坐标点X,Y映射到窗口原点0,0
函数定义:BOOL SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint);
跟SetViewportOrgEx的映射正好相反,这个函数的参数解释参照SetViewportOrgEx。
这里仅给出调用
SetWidnowOrgEx(hdc,200,200,NULL);语句后的窗口坐标图。当然参数也可以为负数,可自行推算窗口坐标点。
第一百一十八个SetScrollPos设置滚动条位置
函数定义:int
SetScrollPos(
HWND hWnd,//滚动条所在窗口句柄
int nBar,//滚动条类型,参照SetScrollInfo函数
int nPos,//滚动条位置
BOOL bRedraw);//是否重画滚动条
例子:参考SetScrollInfo函数的例子。
第一百一十九个SetScrollInfo设置滚动条信息
函数定义:int SetScrollInfo(HWND hWnd;//滚动条所在的窗口句柄
int fnBar,//滚动条类型,SB_VERT垂直滚动条,SB_HORZ水平滚动条
LPSCROLLINFO lpsi,//滚动条各项信息结构体,后面会有介绍
BOOL fRedraw);//指明是否重画滚动条
SetScrollInfo函数的第三个参数是一个SCROLLINFO结构体指针,关于该结构体的解释如下:
typedef struct tagSCROLLINFO
{
UINT cbSize;//指明SCROLLINFO结构体大小,赋值sizeof(SCROLLINFO)
UINT fMask;//指明nMin-nMax、nPage、nPos哪些成员有效,分别对应SIF_RANGE、SIF_PAGE、SIF_POS。
//为SIF_DISABLENOSCROLL表明禁用滚动条。
int nMin;//滚动条范围最小值
int nMax;//滚动条范围最大值
UINT nPage;//页大小(跟滚动条大小相关联)
int nPos;//滚动条位置
int nTrackPos;//这个成员在GetScrollInfo函数中有用。用于接收滚动条位置
} SCROLLINFO, FAR *LPSCROLLINFO;
这里要说明的是,为了避免不必要的麻烦,滚动条大小,滚动条范围必须和可见窗口以及实际窗口(视图?)大小一致。
也就是说当你可见窗口大小是300的时候,那么滚动条页大小也必须是300。如果实际窗口大小是2000的时候,那么滚动条范围就是0~1999,分别是最小值和最大值。而滚动条可移动范围也就是位置是在0~1700,因为当滚动条位置是0的时候,显示窗口是0-300,而位置是1700的时候刚好显示完窗口也就是1700~2000。滚动条位置是以滚动条上边为准。
要想让一个窗口具有滚动条风格,在调用CreateWindow函数时在第三个参数里指明就行了,在CreateWindow函数里也解释过,WS_VSCROLL指明窗口具有垂直滚动条风格,而WS_HSCROLL是水平滚动条。
关于滚动条消息的解释(WM_VSCROLL垂直,WM_HSCROLL水平)
下面的消息码存存于wParam参数里:
SB_LINEUP 滚动条上面的箭头按钮被单击了一下,水平滚动条对应的是左边的按钮
SB_LINEDOWN 滚动条下面的箭头按钮被单击了下,水平滚动条对应的是右边的按钮
SB_PAGEUP 滚动条滑柄与上面箭头按钮之间的区域被单击了一下,水平滚动条是左边之间
SB_PAGEDOWN 滚动条滑柄与下面箭头按钮之间的区域被单击了一下,水平滚动条是右边之间。
SB_ENDSCROLL 鼠标离开了滚动条。
SB_THUMBTRACK 滚动条滑柄被拉动
SB_THUMBPOSITION 滚动条滑柄被拉完后,鼠标左键抬起时。
例子:垂直滚动条窗口应用
水平滚动条的使用方法,跟这个大同小异,所以就不加放进来了,徒增负担,这里面使用了一个最关键的函数SetWindowOrgEx,映射窗口原点,时时根据滚动条位置设置对应的窗口原点。以保证当前显示窗口正确的部分。每次有滚动信息产生的时候,都重画了窗口,这个方法很原始。以后讲到ScrollWIndow函数时会有改进的,还有本例部分参考了
Programing Windows with MFC里面的内容,只不过那里面是用类来实现的,而我这个是完全是用API来实现的,包括创建窗口,消息处理。
好吧,新建一个WIN32 工程,下面是代码:
#include<windows.h>
#include<stdio.h>
#define LINESIZE 8
int nVScrollPos=0,nVPageSize,nViewHeight=2000;
LRESULT CALLBACK WinSunProc(HWND wnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc;
wndcls.lpszClassName="windowclass";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWND wnd;
wnd=CreateWindow("windowclass","first windows",
WS_OVERLAPPEDWINDOW^WS_SIZEBOX^WS_MAXIMIZEBOX|WS_VSCROLL,300,200,600,400,
NULL,NULL,hInstance,NULL);
RECT rect;
GetClientRect(wnd,&rect);
nVPageSize=rect.bottom;//可见窗口高度
SCROLLINFO si;
si.cbSize=sizeof(SCROLLINFO);
si.nMin=0;
si.nMax=1999;//窗口实际大小是2000
si.nPage=nVPageSize;//可见窗口高度和页大小设置一致
si.nPos=0;
si.fMask=SIF_ALL;
::SetScrollInfo(wnd,SB_VERT,&si,FALSE);//设置滚动条信息
ShowWindow(wnd,SW_SHOWNORMAL);
UpdateWindow(wnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
};
LRESULT CALLBACK WinSunProc(HWND wnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
char str[20];
PAINTSTRUCT ps;
HDC hdc;
int y,nDelta;
int nScrollPos,nMaxPos;
switch(uMsg)
{
case WM_VSCROLL:
{
switch(LOWORD(wParam))//wParam的低二字节存储有滚动条消息码。
{
case SB_LINEUP:
nDelta=-LINESIZE;//负数是向上滚动 LINESIZE 是滚动大小8
break;
case SB_PAGEUP:
nDelta=-nVPageSize;//滚动一页,也就是可见窗口高度
break;
case SB_THUMBTRACK:
//wParam的高二字节储存有滚动条的位置
nDelta=(short int)HIWORD(wParam)-nVScrollPos;
break;
case SB_PAGEDOWN:
nDelta=nVPageSize;
break;
case SB_LINEDOWN:
nDelta=LINESIZE;
break;
default:
return 0;
}
}
//计算新位置
//有这样一种情况,当滚动位置是5时,再向上滚动8个点,那是不可能的,所以要计算。
nScrollPos=nVScrollPos+nDelta;
nMaxPos=nViewHeight-nVPageSize;
if(nScrollPos<0)
nDelta=-nVScrollPos;
else if(nScrollPos>nMaxPos)
nDelta=nMaxPos-nVScrollPos;
if(nDelta!=0)
{
nVScrollPos+=nDelta;
SetScrollPos(wnd,SB_VERT,nVScrollPos,TRUE);//设置新位置
::InvalidateRect(wnd,NULL,TRUE);//刷新窗口
}
return 0;
case WM_PAINT:
hdc=BeginPaint(wnd,&ps);
::SetWindowOrgEx(hdc,0,nVScrollPos,NULL);
for(y=0;y<2000;y+=20)
{
MoveToEx(hdc,0,y,NULL);
LineTo(hdc,600,y);
sprintf(str,"%d:",y);
TextOut(hdc,0,y+2,str,strlen(str));
}
return 0;
case WM_CLOSE: ::DestroyWindow(wnd);return 0;
case WM_DESTROY: PostQuitMessage(0);return 0;
}
return DefWindowProc(wnd,uMsg,wParam,lParam);
}
运行效果: