Resizing Controls in MFC - c++

I am writing a program which has two panes (via CSplitter), however I am having problems figuring out out to resize the controls in each frame. For simplicity, can someone tell me how I would do it for a basic frame with a single CEdit control?
I'm fairly sure it is to do with the CEdit::OnSize() function... But I'm not really getting anywhere...
Thanks! :)

A window receives WM_SIZE message (which is processed by OnSize handler in MFC) immediately after it was resized, so CEdit::OnSize is not what you are looking for.
You should add OnSize handler in your frame class and inside this handler as Rob pointed out you'll get width and height of the client area of your frame, then you should add the code which adjusts size and position of your control.
Something like this
void MyFrame::OnSize(UINT nType, int w, int h)
{
// w and h parameters are new width and height of your frame
// suppose you have member variable CEdit myEdit which you need to resize/move
myEdit.MoveWindow(w/5, h/5, w/2, h/2);
}

When your frame receives an OnSize message it will give you the new width and height - you can simply call the CEdit SetWindowPos method passing it these values.
Assume CMyPane is your splitter pane and it contains a CEdit you created in OnCreate called m_wndEdit:
void CMyPane::OnSize(UINT nType, int cx, int cy)
{
m_wndEdit.SetWindowPos(NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}

GetDlgItem(IDC_your_slidebar)->SetWindowPos(...) // actually you can move ,resize...etc

SetWindowPos is a little heavy duty for this purpose. MoveWindow has just what is needed.

Others have pointed out that WM_SIZE is the message you should handle and resize the child controls at that point. WM_SIZE is sent after the resize has finished.
You might also want to handle the WM_SIZING message which gets sent while the resize is in progress. This will let you actively resize the child windows while the user is still dragging the mouse. Its not strictly necessary to handle WM_SIZING but it can provide a better user experience.

I use CResize class from CodeGuru to resize all controls automatically. You tell how you want each control to be resized and it does the job for you.
The resize paradigm is to specify how much each side of a control will move when the dialog is resized.
SetResize(IDC_EDIT1, 0, 0, 0.5, 1);
SetResize(IDC_EDIT2, 0.5, 0, 1, 1);
Very handy when you have a large number of dialog controls.
Source code

When it comes to the window size changes, there are three window messages you may be interested in: ON_WM_SIZE(), ON_WM_SIZING(), and ON_WM_GETMINMAXINFO().
As the official docs says:
ON_WM_SIZE whose message handler is ::OnSize() is triggered after the size of the CWnd has changed;
ON_WM_SIZING whose message handler is ::OnSizing() is triggered when the size of the client area of the clipbord-viewer window has changed;
ON_WM_GETMINMAXINFO whose message handler is ::OnGetMinMaxInfo() is triggered whenever the window needs to know the maximized position or dimensions , or the minimum or maximum tracking size.
If you want to restrict the size of the cwnd to some range, you may refer to message ON_WM_GETMINMAXINFO; and if you want to retrieve the size changes in real time, you may refer to the other two messages.

It is better to use the Dynamic Layout capabilities of each control at the Property section.
Let's say you want to have a specific control, like a heading, always at the center of the view/dialog, then you just choose the properties of Dynamic Layout of the control, Moving Type as Horizontal and Moving X as 50 but you keep sizing to None. This way, when you resize the view, the header remains always at the center. You have to keep in mind that the minimum of the resizing/moving is the size/position of the control within the dialog/view, when you designed it at the Resource View.
This way, you save the burden of geometry and the transformations.

Related

how to resize objects with resizing form using mouse [duplicate]

I am writing a program which has two panes (via CSplitter), however I am having problems figuring out out to resize the controls in each frame. For simplicity, can someone tell me how I would do it for a basic frame with a single CEdit control?
I'm fairly sure it is to do with the CEdit::OnSize() function... But I'm not really getting anywhere...
Thanks! :)
A window receives WM_SIZE message (which is processed by OnSize handler in MFC) immediately after it was resized, so CEdit::OnSize is not what you are looking for.
You should add OnSize handler in your frame class and inside this handler as Rob pointed out you'll get width and height of the client area of your frame, then you should add the code which adjusts size and position of your control.
Something like this
void MyFrame::OnSize(UINT nType, int w, int h)
{
// w and h parameters are new width and height of your frame
// suppose you have member variable CEdit myEdit which you need to resize/move
myEdit.MoveWindow(w/5, h/5, w/2, h/2);
}
When your frame receives an OnSize message it will give you the new width and height - you can simply call the CEdit SetWindowPos method passing it these values.
Assume CMyPane is your splitter pane and it contains a CEdit you created in OnCreate called m_wndEdit:
void CMyPane::OnSize(UINT nType, int cx, int cy)
{
m_wndEdit.SetWindowPos(NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
GetDlgItem(IDC_your_slidebar)->SetWindowPos(...) // actually you can move ,resize...etc
SetWindowPos is a little heavy duty for this purpose. MoveWindow has just what is needed.
Others have pointed out that WM_SIZE is the message you should handle and resize the child controls at that point. WM_SIZE is sent after the resize has finished.
You might also want to handle the WM_SIZING message which gets sent while the resize is in progress. This will let you actively resize the child windows while the user is still dragging the mouse. Its not strictly necessary to handle WM_SIZING but it can provide a better user experience.
I use CResize class from CodeGuru to resize all controls automatically. You tell how you want each control to be resized and it does the job for you.
The resize paradigm is to specify how much each side of a control will move when the dialog is resized.
SetResize(IDC_EDIT1, 0, 0, 0.5, 1);
SetResize(IDC_EDIT2, 0.5, 0, 1, 1);
Very handy when you have a large number of dialog controls.
Source code
When it comes to the window size changes, there are three window messages you may be interested in: ON_WM_SIZE(), ON_WM_SIZING(), and ON_WM_GETMINMAXINFO().
As the official docs says:
ON_WM_SIZE whose message handler is ::OnSize() is triggered after the size of the CWnd has changed;
ON_WM_SIZING whose message handler is ::OnSizing() is triggered when the size of the client area of the clipbord-viewer window has changed;
ON_WM_GETMINMAXINFO whose message handler is ::OnGetMinMaxInfo() is triggered whenever the window needs to know the maximized position or dimensions , or the minimum or maximum tracking size.
If you want to restrict the size of the cwnd to some range, you may refer to message ON_WM_GETMINMAXINFO; and if you want to retrieve the size changes in real time, you may refer to the other two messages.
It is better to use the Dynamic Layout capabilities of each control at the Property section.
Let's say you want to have a specific control, like a heading, always at the center of the view/dialog, then you just choose the properties of Dynamic Layout of the control, Moving Type as Horizontal and Moving X as 50 but you keep sizing to None. This way, when you resize the view, the header remains always at the center. You have to keep in mind that the minimum of the resizing/moving is the size/position of the control within the dialog/view, when you designed it at the Resource View.
This way, you save the burden of geometry and the transformations.

Child Windows that does not occupy client area

In the Win32 API (pure win32), The Menu bar does not occupy any area from the client area of the window. Which means the origin coordinates of the client area is right under the menu bar to the left.
When we create child window controls using CreateWindow (or any other method), that window takes some area of the client-area.
eg:- Creating a button which is at (xPos = 0, yPos = 0) and (width=width_of_client_area, height=20).
After creating the button if you'll use a GDI function like this, it'll be drew below the button:
Rectangle(hdc, 0,0, 200, 200);
But when creating a menu bar, it doesn't occupy client area. (GDI will not be drew under menu).
FINAL QUESTION:
How can i create a control on my parent window like the menu bar ?
The menu is rendered in the non-client area of the window, which is driven by a completely different set of window messages. Keep in mind that you don't actually create child windows for these types of controls. You will need to provide all the rendering and event handling for the customization you want to add. This means that if you want to add a button or checkbox you will need to implement it yourself. You can get your started with a handful of steps but there may be other things that need to be done depending on your requirements.
The first step is to process the WM_NCCALCSIZE message. This will allow you to adjust the size of the non-client area so that you have more space to draw the custom control. Basically you will pass this message on to the default window proc then modify the location and dimensions (just drop the top down X pixels) before returning.
The next step is to handle WM_NCPAINT message. Pass the message on to the default window proc then draw your custom visuals in the area you have reserved. You will be working with a window DC so you can draw to the entire window. It's important to keep this in mind so you don't destroy anything in the client area.
The last item that needs to be done is to handle mouse messages like WM_NCMOUSEMOVE. If the mouse event occurs outside the area where your control is located pass the message to the default window proc, otherwise handle the message yourself and skip the default proc. you will need to manage some state data for keeping track of when a button is down or not so as not to cause conflicts with the default window proc.
Keep in mind that when you want to draw directly to the non-client area you need to call GetWindowDC() instead of GetDC() to acquire a device context.
[Note: A good Google dance will be something like "WinAPI non-client area"]

Constraining window position to desktop working area

I want to allow a user to drag my Win32 window around only inside the working area of the desktop. In other words, they shouldn't be able to have any part of the window extend outside the monitor(s) nor should the window overlap the taskbar.
I'd like to do it in a way that does cause any stuttering. Handling WM_MOVE messages and calling MoveWindow() to reposition the window if it goes off works, but I don't like the flickering effect that's caused by MoveWindow().
I also tried handling WM_MOVING which prevents the need to call MoveWindow() by altering the destination rectangle before the move actually happens. This resolves the flickering problem, but another issue I run into is that the cursor some times gets aways from the window when a drag occurs allowing the user to drag the window around while the cursor is not even inside the window.
How do I constrain my window without running into these issues?
Windows are, ultimately, positioned via the SetWindowPos API.
SetWindowPos starts by validating its parameters by sending the window being sized or moved a WM_WINDOWPOSCHANGING message, and then a WM_WINDOWPOSCHANGED message notifying the window proc of the changed size and/or position.
DefWindowProc handling of these messages is to, in turn, send WM_GETMINMAXINFO and then WM_SIZE or WM_MOVE messages.
Anyway, handle WM_WINDOWPOSCHANGING to filter both user, and code, based attempts to position a window out of bounds.
Keep in mind that users with multi-monitor setups may have a desktop that extends into negative x- and y-coordinates, or that is not rectangular. Also, some users use alternative window managers such as LiteStep, which implement virtual desktops by moving them off-screen; if you try to fight this, your application will break for these users.
You can do this by handling the WM_MOVING message and changing the RECT pointed to by the lParam.
lParam: Pointer to a RECT structure with the current position of the window, in screen coordinates. To change the position of the drag rectangle, an application must change the members of this structure.
you may also want to handle WM_ENTERSIZEMOVE to know when the window is beginning to move, and WM_EXITSIZEMOVE
WM_GETMINMAXINFO is what you seem to be looking for.

How to force a window to maintain a certain width/height ratio when resized

I want my window to always maintain a certain ratio of let's say 1.33333333. So, if the window is width = 800, height = 600 and the user changes the width to 600, I want to change the height to 450 automatically.
I'm already intercepting WM_SIZE but I don't know if it's enough; also I don't know how to change the width or height to maintain my ratio.
WM_SIZING is sent to the window while the user is resizing the window.
Rather handle WM_WINDOWPOSCHANGING - this is sent by the internal SetWindowPos function when code (or the user) changes the window size and will ensure that even tile & cascade operations obey your sizing policy.
See WM_SIZING: http://msdn.microsoft.com/en-us/library/ms632647.aspx
Processing this message allows you to change resulting window size.
Try the Resize event. There is an example of how to maintain a desired aspect ration in the link.

Can I change the thickness of the border of a window with MFC?

Normally, the thickness of a window is 4 pixels, which can be retrieved by GetSystemMetrics method. Can I change its value, for example 2 pixels?
Thank you very much!
Simple answer: No. Not for a specific window.
Complicated answer: The border is drawn as part of the "non-client" region of the window. This is all handled (under the hood) by the default processing (i.e. DefWindowProc), along with the caption, minimize, maximize buttons, etc. You can override this by handling the WM_NCPAINT message. You'll then be responsible for drawing the entire non-client area of your window. You'll also want to handle the WM_NCCALCSIZE message, so that Windows knows how much of the remaining space to give to your client area.
Alternatively, you can set the border style of your window to none. This will allow Windows to draw the caption for you, although it'll probably look slightly different. Unfortunately, by doing this, you lose the drag-to-resize functionality. For that, you'll need to handle the WM_NCHITTEST message.