What parameters are valid to be together to create an HWND? - model-view-controller

I'm trying to create a window using CWnd::CreateEx(). Unfortunately, I can't seem to get the parameters correct to get it to generate a HWND (function always returns FALSE).
class COverlay : public CWnd
{
public:
COverlay();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(COverlay, CWnd)
END_MESSAGE_MAP()
COverlay::COverlay()
{
}
class CMyView : CView
{
public:
CMyView() {}
int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()
int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_overlay.CreateEx(WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT
, _T("Static"), _T(""), WS_VISIBLE | WS_CHILD, CRect(), this, -1);
return 0;
}
I'm trying to create a click through window that overlays the CMyView. In this way I can paint over the view via the overlay and not get flickering as the OS should handle the double buffering and I will only have to deal with the view implementation in a minimal fashion. I will also have an ON_WM_SIZE hander to deal with resizing the overlay to the extents of the view.
What am I doing wrong?
Is there an easier way to do what I am trying to do?

WS_POPUP flag is required to create window with transparency level:
static CString className = AfxRegisterWndClass(0);
DWORD style = WS_CAPTION | WS_SYSMENU | WS_POPUP;
DWORD styleEx = WS_EX_LAYERED;// | WS_EX_TRANSPARENT;
if (!m_overlay.CreateEx(styleEx, className, 0, style, CRect(), this, 0))
{
//error...
return;
}
m_overlay.SetLayeredWindowAttributes(0, 255 * 50 / 100, LWA_ALPHA);
m_overlay.SetWindowPos(0, 0, 0, 400, 300, SWP_SHOWWINDOW | SWP_NOACTIVATE);
Or to change the transparency color:
COLORREF transparentColor = RGB(255, 0, 0);
...
m_overlay.SetLayeredWindowAttributes(transparentColor, 0, LWA_COLORKEY);
void COverlay::OnPaint()
{
CPaintDC dc(this);
CRect rc;
GetClientRect(&rc);
dc.FillSolidRect(rc, transparentColor);
//opaque drawings...
dc.TextOut(0, 0, L"Test...");
}
Note, WS_EX_TRANSPARENT is commented out because that only affects mouse clicks, otherwise it doesn't change the appearance. You can add that flag back in.
This window is popup so it doesn't move when you move the frame window or child window. You would have to override frame and child frame window's OnWindowPosChanged to move it manually to sync with View window.

Related

How to paint title bar window only?

As mention from microsft doc. WM_NCPAINT is use to paint non-client area. it means like title bar. https://learn.microsoft.com/en-us/windows/win32/gdi/wm-ncpaint. But i get unexpected result. it paint client area too. and the weird one. title bar is gone. when launch. after ALT+TAB. the title bar appears with windows 7 style in windows 10.
class CMainFrame::CFrameWnd
{
public:
CMainFrame()
{
Create(
NULL,
"Hello World!",
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
CRect(CPoint(100, 100), CSize(640, 360));
}
protected:
afx_msg void OnNcPaint();
DECLARE_MESSAGE_MAP()
}
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_NCPAINT()
END_MESSAGE_MAP()
void CMainFrame::OnNcPaint()
{
PAINTSTRUCT ps;
CBrush brush;
brush.CreateSolidBrush(RGB(0, 0, 255));
CDC *pDC = BeginPaint(&ps);
pDC->FillRect(&ps.rcPaint, &brush);
EndPaint(&ps);
}
class CApplication : public CWinApp {
BOOL InitInstance() {
CMainFrame* mainWnd = new CMainFrame();
m_pMainWnd = mainWnd;
mainWnd->ShowWindow(SW_NORMAL);
mainWnd->UpdateWindow();
return TRUE;
}
};
In InitInstance, you must call the base class CWinApp::InitInstance() in the first line.
Create your main frame window using
CreateEx(0, AfxRegisterWndClass(0), "Hello World!",
WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 640, 360, NULL, 0);
Add PreCreateWindow to control the edges and other properties
int CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
auto res = CFrameWnd::PreCreateWindow(cs);
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
return res;
}
Use the appropriate paint classes, CPaintDC dc(this), CWindowDC, for overriding OnPaint or OnNcPaint (avoid overriding OnNcPaint), use CClientDC dc(this) for other client area paints. Avoid calling WinAPI functions directly, for example avoid calling BeginPaint. You can also use Visual Studio to create a sample dialog based application, it will be easier to get started with that.

AnimateWindow() incorrectly draws background on high dpi (Win10)

I am creating a simple Win32/MFC application with a main window, and a child-window that uses AnimateWindow() to show (slide up) and hide (slide down) the window. When running the application on 100% dpi scaling, everything behaves normally.
I have overridden WM_ERASEBKGND to render a random red color, to demonstrate the effect. As the window slides-down (hide) on every "step" of the animation the "update rectangle" of the background is repainted, exactly where the child-window disappeared, and the background is to become visible again.
However, when changing the dpi-scaling via Windows Settings (in this case to 125%), an incorrect area is redrawn. It appears as if the area passed into OnEraseBkgnd() is still using the 100% dpi-scaling size, but also the position is off: it appears as if the x/y position of the upper-left corner is passed in screen-space instead of client-space. So the redrawn area looks different, depending on where the on the screen the window is positioned.
The white area is where the child window was actually positioned, and where the redraw of the background should actually have happened.
I have confirmed this effect on Win10 (1803 and 1809) and on Win8.1
Is this a bug in the OS, or is there something I can do to avoid the problem - other than not using AnimateWindow()? ShowWindow() (with SW_SHOW or SW_HIDE) works just fine, btw.
Update: added the full source-code to reproduce the issue.
The problem occurs when using no dpi-aware manifest at all, but it also occurs when using <gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
class CDialogTestApp : public CWinApp
{
virtual BOOL InitInstance();
};
CDialogTestApp theApp;
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {}
};
class CDialogTestDlg : public CDialogEx
{
public:
CDialogTestDlg(CWnd* pParent = nullptr) : CDialogEx(IDD_DIALOGTEST_DIALOG, pParent) {}
protected:
virtual BOOL OnInitDialog();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
private:
CAboutDlg mDialog;
};
BEGIN_MESSAGE_MAP(CDialogTestDlg, CDialogEx)
ON_WM_ERASEBKGND()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
BOOL CDialogTestApp::InitInstance()
{
CWinApp::InitInstance();
CDialogTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
return FALSE;
}
BOOL CDialogTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
mDialog.Create(IDD_ABOUTBOX, this);
return TRUE;
}
BOOL CDialogTestDlg::OnEraseBkgnd(CDC* pDC)
{
COLORREF color = RGB(rand() & 255, 20, 40);
CRect rect;
GetClipBox(pDC->m_hDC, &rect); // retrieve the update-rectangle
CBrush brush(color);
FillRect(pDC->m_hDC, &rect, (HBRUSH)brush.m_hObject);
return TRUE;
}
void CDialogTestDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if (mDialog.IsWindowVisible())
{
mDialog.AnimateWindow(200, AW_HIDE | AW_SLIDE | AW_VER_POSITIVE);
}
else
{
mDialog.SetWindowPos(&CWnd::wndTop, 0, 50, 0, 0, SWP_NOSIZE);
mDialog.AnimateWindow(200, AW_ACTIVATE | AW_SLIDE | AW_VER_NEGATIVE);
}
CDialogEx::OnLButtonUp(nFlags, point);
}

Windows, restoring window bug

I'm playing around with Windows and D2D1
However when restoring my minimized borderless/menuless window
sometimes I get this very ugly bug
For a frame (or a few more) before the window gets drawn with D2D1
it will display this title bar with the name of the window.
This happens on about 5-10% of the restore operations.
The window class style is set to
CS_DBLCLKS|CS_OWNDC
but Ive also tried other styles.
The Window is created with CreateWindow and WS_POPUP|WS_SYSMENU as dwStyle
My rendering method is called on WM_PAINT but I also tried to move it so it
gets called every time but that does not help.
Any help is appreciated :)
#
I've found a workaround which I'm not completely fine with
Instead of calling ShowWindow(hWnd, SW_RESTORE)
I call
ShowWindow(hWnd, SW_HIDE);
ShowWindow(hWnd, SW_RESTORE);
ShowWindow(hWnd, SW_SHOW);
This however results in the taskbar icon being "renewed" which I dont want either.
Short simplified example code which features this problem (when minimizing/restoring)
#include <Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR cmd, int cmdShow)
{
WNDCLASSEX TestWC = { 0 };
TestWC.cbSize = sizeof(WNDCLASSEX);
TestWC.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(50, 50, 50));
TestWC.lpfnWndProc = DefWindowProc;
TestWC.lpszClassName = "Testklasse";
TestWC.style = CS_DBLCLKS | CS_OWNDC;
TestWC.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassEx(&TestWC);
HWND htest = CreateWindow("Testklasse", "Test", WS_POPUP | WS_SYSMENU | WS_VISIBLE, 200, 200, 400, 248, 0, 0, 0, 0);
MSG wMsg = { 0 };
bool shown = true;
while (wMsg.message != WM_QUIT)
{
if (PeekMessage(&wMsg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&wMsg);
DispatchMessage(&wMsg);
}
if (GetAsyncKeyState(VK_INSERT) & 0x8000 && shown)
{
shown = false;
ShowWindow(htest, SW_MINIMIZE);
continue;
}
if (GetAsyncKeyState(VK_DELETE) & 0x8000 && !shown)
{
shown = true;
ShowWindow(htest, SW_RESTORE);
continue;
}
if (GetAsyncKeyState(VK_END) & 0x8000)
PostQuitMessage(1);
Sleep(50);
}
return 1;
}
Found the solution myself:
Minimizing and restoring a borderless window
is undefined behaviour.
A workaround is to catch the WM_NCPAINT and draw the WHOLE
window yourself (even if its borderless it has a non-client area).

Detect Button Press in Tab Page

I am using the WinAPI to create a GUI utility. I have two Tabs and each tab has some buttons. I am creating the buttons with this function:
CreateWindowEx(NULL,"button", "Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
150, 175, 100, 25, tab_window_2, (HMENU) CLEAR_DATA, instance_handle, NULL);
In the Windows proc function, I don't know how to detect when the above button is pressed. I also tried handling CLEAR_DATA in the WM_COMMAND switch construct, as follows.
switch ( message ) {
case WM_COMMAND:{
switch(LOWORD(wparam)) {
case CLEAR_DATA : break
}
}
How can I detect and handle those buttons being pressed?
I am creating the tab_window_2 tab as follows :
class frame_window {
private:
LPCSTR window_class_name;
HINSTANCE instance_handle;
HCURSOR cursor_arrow;
HWND window_handle;
HWND tab_handle;
HWND tab_window_1;
HWND tab_window_2;
HWND current_tab_window;
RECT client_rectangle;
public:
frame_window(LPCSTR window_class_identity) : window_class_name(window_class_identity) {
INITCOMMONCONTROLSEX common_controls;
common_controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
common_controls.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&common_controls);
int screen_width = GetSystemMetrics(SM_CXFULLSCREEN);
int screen_height = GetSystemMetrics(SM_CYFULLSCREEN);
instance_handle = GetModuleHandle(NULL);
WNDCLASS window_class = { CS_OWNDC, main_window_proc, 0, 0,
instance_handle, NULL,
NULL, NULL, NULL,
window_class_name };
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create a standard frame window
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RegisterClass(&window_class);
window_handle = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
window_class_name,
"IR Remote and Barcode Demo",
WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
100, 100, screen_width-1600,
screen_height-490, NULL, NULL,
instance_handle, NULL);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Get the size of the client rectangle for the window we have just created
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RECT client_rect;
GetClientRect(window_handle, &client_rect);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create the tab control window.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tab_handle = CreateWindowEx(NULL, WC_TABCONTROL, NULL,
WS_CHILD | WS_VISIBLE,
10, 10, client_rect.right-client_rect.left-20,
client_rect.bottom-client_rect.top-10,
window_handle, NULL,
instance_handle, NULL);
// Create three tabs.
TCITEM tab_info;
memset(&tab_info, 0, sizeof(tab_info));
tab_info.mask = TCIF_TEXT;
tab_info.pszText = "tab#1";
tab_info.cchTextMax = 5;
SendMessage(tab_handle, TCM_INSERTITEM, 0, (LPARAM)&tab_info);
tab_info.pszText = "tab#2";
SendMessage(tab_handle, TCM_INSERTITEM, 1, (LPARAM)&tab_info);
RECT tab_rectangle;
GetClientRect(tab_handle, &tab_rectangle);
SendMessage(tab_handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&tab_rectangle);
// Create the tab view windows
tab_window_1 = CreateWindowEx(NULL, "STATIC", " ",
WS_CHILD | WS_VISIBLE|SS_OWNERDRAW,
tab_rectangle.left+10, tab_rectangle.top+10,
tab_rectangle.right-tab_rectangle.left,
tab_rectangle.bottom -tab_rectangle.top,
tab_handle, (HMENU)1,
instance_handle, NULL);
SetParent(tab_window_1, window_handle);
current_tab_window = tab_window_1;
tab_window_2 = CreateWindowEx(NULL, "STATIC", " ",
WS_CHILD|SS_OWNERDRAW,
tab_rectangle.left+10, tab_rectangle.top+10,
tab_rectangle.right-tab_rectangle.left,
tab_rectangle.bottom -tab_rectangle.top,
tab_handle, (HMENU)2,
instance_handle, NULL);
CreateWindowEx(NULL,"button", "Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
150, 175, 100, 25, tab_window_2, (HMENU) CLEAR_DATA, instance_handle, NULL);
SetParent(tab_window_2, window_handle);
SetCursor(LoadCursor(NULL, IDC_ARROW));
SetWindowLongPtr(window_handle, GWL_USERDATA, (LONG)this);
ShowWindow(window_handle, SW_SHOW);
UpdateWindow(window_handle);
}
~frame_window() {
UnregisterClass(window_class_name, instance_handle);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Windows main entry point
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE, LPWSTR, INT) {
frame_window main_window("my base window");
main_window.run();
return 0;
}
The WM_COMMAND for handling the events from a Push Button must be in the Window Procedure of it's parent. Your code show tab_window_2 as the parent for the CLEAR_DATA button.
Move the WM_COMMAND code in the right Window Proc (or Dialog Proc).
EDIT: I think you didn't understand how the Tab Control works.
If you want each Tab View to contain more than a dummy static control, you should use modeless and borderless DialogBox, children of the Tab Control, and show/hide them accordingly to the TCN_SELCHANGE notification.
If you don't realy need the Dialog Box functionalities, you could create regular child windows (with RegisterClass/CreateWindow) and then add your controls as child windows.
In either case, the show/hide code must react to TCN_SELCHANGE.
With regular child windows or with Dialog Box the net effect is the same: you will have a Window Procedure, or a Dialog Procadure, in which WM_COMMAND will deal with user actions.
Don't play with SetParent. But, if you absolutly want it, then use:
HWND hWndPushClear = CreateWindowEx(NULL,"button", "Clear", [...]
SetParent( hWndPushClear, window_handle );
You may have weird Paint/Focus problems, however.
EDIT: Unfortunately, it seems that there is no good Tab Control tutorial in line.
I recommend updating your actual source code. As a starting point, do the following:
Replace CreateWindowEx by CreateDialog for tab_window_1 end tab_window_2 (only one Dialog must be visible)
Suppress all SetParent calls
Your CreateWindowEx() and WM_COMMAND code are seems correct.
So,
Check return value of CreateWindowEx() whether is NULL. NULL means which is failed to create control.
Check HWND of parent is valid. In your code, it is tab_window_2.
Also, if you do not want to any WS_EX_XXX style, you can just use CreateWindow().
This code is just sample.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
switch(iMessage) {
case WM_CREATE:
CreateWindow("button","Clear",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20,20,100,25,hWnd,(HMENU)0,g_hInst,NULL);
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 0:
MessageBox(hWnd,"Clear Button Clicked","Button",MB_OK);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

How to create a resizable CDialog in MFC?

I have to create a dialog based application, instead of old CFormView type of design. But CDialog produces fixed-size dialogs. How can I create dialog based applications with resizable dialogs?
In the RC resource file if the dialog has this style similar to this it will be fixed size:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
If the dialog has this style it will be sizeable:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
With these sizable frame options the dialog will be re-sizeable but you will still need to do a lot of work handling the WM_SIZE message to manage the sizing an positioning of the controls within the dialog.
In addition to setting the style to WS_THICKFRAME, you'll probably also want to have a system to move and resize the controls in a dialog as the dialog is resized. For my own personal use I've created a base class to replace CDialog that has this capability. Derive from this class and in your InitDialog function call the AutoMove function for each child control to define how much it should move and how much it should resize relative to the parent dialog. The size of the dialog in the resource file is used as a minimum size.
BaseDialog.h:
#if !defined(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_)
#define AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <vector>
class CBaseDialog : public CDialog
{
// Construction
public:
CBaseDialog(UINT nIDTemplate, CWnd* pParent = NULL); // standard constructor
void AutoMove(int iID, double dXMovePct, double dYMovePct, double dXSizePct, double dYSizePct);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CBaseDialog)
protected:
//}}AFX_VIRTUAL
protected:
//{{AFX_MSG(CBaseDialog)
virtual BOOL OnInitDialog();
afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
afx_msg void OnSize(UINT nType, int cx, int cy);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
public:
bool m_bShowGripper; // ignored if not WS_THICKFRAME
private:
struct SMovingChild
{
HWND m_hWnd;
double m_dXMoveFrac;
double m_dYMoveFrac;
double m_dXSizeFrac;
double m_dYSizeFrac;
CRect m_rcInitial;
};
typedef std::vector<SMovingChild> MovingChildren;
MovingChildren m_MovingChildren;
CSize m_szInitial;
CSize m_szMinimum;
HWND m_hGripper;
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_)
BaseDialog.cpp:
#include "stdafx.h"
#include "BaseDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CBaseDialog::CBaseDialog(UINT nIDTemplate, CWnd* pParent /*=NULL*/)
: CDialog(nIDTemplate, pParent),
m_bShowGripper(true),
m_szMinimum(0, 0),
m_hGripper(NULL)
{
}
BEGIN_MESSAGE_MAP(CBaseDialog, CDialog)
//{{AFX_MSG_MAP(CBaseDialog)
ON_WM_GETMINMAXINFO()
ON_WM_SIZE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CBaseDialog::AutoMove(int iID, double dXMovePct, double dYMovePct, double dXSizePct, double dYSizePct)
{
ASSERT((dXMovePct + dXSizePct) <= 100.0); // can't use more than 100% of the resize for the child
ASSERT((dYMovePct + dYSizePct) <= 100.0); // can't use more than 100% of the resize for the child
SMovingChild s;
GetDlgItem(iID, &s.m_hWnd);
ASSERT(s.m_hWnd != NULL);
s.m_dXMoveFrac = dXMovePct / 100.0;
s.m_dYMoveFrac = dYMovePct / 100.0;
s.m_dXSizeFrac = dXSizePct / 100.0;
s.m_dYSizeFrac = dYSizePct / 100.0;
::GetWindowRect(s.m_hWnd, &s.m_rcInitial);
ScreenToClient(s.m_rcInitial);
m_MovingChildren.push_back(s);
}
BOOL CBaseDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// use the initial dialog size as the default minimum
if ((m_szMinimum.cx == 0) && (m_szMinimum.cy == 0))
{
CRect rcWindow;
GetWindowRect(rcWindow);
m_szMinimum = rcWindow.Size();
}
// keep the initial size of the client area as a baseline for moving/sizing controls
CRect rcClient;
GetClientRect(rcClient);
m_szInitial = rcClient.Size();
// create a gripper in the bottom-right corner
if (m_bShowGripper && ((GetStyle() & WS_THICKFRAME) != 0))
{
SMovingChild s;
s.m_rcInitial.SetRect(-GetSystemMetrics(SM_CXVSCROLL), -GetSystemMetrics(SM_CYHSCROLL), 0, 0);
s.m_rcInitial.OffsetRect(rcClient.BottomRight());
m_hGripper = CreateWindow(_T("Scrollbar"), _T("size"), WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP,
s.m_rcInitial.left, s.m_rcInitial.top, s.m_rcInitial.Width(), s.m_rcInitial.Height(),
m_hWnd, NULL, AfxGetInstanceHandle(), NULL);
ASSERT(m_hGripper != NULL);
if (m_hGripper != NULL)
{
s.m_hWnd = m_hGripper;
s.m_dXMoveFrac = 1.0;
s.m_dYMoveFrac = 1.0;
s.m_dXSizeFrac = 0.0;
s.m_dYSizeFrac = 0.0;
m_MovingChildren.push_back(s);
// put the gripper first in the z-order so it paints first and doesn't obscure other controls
::SetWindowPos(m_hGripper, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
return TRUE; // return TRUE unless you set the focus to a control
}
void CBaseDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
CDialog::OnGetMinMaxInfo(lpMMI);
if (lpMMI->ptMinTrackSize.x < m_szMinimum.cx)
lpMMI->ptMinTrackSize.x = m_szMinimum.cx;
if (lpMMI->ptMinTrackSize.y < m_szMinimum.cy)
lpMMI->ptMinTrackSize.y = m_szMinimum.cy;
}
void CBaseDialog::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
int iXDelta = cx - m_szInitial.cx;
int iYDelta = cy - m_szInitial.cy;
HDWP hDefer = NULL;
for (MovingChildren::iterator p = m_MovingChildren.begin(); p != m_MovingChildren.end(); ++p)
{
if (p->m_hWnd != NULL)
{
CRect rcNew(p->m_rcInitial);
rcNew.OffsetRect(int(iXDelta * p->m_dXMoveFrac), int(iYDelta * p->m_dYMoveFrac));
rcNew.right += int(iXDelta * p->m_dXSizeFrac);
rcNew.bottom += int(iYDelta * p->m_dYSizeFrac);
if (hDefer == NULL)
hDefer = BeginDeferWindowPos(m_MovingChildren.size());
UINT uFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER;
if ((p->m_dXSizeFrac != 0.0) || (p->m_dYSizeFrac != 0.0))
uFlags |= SWP_NOCOPYBITS;
DeferWindowPos(hDefer, p->m_hWnd, NULL, rcNew.left, rcNew.top, rcNew.Width(), rcNew.Height(), uFlags);
}
}
if (hDefer != NULL)
EndDeferWindowPos(hDefer);
if (m_hGripper != NULL)
::ShowWindow(m_hGripper, (nType == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
}
Since Visual Studio 2015, you can use MFC Dynamic Dialog Layout, but it seems, there is no way to restrict dialog size to minimal size (still only the old way by handling WM_GETMINMAXINFO).
Dynamic layout can be done:
at design time in resource editor by selecting the control and setting the Moving Type and Sizing Type properties (this emits new AFX_DIALOG_LAYOUT section into .rc file);
or programatically using the CMFCDynamicLayout class.
Documentation: Dynamic Layout
If your using a dialog template then open the dialog template in the resource editor and set the Style property to Popup and the Border property to Resizing. I'm pretty sure this will do the same as what jussij said and set the WS_POPUP and WS_THICKFRAME styles. To set these dynamically then override the PreCreateWindow function and add the following:
cs.style |= WS_POPUP | WS_THICKFRAME;
There is no easy way to do this. Basically, you will need to dynamically layout controls when the window size is changed.
See http://www.codeproject.com/KB/dialog/resizabledialog.aspx for an example
I have some blog instructions on how to create a very minimalist re-sizeable dialog in MFC.
It is basically an implementation of Paulo Messina's posting at CodeProject
but with as much extraneous stuff removed as possible, just to help clarify how to do it better.
It is fairly straightforward to implement once you've had a bit of practice: the important bits are to:
i. ensure you have his CodeProject libraries etc pulled into your project and it all compiles correctly.
ii. do the extra initialization required inside the OnInitDialog method: make the gripper visible, set the maximum dilog size, add anchor points to the dialog control items that you wish to 'stretch' etc.
iii. Replace usage of CDialog with CResizableDialog at the appropriate points: in the dialog class definition, constructor, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog etc.
I've tried many MFC layout libraries and found this one the best: http://www.codeproject.com/KB/dialog/layoutmgr.aspx. Check out the comments there for some bug fixes and improvements (disclaimer: some of them by me ;) ). When you use this library, setting the correct resize flags on your window will be handled for you.

Resources