Introduction
Recently I've been reading up on WTL, and I came across a rather interesting class that I hadn't seen mentioned
anywhere, CDialogResize. Given the large number of MFC implementations of resizable dialogs, it's
great that WTL provides its own, so you only have to learn one class and one way of specifying what gets resized.
In this article, I will outline WTL's resizing support and provide a sample program to illustrate some of the features.
You should already be familiar with WTL and how to install it. If you need help with this, there are articles on
those subjects in the WTL section.
Using CDialogResize
The basics
As with many other WTL features, you begin by adding CDialogResize to the inheritance list of your
dialog class. So, if you make a dialog-based app with the WTL AppWizard, you would add the line listed here in
red:
class CMainDlg : public CAxDialogImpl<CMainDlg>,
public CDialogResize<CMainDlg>
CDialogResize is declared in atlframe.h, so add that header to your includes if it isn't there
already.
The next step is to initialize the CDialogResize code in your dialog's OnInitDialog
handler:
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
DlgResize_Init();
...
}
DlgResize_Init() has a few optional parameters, which I'll cover later.
Next, add an entry in the dialog's message map that passes sizing messages to CDialogResize:
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
...
CHAIN_MSG_MAP(CDialogResize<CMainDlg>)
END_MSG_MAP()
Finally, add a new map to the dialog class that lists which controls in the dialog will be resized:
class CMainDlg : public CAxDialogImpl<CMainDlg>, public CDialogResize<CMainDlg>
{
...
public:
BEGIN_DLGRESIZE_MAP(CMainDlg)
END_DLGRESIZE_MAP()
...
};
I'll describe how to fill in this map in the "Setting up the resize map" section.
Initializing CDialogResize
CDialogResize is initialized by calling to DlgResize_Init(). Its prototype is:
void CDialogResize::DlgResize_Init (
bool bAddGripper = true,
bool bUseMinTrackSize = true,
DWORD dwForceStyle = WS_THICKFRAME | WS_CLIPCHILDREN );
The parameters are:
bAddGripper: This controls whether CDialogResize adds a size box to the bottom-right corner
of the dialog. Pass true to add the size box, or false to not add it.
bUseMinTrackSize: This parameter controls whether CDialogResize restricts the minimum size
of the dialog. If you pass true, the dialog is not allowed to be sized smaller than its initial size
(as stored in the resource file). Pass false if you don't want to restrict the dialog's minimum size.
dwForceStyle: Specifies window styles to apply to the dialog. The default value is usually sufficient.
Setting up the resize map
The dialog resize map tells CDialogResize which controls to move or size. An entry looks like this:
DLGRESIZE_CONTROL(ControlID, Flags)
ControlID is the ID of the dialog control. The possible flags and their meanings are:
DLSZ_SIZE_X: Resize the width of the control as the dialog resizes horizontally.
DLSZ_SIZE_Y: Resize the height of the control as the dialog resizes vertically.
DLSZ_MOVE_X: Move the control horizontally as the dialog resizes horizontally.
DLSZ_MOVE_Y: Move the control vertically as the dialog resizes vertically.
DLSZ_REPAINT: Invalidate the control after every move/resize so it repaints every time.
Note that you cannot move and size a control in the same dimension. If you specify, say DLSZ_MOVE_X
and DLSZ_SIZE_X together, the size flag is ignored.
You can also group controls together so that they move and size proportionally to each other. I will cover this
subject later.
Sample Project
The sample project included with this article is a simple dialog-based app that functions as a browser (using
the WebBrowser ActiveX control). The control IDs are listed below; you should refer back to this diagram later
when I discuss moving and resizing these controls.
![[Dlg control IDs - 17K]](wtldlgresize/dlgids.gif)
The controls will move and size according to these rules:
- The Location edit box will resize horizontally.
- The Go, Exit, and About buttons will move horizontally.
- The Back, Forward, Stop, and Refresh buttons will resize horizontally in a group.
- The browser control will resize horizontally and vertically
The browser's OnInitDialog() function initializes CDialogResize like this:
LRESULT OnInitDialog(UINT , WPARAM , LPARAM ,
BOOL& )
{
DlgResize_Init();
...
}
This uses the default parameters to DlgResize_Init(), which results in a size box being added.
Also, the dialog cannot be sized smaller than its initial size. Here's what the dialog looks like on startup:
![[Main dialog - 28K] \](wtldlgresize/dlgsize1.gif)
Here is the resize map, which lists the moving and sizing behavior for the controls. Note the new macros -
BEGIN_DLGRESIZE_GROUP() and END_DLGRESIZE_GROUP() - that put the four browser control
buttons into a resizing group.
BEGIN_DLGRESIZE_MAP(CMainDlg)
DLGRESIZE_CONTROL(IDC_URL, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_GO, DLSZ_MOVE_X)
DLGRESIZE_CONTROL(IDC_EXIT, DLSZ_MOVE_X)
DLGRESIZE_CONTROL(ID_APP_ABOUT, DLSZ_MOVE_X)
BEGIN_DLGRESIZE_GROUP()
DLGRESIZE_CONTROL(IDC_BACK, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_FORWARD, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_STOP, DLSZ_SIZE_X)
DLGRESIZE_CONTROL(IDC_REFRESH, DLSZ_SIZE_X)
END_DLGRESIZE_GROUP()
DLGRESIZE_CONTROL(IDC_BROWSER, DLSZ_SIZE_X|DLSZ_SIZE_Y)
END_DLGRESIZE_MAP()
Here is the dialog after being resized:
![[Bigger dialog - 61K]](wtldlgresize/dlgsize2.gif)
Notice how the edit box is wider, and the browser control is wider and taller. The behavior of the four grouped
buttons is a bit tough to put into words, and the WTL code offers little guidance since there are few comments.
But the basic idea is: imagine a bounding rectangle that surrounds all four buttons. That rectangle resizes like
any other control, and all the buttons are sized proportionally so they remain within the bounding rectangle. If
the buttons were to be moved, instead of resized, they would be positioned to always be evenly spaced apart. Note
that all the controls in a group should have the same DLSZ_* flags to produce meaningful behavior.
Bugs and Problems with CDialogResize
So far, I've only seen two problems. One is that there seems to be an off-by-one bug somewhere, because the
first time you resize the dialog, some of the controls shift the wrong direction by one pixel. The other, more
serious problem, is a bug in the WTL AppWizard that is exposed when you add CDialogResize as a base
class of your dialog. The AppWizard-generated code that displays the dialog looks like this:
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE ,
LPTSTR lpstrCmdLine, int nCmdShow)
{
CMainDlg dlgMain;
int nRet = dlgMain.DoModal();
_Module.Term();
::CoUninitialize();
return nRet;
}
The trouble with that is the CMainDlg destructor is called after _Module.Term(). This
causes a crash in a release build if it is built with the _ATL_MIN_CRT symbol defined. The solution
is to put the CMainDlg object in an enclosing block:
int nRet;
{
CMainDlg dlgMain;
nRet = dlgMain.DoModal();
}
| You must Sign In to use this message board. |
|
|
 |
|
 |
I built this under VS2005 and WTL80 and the dialog would not resize until I added the WS_THICKFRAME style to the dialog template as specified in ATLFRAME.H:
/////////////////////////////////////////////////////////////////////////////// // CDialogResize - provides support for resizing dialog controls // (works for any window that has child controls)
// Put CDialogResize in the list of base classes for a dialog (or even plain window), // then implement DLGRESIZE map by specifying controls and groups of control // and using DLSZ_* values to specify how are they supposed to be resized. // // Notes: // - Resizeable border (WS_THICKFRAME style) should be set in the dialog template
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
There was a breaking change in WTL 7.5. In 7.0, DlgResize_Init() had this prototype:void DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_THICKFRAME | WS_CLIPCHILDREN); But in 7.5, it'svoid DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_CLIPCHILDREN); Notice WS_THICKFRAME was removed from dwForceStyle's default value.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi Michael,
Thanks for a most series of articles on WTL. Only just started with WTL, but I'm loving it already! 
I have a question about the problem you highlighted with the AppWizard generated code that is exposed when you add CDialogResize as a base class of your dialog.
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE , LPTSTR lpstrCmdLine, int nCmdShow) { CMainDlg dlgMain; int nRet = dlgMain.DoModal();
_Module.Term(); ::CoUninitialize();
return nRet; }
int nRet;
{ CMainDlg dlgMain; nRet = dlgMain.DoModal(); }
I'm using WTL 8.0 (same as WTL 7.5 except AppWizard and docs has been updated for VS 2005). I've created a modeless dialog based app using the AppWizard which has generated the following code. Am I right in thinking that because CMainDialog is instantiated (correct terminology ?) in the Run() function, the destructor for CMainDialog is called when Run() exits (and so before _Module.Term() is called). Hence, this code is now safe ?
CAppModule _Module;
int Run(LPTSTR = NULL, int nCmdShow = SW_SHOWDEFAULT) { CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop);
CMainDlg dlgMain;
if(dlgMain.Create(NULL) == NULL) { ATLTRACE(_T("Main dialog creation failed!\n")); return 0; }
dlgMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop(); return nRet; }
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE , LPTSTR lpstrCmdLine, int nCmdShow) { HRESULT hRes = ::CoInitialize(NULL); ATLASSERT(SUCCEEDED(hRes));
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_BAR_CLASSES); hRes = _Module.Init(NULL, hInstance); ATLASSERT(SUCCEEDED(hRes));
int nRet = Run(lpstrCmdLine, nCmdShow);
_Module.Term(); ::CoUninitialize();
return nRet; }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Defenestration wrote: Am I right in thinking that because CMainDialog is instantiated (correct terminology ?) in the Run() function, the destructor for CMainDialog is called when Run() exits (and so before _Module.Term() is called). Hence, this code is now safe ? Yes.
--Mike-- Visual C++ MVP  LINKS~! Ericahist | PimpFish | CP SearchBar v3.0 | C++ Forum FAQ
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
I must admit I had some doubts about the flexibility of CDialogResize, particularly after extensively using Herbert Menke's CResizeCtrl[^] in MFC projects.
However, having now had a crack at it (in a Visual Studio .NET toolwindow, no less!) I have to admit though this is even easier to use - although I suspect there are some resizing scenarios it won't handle, since it doesn't give you control of how far a control should be moved/sized...it seems to always assume 100%.
Thanks for a very good explanation of how to use this very useful class.
Anna 
Riverblade Ltd - Software Consultancy Services
Anna's Place | Tears and Laughter
"Be yourself - not what others think you should be" - Marcia Graesch
"Anna's just a sexy-looking lesbian tart" - A friend, trying to wind me up. It didn't work.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I can't figure out how to center controls (horizontally or vertically) on a dialog. It looks like this (quite obvious) functionality is not supported by CDialogResize. I was trying to join controls in a group, to add invisible anchor windows with no result.
Dos anybody know about possible workarounds for this?
Thanks!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello, I'm dynamically creating my buttons, so once I add the button to DLGRESIZE_CONTROL(IDD_BUTTON_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y)
The coordinate system seems to completely change (0,0) is not the top of the window but somewhere in the middle.
Can you guys suggest me how to do it? I want to place the button in right bottom corner of my screen (its not a dialog but a View CDialogImpl).
Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
I have a dialog that I am showing Modal (In a seperate thread) with a single static image. I would just like to allow resizing and show scroll bars when the dialog gets too small. It would also be great if I could limit the max and min size of the dlg.
Please Help  -- Rick
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Nice example, but with one problem. In my app COM is initialized through CoInitializeEx(NULL, COINIT_MULTITHREADED) in place of CoInitialize(NULL), because my app use COM object, that return events from different thread and without COINIT_MULTITHREADED it crash all app on shutdown. An in that case, when dialog opens, WebBrowser control is drawed out of dialog on left top side of desktop (at dialog remain empty rectangle, that is not be updated anymore. Without COINIT_MULTITHREADED dialog works as expected. Where is problem, i don't know
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I found source of this problem - WebBrowser ActiveX control needs STA to run, that means, app must be intialized through CoInitialize(NULL) or CoInitializeEx(NULL, COINIT_APARTAMENTTHREADED). Then i redesigned our dialog for work in different thread:
void CReportManager::StartReport() { Lock(); .....
// Create report window thread. DWORD dwThreadId; HANDLE hThread = ::CreateThread(NULL, 0, CReportManager::ReportFunction, this, 0, &dwThreadId); if (hThread == NULL) { ATLTRACE(_T("* Unable to create report window thread.")); Cancel(); Unlock(); return; } ::CloseHandle(hThread);
..... Unlock(); }
// Report manager client window thread function. DWORD WINAPI CReportManager::ReportFunction(LPVOID lpParam) { CReportManager* pThis = reinterpret_cast(lpParam); volatile BOOL *bWorking = &pThis->m_bWorking;
if (bWorking && pThis->m_rptState == rptRunning) { // Reinitialize COM to STA for this thread. ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); CReportViewDlg dlg(pThis->m_strTitle); dlg.DoModal(g_pwndMain->m_hWnd); pThis->m_hWndRpt = NULL; ::CoUninitialize(); }
return 0; }
Now all works as i expected
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
When reading Mike's article, i immediately re-implemented a project i am actually working on. It's a simple CDialogImpl (now with CDialogResize) that hold a couple of simple controls (buttons, checkbuttons,..). And -now here it comes- it also contains a CTabCtrl-derived control and 2 CListViewCtrl-derived objects that make up the views for the Tab. So far, when constructing that all without CDialogResize, it worked fine, although static in size. Now everything (re-)sizes fine (thank you Mike) but, when the dialog gets obscured by an other window and is brought back to the foreground, the CListViewCtrl objects do not get refreshed. I traced this down, finding that the notification is not handed down to the controls' message map. Does anyone have an idea (that does not end up in rewriting the tab's OnPaint())? One additional clue: i already use REFLECT_NOTIFICATIONS()...
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
|
 |
|
 |
Hi,
I have a TabCtrl on my dialog and it resizes well. I wonder how to resize the dialogs attached to the tab automatically since I cannot add my dialogs shown on the tabs using DLGRESIZE_CONTROL(IDD_DIALOG2, DLSZ_SIZE_X|DLSZ_SIZE_Y) to the resize-method, because the dialog does naturally not exist by the time that code (macro) ist executed. The dialogs for the tabs are (only) created in the OnInitDialog-handler of the dialog tha contains the TabCtrl. Do I in any case have to track the resize of the tab, or is there a way to track the resize with the CDialogResize-class and attach the dialogs to that class?
Best, Bernhard
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Anyone found a solution to this one, I have exactly the same problem, my tab container resizes fine but the child windows within it stay the same. Cheers, Rob.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
When using a CTabCtrl, for the child windows I create a CDialogImpl-derived class to handle the dialog items, etc (I guess you've done the same thing). In this class I placed the code just as Michael described, but in the WM_INITDIALOG handler I changed the style to my dialog's. Damn, I'm bad at describing things, so here's my code:
class CProductInventoryView : public CDialogImpl<CProductInventoryView>, public CDialogResize<CProductInventoryView> { public: enum { IDD = IDD_PRODUCT_VIEW_INVENTORY };
BEGIN_MSG_MAP(CProductInventoryView) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) CHAIN_MSG_MAP(CDialogResize<CProductInventoryView> ) END_MSG_MAP()
BEGIN_DLGRESIZE_MAP(CProductInventoryView) ...etc... END_DLGRESIZE_MAP()
protected: LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); };
LRESULT CProductInventoryView::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { DlgResize_Init(false, true, DS_SETFONT | DS_FIXEDSYS | WS_CHILD);
return S_OK; }
and it's working for me. Hope this helps (although this post is two years from the OP).
Stupidity is an International Association - Enrique Jardiel Poncela
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Any tips on setting the initial dialog size without the controls being placed incorrectly? I want to have persistent dialog sizes (size saved to the registry) but when I call SetWindowPos in OnInitDialog, it ends up a real mess. I have tried various hacks but haven't found an effective solution...
When I am king, you will be first against the wall.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This may/may not help, but there is a bug in WTL 7.0 whereas a resize of a hidden dialog can cause all kinds of resize issues. In my case, it was cutting off certain HTML items from the page that should have appeared on the right. On the yahoo WTL group, there is a post on a WTL 7.0 CDialogResize issue that aids in fixing this outright (by modifying atlframe.h). My fix was to first do a ShowWindow(SW_SHOW), then provide a SetWindowPos call.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
With WinXP and VC.NET 2003
I'm using DialogResize in a dialog box created as a child window. When the dialog resizes all combo boxes with the droplist style have their text magically selected. The text wasn't selected before the move. This is most annoying.
Any one have any idea's what is going on?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Well done Michael. I just added a resizeable dialog to my WTL app in minutes.
When I am king, you will be first against the wall.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|