做一个单文档的程序,需要根据显示器不同,自适应显示器大小。
首先是,需要自动修改程序框体大小。
以前都是在基于对话框的应用程序中这么弄。只要在对话框的初始化函数OnInitDialog()中调用MoveWindow就可以解决了。
类似下面这样的做法
------------------------------------------------------------------------------
CRect rect;
rect.left=0;
rect.top=0;
rect.right=200;
rect.bottom=100;
MoveWindow(&rect);
------------------------------------------------------------------------------
显然单文档是不行的。
对于单文档(SDI)与多文档(MDI)的应用程序,就需要重写窗口的 PreCreateWindow函数了。
VS2010的工程引导程序生成的程序就已经有了相关的函数就去修改下就可以了。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
| WS_THICKFRAME |WS_MINIMIZEBOX | WS_SYSMENU;
cs.x = 0;
cs.y = 0;
cs.cx = 1280;
cs.cy = 1024;
return TRUE;
}
有的时候,会发现这样还是不行的,就比如我的这个。是因为在其他地方有再次修改框体大小的操作。
void CXXXView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
}
void CScrollView::ResizeParentToFit(BOOL bShrinkOnly)
{
// adjust parent rect so client rect is appropriate size
ASSERT(m_nMapMode != MM_NONE); // mapping mode must be known
// determine current size of the client area as if no scrollbars present
CRect rectClient;
GetWindowRect(rectClient);
CRect rect = rectClient;
CalcWindowRect(rect);
rectClient.left += rectClient.left - rect.left;
rectClient.top += rectClient.top - rect.top;
rectClient.right -= rect.right - rectClient.right;
rectClient.bottom -= rect.bottom - rectClient.bottom;
rectClient.OffsetRect(-rectClient.left, -rectClient.top);
ASSERT(rectClient.left == 0 && rectClient.top == 0);
// determine desired size of the view
CRect rectView(0, 0, m_totalDev.cx, m_totalDev.cy);
if (bShrinkOnly)
{
if (rectClient.right <= m_totalDev.cx)
rectView.right = rectClient.right;
if (rectClient.bottom <= m_totalDev.cy)
rectView.bottom = rectClient.bottom;
}
CalcWindowRect(rectView, CWnd::adjustOutside);
rectView.OffsetRect(-rectView.left, -rectView.top);
ASSERT(rectView.left == 0 && rectView.top == 0);
if (bShrinkOnly)
{
if (rectClient.right <= m_totalDev.cx)
rectView.right = rectClient.right;
if (rectClient.bottom <= m_totalDev.cy)
rectView.bottom = rectClient.bottom;
}
// dermine and set size of frame based on desired size of view
CRect rectFrame;
CFrameWnd* pFrame = EnsureParentFrame();
pFrame->GetWindowRect(rectFrame);
CSize size = rectFrame.Size();
size.cx += rectView.right - rectClient.right;
size.cy += rectView.bottom - rectClient.bottom;
pFrame->SetWindowPos(NULL, 0, 0, size.cx, size.cy,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
可以很清楚的看到这里有个修改。在确认这个初始化函数的不会对后面造成影响情况下,可以注释掉。
然后,就是获取设备相关的大小了。
很自然的就会想到API函数GetSystemMetrics,相关参数如下
在实际使用中,发现不论是哪套参数都不是太理想。
SM_CXSCREEN,SM_CYSCREEN获取的是真正屏幕的大小:屏幕分辨率
这个时候工具栏会遮挡部分程序部分。
SM_CXFULLSCREEN,SM_CYFULLSCREEN获取的是显示屏幕的大小,但不包括工具栏等区域
但是会有一小截的空余部分。
这个就要用MSDN关于SM_CXFULLSCREEN说明中的 SystemParametersInfo函数了。
函数原型
BOOL SystemParametersInfoA(
UINT uiAction,
UINT uiParam,
PVOID pvParam,
UINT fWinIni
);
使用方法无论是MSDN还是网上都有足够多的资料示例了。直接给我的用例吧。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
| WS_THICKFRAME |WS_MINIMIZEBOX | WS_SYSMENU;
RECT rect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
int x1 = GetSystemMetrics(SM_CXFULLSCREEN);
int y1 = GetSystemMetrics(SM_CYFULLSCREEN);
int x2 = GetSystemMetrics(SM_CXSCREEN);
int y2 = GetSystemMetrics(SM_CYSCREEN);
cs.x = 0;
cs.y = 0;
cs.cx = rect.right;
cs.cy = rect.bottom;
return TRUE;
}
这两步之后,就基本达到我所预想的目的了。