Pages

Tuesday, November 10, 2009

Window with a predefined client rectangle

The task was to make a window with an image in the background and do not stretch the image.

The first application version I made just loaded the image into the memory, detected its size and created a popup window (WS_POPUP style) exactly of this size.

The QA said that it will be good, if the window will be moveable and sizeable. Ok. I added a caption and a frame. Now the image in the background is streched.

The first solution I found in Google - detect Window rectangle, then the client rectangle and the difference should be taken into a account when I set a rectangle for MoveWindow function:
HDC hDC = ::GetDC(NULL);
const int w = GetDeviceCaps(hDC, HORZRES);
const int h = GetDeviceCaps(hDC, VERTRES);
::ReleaseDC(NULL, hDC);

RECT rcClient, rcWindow;
GetClientRect(&rcClient);
GetWindowRect(&rcWindow);
m_Diff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
m_Diff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
cx += m_Diff.x;
cy += m_Diff.y;

RECT rect;
rect.left = (w >> 1) - (cx >> 1);
rect.top = (h >> 1) - (cy >> 1);
rect.right = rect.left + cx;
rect.bottom = rect.top + cy;

MoveWindow(&rect);
Two variables cx and cy are the predefined size of the background image.

This code works. But something's wrong here. Let's use Google again. :)

Of course, there is a Win32 API function that does the job: AdjustWindowRectEx.

And here is the code:
BOOL Center(int cx, int cy, RECT& rect)
{
HDC hDC = ::GetDC(NULL);
const int w = GetDeviceCaps(hDC, HORZRES);
const int h = GetDeviceCaps(hDC, VERTRES);
::ReleaseDC(NULL, hDC);

rect.left = (w >> 1) - (cx >> 1);
rect.top = (h >> 1) - (cy >> 1);
rect.right = rect.left + cx;
rect.bottom = rect.top + cy;

AdjustWindowRectEx(&rect, s_nWndStyle, FALSE, s_nWndStyleEx);
return TRUE;
}
This function above calculates the window rectangle for the predefined client area in the center of the desktop.
The function exists even for Windows Mobile and Windows CE: AdjustWindowRectEx. This MSDN article contains an example.

Now if you need to control the size of your window (do not allow to be smaller or bigger than the predefined size), you need to handle WM_GETMINMAXINFO message. For example, in the ATL-based application it will look as the following:
LRESULT OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LPMINMAXINFO pInfo = (LPMINMAXINFO)lParam;
if (pInfo != NULL)
{
pInfo->ptMinTrackSize = CPainter::GetMinSize();
}
return 0;
}
ptMinTrackSize in the MINMAXINFO structure is just a point, for example, set it as { 200, 200 } and the window cannot be smaller then 200x200.

More information about this WM_GETMINMAXINFO you can find on The Old New Thing.

No comments:

Post a Comment