背景
最近负责维护一个使用MFC实现的系统,客户想要添加一个全屏显示的功能,感觉在MFC使用独占显卡的方式貌似不会(这个需要学习啊),所以就像通过伪全屏的功能实现以下。
1. 在MainFrame类中加入了几个成员变量和响应的函数用来控制全屏操作
具体变量如下:
/* 该变量用于保存原有的窗口放置 */ WINDOWPLACEMENT m_tOldWndPlacement; /* 该变量用于表示当前是否是全屏显示状态 */ BOOL m_bFullScreen; /* 该变量用于表示全屏显示情况下所占用的矩形 */ CRect m_oFullScreenRect;
具体函数如下:
public: /* 该函数用于将当前框架进行非独占全屏显示 */ void FullScreen(); /* 该函数用于结束当前的非独占全屏显示模式 */ void EndFullScreen(); //注:将全屏功能函数分开写而非合成一个函数的原因是为了更多的调用组合 /* 该函数用来获得当前是否在全屏显示状态 */ BOOL IsFullScreen(){return m_bFullScreen;} public: /* 该消息响应更该窗口大小时,返回给系统最小最大的阈值 */ afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
函数具体实现如下:
/* @描述: 该函数用于将当前框架进行非独占全屏显示 @参数: void @返回值: void */ void MainFrame::FullScreen() { WINDOWPLACEMENT wpNew; if(!m_bFullScreen) { //全屏幕显示模式 //隐藏工具条和状态条,以下两个工具条为维护系统中的,需要换成自己要隐藏的工具条 m_wndStatusBar.ShowWindow(SW_HIDE); m_wndRibbonBar.ShowWindow(SW_HIDE); //保存正常视图时的窗口位置信息以便恢复原来状态 GetWindowPlacement (&m_tOldWndPlacement); m_tOldWndPlacement.length =sizeof(m_tOldWndPlacement); //调整RECT为新的窗口尺寸 //::SystemParametersInfo(SPI_GETWORKAREA,0,&m_oFullScreenRect,0); //用这个函数老是有问题,正在查为什么 ::GetWindowRect(::GetDesktopWindow(),&m_oFullScreenRect); ::AdjustWindowRectEx(&m_oFullScreenRect,GetStyle(),TRUE,GetExStyle()); wpNew = m_tOldWndPlacement; wpNew.showCmd = SW_SHOWNORMAL; wpNew.rcNormalPosition = m_oFullScreenRect; m_bFullScreen=TRUE; } SetWindowPlacement(&wpNew);
}
/* @描述: 该函数用于结束当前的非独占全屏显示模式 @参数: void @返回值: void */ void MainFrame::EndFullScreen() { if (m_tOldWndPlacement.length > 0) { //正常显示模式 m_bFullScreen=FALSE; //恢复工具条和状态条,以下两个工具条为维护系统中的,需要换成自己要恢复的工具条 m_wndStatusBar.ShowWindow(SW_NORMAL); m_wndRibbonBar.ShowWindow(SW_NORMAL); SetWindowPlacement(&m_tOldWndPlacement); } } /* @描述: 该消息响应更该窗口大小时,返回给系统最小最大的阈值 这里重写的目的是在全屏状态下,设置窗口的最大最小阈值为全屏大小 @参数: MINMAXINFO * lpMMI - 系统传入的代表最大值最小值的结构体,结构体意义可参见MSDN @返回值: void */ void MainFrame::OnGetMinMaxInfo(MINMAXINFO* lpMMI) { if(m_bFullScreen) { /* 窗口最大化时的尺寸 */ lpMMI->ptMaxSize.y=m_oFullScreenRect.Height(); lpMMI->ptMaxSize.x=m_oFullScreenRect.Width(); /* 窗口追踪(拖动)改变时最大的尺寸 */ lpMMI->ptMaxTrackSize.y = lpMMI-> ptMaxSize.y; lpMMI->ptMaxTrackSize.x = lpMMI-> ptMaxSize.x; } CBCGPMDIFrameWnd::OnGetMinMaxInfo(lpMMI); }
2. 添加相应的调用方式
为了简单,我采用的就是快捷键的调用方式,这里就不细说了,也有不少文章采用了再绘制一个工具条的方式,我觉得再加上这样的效果会更好,如果想创建的话可以去搜索一下相关文章,很容易。
3. 注意细节
相关变量记得在构造函数中初始化。
问题
接下来就碰到比较郁闷的问题了,当任务栏在屏幕下方时,程序可以正确全屏(也是后来试出来的,常年任务栏在左侧),但任务栏在左侧、上侧时,都会预留出任务栏的空间,这可就让人郁闷了,但是修改还是要做,就开始想办法,从现象上来看,就是显示位置的矩阵设置的不正确,就开始进行调试,发现::GetWindowRect(::GetDesktopWindow(),&m_oFullScreenRect)这套操作返回的矩形都是以(0,0)为参考的,经过::AdjustWindowRectEx(&m_oFullScreenRect,GetStyle(),TRUE,GetExStyle())调整之后,会有少许的变化,从函数的名称和调用参数来看,这个函数只会负责根据风格调整一下矩形,可能和任务栏根本挨不着边际,所以只能继续想办法。
就查到了SHAppBarMessage这个函数,发现其功能甚是强大,具体方式可以参加MSDN中对该函数和相关参量的描述:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb762108(v=vs.85).aspx
通过功能描述就感觉可以通过简单的判断实现区域的重新计算,就开始在FullScreen()函数中修改代码(避免周末加班。。。)
/* 根据任务栏位置开始调整 */ APPBARDATA l_tBarData; l_tBarData.hWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL); if (l_tBarData.hWnd != NULL) { UINT l_uiBarState = ::SHAppBarMessage(ABM_GETSTATE,&l_tBarData); if (l_uiBarState != ABS_AUTOHIDE) { ::SHAppBarMessage(ABM_GETTASKBARPOS,&l_tBarData); UINT l_uiBarHeight = l_tBarData.rc.top - l_tBarData.rc.bottom; UINT l_uiBarWidth = l_tBarData.rc.right - l_tBarData.rc.left; ::SHAppBarMessage(ABM_QUERYPOS,&l_tBarData); switch(l_tBarData.uEdge) { case ABE_BOTTOM: break; case ABE_LEFT: m_oFullScreenRect.left -= l_uiBarWidth; m_oFullScreenRect.right -= l_uiBarWidth; break; case ABE_RIGHT: m_oFullScreenRect.left += l_uiBarWidth; m_oFullScreenRect.right += l_uiBarWidth; break; case ABE_TOP: m_oFullScreenRect.top -= l_uiBarHeight; m_oFullScreenRect.bottom -= l_uiBarHeight; break; } } }
运行,无效。调试,莫名其妙。这种结果果然很让人无语,就开始继续看文档说明,后来才发现cbSize这个关键字,一般想要有效的话,这个关键字一定要有值。
所以在成功获得窗口句柄后,需要加入这样一条代码
l_tBarData.cbSize = sizeof(l_tBarData);
再运行,就可以顺利通过了,发现这些基本的API编程基础还真是很重要,不知道有没有人和我一样因为少了这句话而郁闷半天。
总结
虽然解决的过程还是比较曲折的,但是提交了一个差不多的效果,但其实程序中还是出现了挺多的问题,有一部分是由于采用了比较陌生的界面库,在标题栏隐藏方面还是有些问题,除此以外应该就是伪全屏模式的缺点了,在尺寸修改,定位方面都需要有约束,所以接下来就想考虑一下独占的全屏显示,不过查了一下还没看到相关的资料,都说MFC实现这种独占方式并不容易,但是还是想试一试。