MFC学习笔记——如何使窗口最大化,但不遮盖任务栏

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/shanshangyouzhiyangM/article/details/51954472

在写程序时,如果包含了标题栏,但是没有包含最大化按钮或者最小话按钮。

那么人工用ShowWindow(SW_MAXIMIZE),窗口会盖住任务栏,并且窗口最大化后还可以拖动!

1、窗口最大化可以拖动的问题就是在最大化时不让其拖动,

只要响应WM_NCLBUTTONDOWN时判断是否最大化 IsZoomed如果是则直接返回就OK!

2、解决遮住任务栏的问题。。

使用ShowWindow(SW_MAXIMIZE),使对话框最大化后,任务栏也被遮住了,如何去掉任务栏的遮盖?而且对话框窗口还处于激活状态。

(一)方法一

此时,应该没有设置WS_CAPTION属性,只能自己MoveWindow。

函数ShowWindow(SW_MAXIMIZE)应该是在OnInitDialog()中调用的。

ShowWindow(SW_MAXIMIZE)是覆盖任务栏的。
用下面代码代替之

 CRect rcWorkArea; 
 // 获取工作区的大小
 SystemParametersInfo(SPI_GETWORKAREA,0,&rcWorkArea,0); 
 MoveWindow(&rcWorkArea); 

下面介绍一下SystemParametersInfo()函数。

  • 函数原型:
    BOOL SystemParametersinfo(UINT uiAction,UINT uiParam,PVOID pvParam,UINT fWinlni);

  • 参数:
    uiAction:该参数指定要查询或设置的系统级参数。
    SPI_GETWORKAREA:检索工作区大小。工作区是指没有被任务遮盖的屏幕部分。
    uiParam:uiParam 在参数说明中所有为ulParam均为错误。
    vParam:与查询或设置的系统参数有关。关于系统级参数的详情,请参考uiAction参数。否则在没有指明情况下,必须将该参数指定为NULL。
    fWinlni:如:如果设置系统参数,则它用来指定是否更新用户配置文件(Profile)。亦或是否要将WM_SETTINGCHANGE消息广播给所有顶层窗口,以通知它们新的变化内容。

注意:由于这里,ShowWindow(SW_MAXIMIZE)函数是在OnInitDialog()中调用的,按照这种方法,用MoveWindow来替换的话,如果工作区发生改变,窗口依然会覆盖其他部件(比如,任务栏由下面挪到侧边或是最上方,这是工作区都发生了改变,而窗口位置不变,就会遮盖住任务栏,或是显示不完全)。

(二)方法二
重载WM_GETMINMAXINFO:

void CTabDlg::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)   
{  
    // TODO: Add your message handler code here and/or call default  
    lpMMI->ptMaxSize.y = GetSystemMetrics(SM_CYFULLSCREEN)+GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYDLGFRAME);  
    CDialog::OnGetMinMaxInfo(lpMMI);  
}  

afx_msg void OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI );
参数:
lpMMI 指向一个MINMAXINFO结构,其中包含了有关窗口的最大化大小和位置以及最小、最大跟踪大小的信息。有关这个结构的更多信息参见MINMAXINFO结构。
说明:
每当Windows需要知道窗口的最大化位置或大小,或者最小、最大的跟踪大小时,框架就调用这个成员函数。最大化大小是指当窗口的边框被完全扩展时窗口的大小。窗口的最大跟踪大小是指用边框改变窗口的大小时可以达到的最大窗口大小。窗口的最小跟踪大小是指用边框改变窗口大小时可以达到的最小窗口大小。
Windows填充一个点组成的数组,为不同的位置和大小指定了缺省值。应用程序可以在OnGetMinMaxInfo中改变这些值。
注意
框架调用这个成员函数以允许你的应用程序处理一个Windows消息。传递给你的成员函数的参数反映了接收到消息时框架接收到的参数。如果你调用了这个函数的基类实现,则该实现将使用最初传递给消息的参数(而不是你提供给这个函数的参数)。

// 申明方式
BEGIN_MESSAGE_MAP(COfflineSCDlg, CDialog)
    AFX_MSG_MAP(COfflineSCDlg)
    ON_WM_GETMINMAXINFO()   //申明方式
    AFX_MSG_MAP
END_MESSAGE_MAP()

// 重载说明
afx_msg void OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI );

// 实现:OnGetMinMaxInfo
// 限制窗口的大小
void CAAAAADLG::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
    lpMMI->ptMinTrackSize.x=400; //限定最小宽度
    lpMMI->ptMinTrackSize.y=400; //限定最小高度
    // lpMMI->ptMaxTrackSize.x=100; //限定最大宽度
    // lpMMI->ptMaxTrackSize.y=100; //限定最大高度
    CDialog::OnGetMinMaxInfo(lpMMI);  
}

(三)方法三
由于一般窗口大小的改变,都是用户拖动窗口边框而造成的。所以,我们可以截获主窗口消息WM_NCHITTEST在其响应函数中判断CWnd::OnNcHitTest()的返回值是否为HTRIGHT,HTLEFT,HTTOP,HTBOTTOM四个值之一,如果是,说明用户此时已点击了四个边框之一,此时我们应该返回HTCLIENT.那么,鼠标的形状就不会变成水平或垂直的双向箭头,用户就不可能依靠拖动边框来改变窗口大小了。

另外,还应补上一个小漏洞,就是还要把系统菜单中的SC_SIZE去掉。


关于窗口变化的几个消息处理函数
3个消息分别是:WM_SIZE、WM_SIZING、WM_GETMINMAXINFO;

分别对应相应的处理函数:OnSize、OnSizing、OnGetMinMaxInfo;

当窗口大小发生变化时,响应的顺序依次是:WM_GETMINMAXINFO–>WM_SIZING–>WM_SIZE。

1、OnGetMinMaxInfo
这个函数在窗口初始化的时候会被调用一次,当窗口大小发生改变的时候也会被调用。利用这个函数,可以比较方便的实现窗口最大最小尺寸的控制。
参数lpMMI是一个结构体指针,其中包含了有关窗口的最大化大小和位置以及最小、最大跟踪大小的信息。
使用这个函数控制窗口最小尺寸的示例的代码如下:

void CXXXDlg::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)   
{  
    lpMMI->ptMinTrackSize.x = 500;   //x宽度  
    lpMMI->ptMinTrackSize.y = 100;   //y高度  

    CDialog::OnGetMinMaxInfo(lpMMI);  
}  

以上代码可以使得窗口大小变化时,最小宽度为500px,最小高度为100px。

2、OnSizing
这个函数在窗口大小发生变化时被调用。在这个函数里,也可以控制窗口的最大最小尺寸,但是没有OnGetMinMaxInfo方便。

void CXXXDlg::OnSizing(UINT fwSide, LPRECT pRect)   
{  
    if ((pRect->right - pRect->left) < 500)  
    {  
        //return ;  //直接return是无效的,窗口大小还是会改变  
        pRect->right = pRect->left + 500;  
    }  
    CDialog::OnSizing(fwSide, pRect);  
}  

用上面的方法,如果是从右边改变大小,可以达到想要的效果,但是从左边改变大小,虽然大小可以控制在最小500,但是当达到最小宽度后,再缩小,会发现整个窗口往右移动了,原因是代码中的pRect->right = pRect->left + 500;这句是针对left来改变right的,所以left移动了,right也移动了,看上去就像是这个窗口右移了。所以针对这种情况又要另外做相应的处理。

3、OnSize
这个函数会在窗口大小改变结束后被调用,通常会在这个函数里重新摆放各个控件的位置及大小。用这个函数暂时没有找到什么办法可以控制窗口的最大最小大小。


SendMessage
SendMessage函數是阻塞的。PostMessage函數是非阻塞的。
SendMessage:
函数功能:该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回。

函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);

参数:

hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。

Msg:指定被发送的消息。

wParam:指定附加的消息指定信息。

IParam:指定附加的消息指定信息。

返回值:返回值指定消息处理的结果,依赖于所发送的消息。

备注:需要用HWND_BROADCAST通信的应用程序应当使用函数RegisterWindowMessage来为应用程序间的通信取得一个唯一的消息。
参考:http://blog.csdn.net/tangaowen/article/details/5770816

在WM_LBUTTONDOWN里面处理
SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, > MAKELPARAM(point.x, point.y));
或者
响应NCHITTEST消息,命中测试。

将会导致WM_LBUTTONDOWN响应阻塞,也就是按下鼠标,不会收到WM_LBUTTONDOWN消息,而要等到鼠标放开,才有WM_LBUTTONDOWN和WM_LBUTTONUP进行响应。

到这里很清楚了,就是由于我发送了SendMessage NCLBUTTONDOWN消息,SendMessage消息不能返回,导致WM_LBUTTTONDOWN响应不能返回,当我鼠标松开弹起的时候,系统接管结束,SendMessage返回,我的WM_LBUTTONDOWN返回,表面上看是响应延时。实际是SendMessage阻塞了。

有人提出,我处理拖动应该采用WM_NCHITTEST,经过测试,发现一旦采用对WM_NCHITTEST进行处理拖动,窗口将无法响应任何鼠标点击消息。应该是所有鼠标消息都被系统的拖动接管了。所有这个方法是不行的。

还有人提出,我应该将SendMessage改为PostMessage消息,测试结果就是,能够马上响应WM_LBUTTONDOWN,但是却基本不能响应WM_LBUTTONUP,原因猜测也是在执行拖动的时候,不能响应消息,消息被系统接管。

展开阅读全文

没有更多推荐了,返回首页