沧桑不尽
无穷的沧桑,不尽的落叶
 目的还是为了大家进一步熟悉Windows的窗口实现消息的机制,也使大家了解多一点的Windows API 函数,
从而利于日后的实际编程。平常我们都是用鼠标拖着窗口来改变窗口的,今天我们来点新鲜的,通过单击鼠标来使窗口改变位置。
从前面的知识中,我们知道,窗口的初始位置是在CreateWindow函数中设定的,Windows既然可以让用户通过鼠标拖来改
变窗口位置,那么肯定就有函数是专门用来改变窗口位置的。是的,的确有这样的函数,常用的有两个,它们是
etWindowPos和MoveWindow。两个函数的详细情况如下:

SetWindowPos

  函数功能:该函数改变一个子窗口,弹出式窗口式顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。

  函数原型:BOOL SetWindowPos(HWN hWnd,HWND hWndlnsertAfter,int X,int Y,int cx,int cy,UNIT.Flags);

  参数:

  hWnd:窗口句柄。

  hWndlnsertAfter:在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄,或下列值之一:

  HWND_BOTTOM:将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶级位置,并且被置在其他窗口的底部。

  HWND_NOTOPMOST:将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后)。如果窗口已经是非顶层窗口则该标志不起作用。

  HWND_TOP:将窗口置于Z序的顶部。

  HWND_TOPMOST:将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。

  查看该参数的使用方法,请看说明部分。

  x:以客户坐标指定窗口新位置的左边界。

  Y:以客户坐标指定窗口新位置的顶边界。

  cx:以像素指定窗口的新的宽度。

  cy:以像素指定窗口的新的高度。

  uFlags:窗口尺寸和定位的标志。该参数可以是下列值的组合:

  SWP_ASNCWINDOWPOS:如果调用进程不拥有窗口,系统会向拥有窗口的线程发出需求。这就防止调用线程在其他线程处理需求的时候发生死锁。

  SWP_DEFERERASE:防止产生WM_SYNCPAINT消息。

  SWP_DRAWFRAME:在窗口周围画一个边框(定义在窗口类描述中)。

  SWP_FRAMECHANGED:给窗口发送WM_NCCALCSIZE消息,即使窗口尺寸没有改变也会发送该消息。如果未指定这个标志,只有在改变了窗口尺寸时才发送WM_NCCALCSIZE。

  SWP_HIDEWINDOW;隐藏窗口。

  SWP_NOACTIVATE:不激活窗口。如果未设置标志,则窗口被激活,并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置)。

  SWP_NOCOPYBITS:清除客户区的所有内容。如果未设置该标志,客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区。

  SWP_NOMOVE:维持当前位置(忽略X和Y参数)。

  SWP_NOOWNERZORDER:不改变z序中的所有者窗口的位置。

  SWP_NOREDRAW:不重画改变的内容。如果设置了这个标志,则不发生任何重画动作。适用于客户区和非客户区(包括标题栏和滚动条)和任何由于窗回移动而露出的父窗口的所有部分。如果设置了这个标志,应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分。

  SWP_NOREPOSITION;与SWP_NOOWNERZORDER标志相同。

  SWP_NOSENDCHANGING:防止窗口接收WM_WINDOWPOSCHANGING消息。

  SWP_NOSIZE:维持当前尺寸(忽略cx和Cy参数)。

  SWP_NOZORDER:维持当前Z序(忽略hWndlnsertAfter参数)。

  SWP_SHOWWINDOW:显示窗口。

  返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误消息,请调用GetLastError函数。

  备注:如果设置了SWP_SHOWWINDOW和SWP_HIDEWINDOW标志,则窗口不能被移动和改变大小。如果使用SetWindowLoog改变了窗口的某些数据,则必须调用函数SetWindowPos来作真正的改变。使用下列的组合标志:SWP_NOMOVEISWP_NOSIZEISWP_FRAMECHANGED。

  有两种方法将窗口设为最顶层窗口:一种是将参数hWndlnsertAfter设置为HWND_TOPMOST并确保没有设置SWP_NOZORDER标志;另一种是设置窗口在Z序中的位置以使其在其他存在的窗口之上。当一个窗口被置为最顶层窗口时,属于它的所有窗口均为最顶层窗口,而它的所有者的z序并不改变。

  如果HWND_TOPMOST和HWND_NOTOPMOST标志均未指定,即应用程序要求窗口在激活的同时改变其在Z序中的位置时,在参数hWndinsertAfter中指定的值只有在下列条件中才使用:

  在hWndlnsertAfter参数中没有设定HWND_NOTOPMOST和HWND_TOPMOST标志。

  由hWnd参数标识的窗口不是激活窗口。

  如果未将一个非激活窗口设定到z序的顶端,应用程序不能激活该窗口。应用程序可以无任何限制地改变被激活窗口在Z序中的位置,或激活一个窗口并将其移到最高级窗口的顶部或非最高级窗口的顶部。

  如果一个顶层窗口被重定位到z序的底部(HWND_BOTTOM)或在任何非最高序的窗口之后,该窗口就不再是最顶层窗口。当一个最顶层窗口被置为非最顶级,则它的所有者窗口和所属者窗口均为非最顶层窗口。

  一个非最顶端窗口可以拥有一个最顶端窗口,但反之则不可以。任何属于顶层窗口的窗口(例如一个对话框)本身就被置为顶层窗口,以确保所有被属窗口都在它们的所有者之上。

  如果应用程序不在前台,但应该位于前台,就应调用SetForegroundWindow函数来设置。

  Windows CE:如果这是一个可见的顶层窗口,并且未指定SWP_NOACTIVATE标志,则这个函数将激活窗口、如果这是当前的激活窗口,并且指定了SWP_NOACTIVATE或SWP_HIDEWINDOW标志,则激活另外一个可见的顶层窗口。

  当在这个函数中的nFlags参数里指定了SWP_FRAMECHANGED标志时,WindowsCE重画窗口的整个非客户区,这可能会改变客户区的大小。这也是重新计算客户区的唯一途径,也是通过调用SetwindowLong函数改变窗口风格后通常使用的方法。

SetWindowPos将使WM_WINDOWPOSCHANGED消息向窗口发送,在这个消息中传递的标志与传递给函数的相同。这个函数不传递其他消息。

MoveWindow

  函数功能:该函数改变指定窗口的位置和尺寸。对于顶层窗口,位置和尺寸是相对于屏幕的左上角的:对于子窗口,位置和尺寸是相对于父窗口客户区的左上角坐标的。

  函数原型:BOOL MoveWindow(HWND hWnd,int x.int y,int nWidth,int nHeight,BOOL BRePaint);

  参数:

  hWnd:窗口句柄。

  x:指定窗口的新位置的左边界。

  Y:指定窗口的新位置的顶部边界。

  nWidth:指定窗口的新的宽度。

  nHaight:指定窗口的新的高度。

  bRepaint:确定窗口是否被刷新。如果该参数为TRUE,窗口接收一个WM_PAINT消息;如果参数为FALSE,不发生任何刷新动作。它适用于客户区,非客户区(包括标题栏和滚动条),及由于移动子窗口而露出的父窗口的区域。如果参数为FALSE,应用程序就必须明确地使窗口无效或重画该窗口和需要刷新的父窗口。

  返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。

  备注:如果bRepaint为TRUE,系统在窗口移动后立即给窗口过程发送WM_PAINT消息(即由MoveWindow函数调用UPdateWindow函数)。如果bRepaint 为FALSE,系统将WM_PAINT消息放在该窗口的消息队列中。消息循环只有在派遣完消息队列中的其他消息时才派遣WM_PAINT消息。

MoveWindow给窗口发送WM_WfNOWPOSCHANGING,WM_WINDOWPOSCHANGED,WM_MOVE,WM_SIZE和WM_NCCALCSIZE消息。

以上的东西,都是从msdn上翻译过来的,把它们翻译过来,是在有故意添文字之嫌。看了函数说明就好的多了吧,我们只把上节中的代码稍加修改即可,我这里给出我的代码,大家可以借鉴下(我觉得知道这两个函数怎么用,真是没什么要的说的了):

 

代码
#include <windows.h>

#define CUR 0x1000 //预定义光标的id

HDC hDC;
//HDC是指设备上下文(暂时不用管,只要能这样用就可以了)的句柄
//PAINTSTRUCT要绘制的信息
//详情请登陆http://msdn.microsoft.com/en-us/library/dd162768(VS.85).aspx
//了解下就可以了,没什么重要的东西
PAINTSTRUCT paint;
RECT rect;
//RECT用来存储窗口信息的结构,只要是窗口的坐标、宽度和高度。

//自定义函数MoveLeft,使窗口向左移动5像素,此函数中调用MoveWindow函数
int MoveLeft(HWND hWnd)
{
GetWindowRect(hWnd,
&rect);//获取窗口的信息
MoveWindow(hWnd,rect.left-5,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE);
return 1;
}

//自定义函数MoveRight,是窗口向右移动5像素,此函数中调用SetWindowPos函数(换个口味)
int MoveRight(HWND hWnd)
{
GetWindowRect(hWnd,
&rect);//获取窗口的信息
SetWindowPos(hWnd,HWND_NOTOPMOST,
rect.left
+5,rect.top,
rect.right
-rect.left,
rect.bottom
-rect.top,
SWP_NOZORDER);
return 1;
}

//回调函数
LRESULT WINAPI WinProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
switch(Msg)//处理消息过程,什么是消息,下节再讲
{
case WM_PAINT:
//BginPaint做些绘画的开始工作,填充PAINSTURCT结构,返回设备上下文(暂时不用理解)句柄
hDC=BeginPaint(hWnd,&paint);
//GetClientRect用来获取窗口所在客户区的位置大小信息
GetClientRect(hWnd,&rect);
//DrawText就是Windows用来“画字”的笔了,DT_*之类是指文字的样式,看字面意思也能看懂的
//有多少样式呢,可以查看这里http://msdn.microsoft.com/en-us/library/ms901121.aspx
//本例中是单线、水平居中和竖直居中。
DrawText(hDC,"Hello World!",-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
//EndPaint就是做些收尾的工作了。
EndPaint(hWnd,&paint);
break;
case WM_LBUTTONUP://鼠标左键松开时
MoveLeft(hWnd);
break;
case WM_RBUTTONUP://鼠标右键松开时
MoveRight(hWnd);
break;
case WM_DESTROY://响应鼠标单击关闭按钮事件
PostQuitMessage(0);//退出消息队列,至于什么是消息队列,下节说
return 0;//退出函数
}
return DefWindowProc(hWnd,Msg,wParam,lParam);
}

//主函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
char *cName = "myWindow";
WNDCLASSEX wc;
HWND hWnd;
MSG Msg;

wc.cbClsExtra
= 0;
wc.cbWndExtra
= 0;
wc.cbSize
= sizeof(WNDCLASSEX);
wc.hbrBackground
= (HBRUSH)GetStockObject(WHITE_BRUSH);//通过函数来设置一个白色的背景,这里大家设置为NULL看看,会很有趣的
wc.hCursor = LoadCursor(hInstance,MAKEINTRESOURCE(CUR));//这里改了,来载入光标资源
wc.hIcon = NULL;//不设置
wc.hIconSm = NULL;//不设置
wc.hInstance = hInstance;//当前程序的句柄,hInstance是有系统给传递的
wc.lpfnWndProc = WinProc;//窗口处理过程的回调函数。
wc.lpszClassName =(LPSTR)cName;//窗口类的名字。
wc.lpszMenuName = NULL;//目录名,不设置
wc.style = CS_HREDRAW | CS_VREDRAW;

RegisterClassEx(
&wc);//在系统中注册

hWnd
= CreateWindowEx(WS_EX_CLIENTEDGE,cName,"我的窗口我喜欢",WS_OVERLAPPEDWINDOW,
200,100,600,400,NULL,NULL,hInstance,NULL);//创建窗口,窗口标题为"我的窗口我喜欢"
if(hWnd == NULL)
{
//容错处理
MessageBox(NULL,"There's an Error","Error Title",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
ShowWindow(hWnd,nShowCmd);
//显示窗口
UpdateWindow(hWnd);

//下面是对消息的循环处理,大家先不必管这些,下节课我会细说的
while(GetMessage(&Msg,NULL,0,0))
{
TranslateMessage(
&Msg);//翻译消息
DispatchMessage(&Msg);//分派消息
}
return Msg.message;
}

 

 

这个结果,不能用静态的图片来说明什么,大家自己编译连接后,击鼠标试试,看能出现预期的效果不,我这里就不贴图出来了,贴静态的没啥意思,贴动态的,有点不想整,我也懒,哈。

 

经过这几次折腾,如果大家真的每一次都手写了,相信其中的那关键的且相同的那部分代码应该是非常熟悉了,到此就我们
就该升级了,就行高一层次的修炼,后面两节,我准备给大家说些资源的深入细节,还有再在写几个完全实用的小程序和几
个恶作剧程序,不知大家意下如何。

 

 

 

 

 

 

posted on 2010-01-23 20:49  沧桑不尽  阅读(2541)  评论(7编辑  收藏  举报