|
Hi Serge,
great article and congratulations ... your resizable classes are ready to use. Only one thing that most likely has to do with WTL 7.1:
So I compiled your example project usind Visual Studio .Net 2003 and WTL 7.1 and there was just one change I had to do, 'WTL::_U_STRINGorID' to 'ATL::_U_STRINGorID' in LayoutMgr.h.
But now there is a misbehaviour of ActiveX controls. The controls in your form view as well as the 'CAxResizablePropertyPageImpl' - derived dialogs appear to have a shadow on the screen, a double existence ... which can be kind of annoying.
Unfortunately the AX ctrls are the point I was looking for when I came across your article.
Is there an easy fix to this thing or where would I most likely have to look at ?
Thanks,
Lothar
|
|
|
|
|
Hi Serge.
When we worked together at Dell years ago, you had a version of this code that worked on the MFC build (with no WTL, or not that I was aware of).
Does the code you've posted here, which I presume is an evolution or re-write of that older code, does it work with non-WTL projects (eg. MFC projects)?
Hope you're well & look forward to hearing from you
Jon
|
|
|
|
|
Here's what you do...
First remove the test for themes in LayoutMgr<T>::OnEraseBackground- it doesn't seem to be a big problem.
Then, what you need to do is give dialog ID's to forms which are attached to static controls. Since the _controlsToClip uses the static id's, it's not actually excluding the property sheets. Simply make 2 new ids, and add them to the list. Then in CWTLTestView::OnInitDialog add this:
_ps1.SetDlgCtrlID(IDC_PS_FORM_1);
_ps2.SetDlgCtrlID(IDC_PS_FORM_2);
This gets rid of much of the flicker, and we're down to standard techniques for eliminating flicker. You can override TestPS2 to handle WM_ERASEBKGND and have it do nothing. This clears up almost all flicker, except for the little graph image, which appears to be an ole control. I'll leave that one to the masses.
The important piece is to make sure you give ID's to controls that are being created outside the dialog template. I hope this helps...
|
|
|
|
|
Hi
I am busy for a school project and i have been searching for the correct way to Handle the Message WM_ERASEBKGND. In Delphi you can fetch the message and return a 1 as result.
Is there a quick way to make a custom skinable form?
Grtzz
|
|
|
|
|
Hi,
Does anyone know how to get this working with listview elements (sysListView32, CListCtrl ) as an element on a CResizablePropertyPageImpl inheritor while using the CPopupResizablePropertySheetImpl . I've tried everything... down to enumerating through the child windows and comparing the atom values to grab my listview handle (I strongly recommend against this).
My main problem seems to be getting an actual handle (HWND) to the listview. Otherwise, I've found the handle, but ran into a problem of not knowing whether this actually works with a listview.
If you have succeeded in doing this and write nothing more than... "Yes, it would work with a win32 listview", than I would be more than gracious. Of course advice on the topic would be f***ing awesome.
Thanks!
Andy Lam
Software Developer
|
|
|
|
|
class CformView : public CDialogImpl<cformview>, CDialogResize<cformview>
{
public:
enum { IDD = IDD_TTT_FORM };
BOOL PreTranslateMessage(MSG* pMsg)
{
return IsDialogMessage(pMsg);
}
BEGIN_DLGRESIZE_MAP(CformView)
DLGRESIZE_CONTROL(IDC_TREE,DLSZ_SIZE_X|DLSZ_SIZE_Y)
END_DLGRESIZE_MAP()
BEGIN_MSG_MAP(CformView)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
CHAIN_MSG_MAP(CDialogResize<cformview>)
END_MSG_MAP()
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
DlgResize_Init(true);
return TRUE;
}
};
|
|
|
|
|
Can anyone modify this wizard to generate WTL code instead of MFC.
http://www.codeguru.com/propertysheet/PropSheet_Wizard.shtml
|
|
|
|
|
LayoutMgr<t>::OnEraseBackground has a bug. It will clip control from _controlsToClip even if it's not visible. I would rewrite line
if (chwnd != 0)
to
if ((chwnd != 0) && (IsWindowVisible( chwnd) )
The overall code is great and very flexible.
Thanks.
George
|
|
|
|
|
Thanks for the bug fix
Serge
|
|
|
|
|
You don't need a special "SizeGrip" class for the gripper - you can use the built-in one (the scrollbar window class with SBS_SIZEBOX and SBS_SIZEGRIP styles).
A couple added benefits of using the built-in gripper:
- Under Windows XP themes, the gripper takes on the look of the theme (dots instead of lines for example). The class "SizeGrip" isn't theme aware.
- You can click "farther in" on the gripper to drag. With the "SizeGrip" class, the mouse needs to be near the bottom or right for the cursor to change and to be able to drag
Its pretty easy to change the code:
- Remove the class SizeGrip from LayoutMgr.h (lines 37-61) and LayoutMgr.cpp (lines 18-57)
- Change LayoutMgr.h line 270 (line 245 after removing above lines above) from
SizeGrip _handle; to
CWindow _handle;
- Change LayoutMgr.h line 1184 (line 1159 after removing lines above) from
_handle.Create(*pT);
to
RECT rectDlg;
pT->GetClientRect(&rectDlg);
_handle.Create(_T("SCROLLBAR"), pT->m_hWnd, rectDlg, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEBOX | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 0, ATL_IDW_STATUS_BAR);
-Daniel
|
|
|
|
|
Thanks for the tip.
I will integrate it in the sources in the next release.
Serge
|
|
|
|
|
Very nice and useful code! It's more feature filled then built-in WTL::CDialogResize class.
But I've found one bug. To see it, just perform the following steps:
1. reduce the window's size until scrollbar will be shown.
2. scroll window to right-most (or bottom-most) position, depend on tested scrollbar.
3. start increasing window size by dragging right (or bottom) window border.
4. all controls must be scrolled to the right (or bottom) direction, but they are stay unchanged.
To fix this problem you can patch CScrollImpl class. Just open AtlScrl.h file and go to 440 line.
Replace the following lines:
if(bUpdate)
SetScrollOffset(x, y);
With new code:
if(bUpdate)
{
pT->ScrollWindowEx(m_ptOffset.x - x, m_ptOffset.y - y, m_uScrollFlags);
SetScrollOffset(x, y, FALSE);
}
BTW I don't know who from Microsoft developers collects WTL errors. If anybody knows it, please post this message to Microsoft team.
|
|
|
|
|
Thanks for the bug fix.
In my opinion, if you want to see this bug fixed in the next release of WTL, is to post a bug report to the WTL discussion group. Nenad Stefanovic, the author of WTL, participates quite often to the discussions.
Serge
|
|
|
|
|
For applications, which will run on various
screen resolutions, I wrote a scalable dialog
class. I've tried to add the code, didn't work,
probably too many lines. Anyone interested can
e-mail me. If no reply, try also weinies_nospam@telus.net.
This class can perfectly be used together with Serge's classes.
Here the header and some fragments:
///////////////////////////////////////////////////////////////////////////////
//
// CAliAxScalableDialogImpl
//
// DESCRIPTION
// Scale the dialog based on the dialog template font. Based on the idea
// in CDialogTemplate (MFC).
//
// NOTE
// You are free to use, distribute or modify this code
// as long as the header is not removed or modified.
//
// MODIFICATIONS
// 09/14/2001 A. Weinie
// Created
//
///////////////////////////////////////////////////////////////////////////////
template <class t,="" class="" dlgbase="CAxDialogImpl<T"> >
class CAliAxScalableDialogImpl : public DlgBase
{
private:
TCHAR m_szFontFace[LF_FACESIZE];
WORD m_iFontPointSize;
DWORD m_dwTemplateSize;
DLGTEMPLATE* m_pDlgTempl; // can be the unmodified or modified (m_hGlobal) resource content
HGLOBAL m_hGlobal;
...
...
///////////////////////////////////////////////////////////////////////////////
// If you want to scale the dlg, call SetFontSize or SetFontFaceAndSize before.
// The dialog will be scaled based on the dialog template font size.
// GetDialogSize cann be used to check the (modified) size of the dialog.
///////////////////////////////////////////////////////////////////////////////
int DoModal( HWND hWndParent = ::GetActiveWindow(),
LPARAM dwInitParam = NULL)
...
...
|
|
|
|
|
|
- Firstly, I think I have far more control on how the dialog children are layout. If you want, I can provide you with a lot of useful layouts which cannot be done with
CDialogResize . - Secondly, my classes support dynamic setting of the contraints. It means that I can add or remove controls during the life of the dialog
- Thirdly: there is no support for property sheet. It can be done for
CDialogResize but as this code is an adaptation of MFC code, it was far easier to do a straight adaptation for WTL.
Serge
|
|
|
|
|
First of all I want to mention that I
appreciate Serge's great contribution.
Depending on the screen resolution I'd
like to scale the the dialog before It'll
be displayed. For MFC, there are some solutions
available. They use the undocumented class
CDialogTemplate and of course DLGTEMPLATE.
Just with modifying of the dialog font size,
the entire dialog will be scaled. Very handy.
Has somebody (yeah, maybe you Serge) already
done something like that using ATL/WTL or Win
API?
Thanks!
|
|
|
|
|
Are you looking for a solution to the Small vs. Large font problem with dialogs? If so I came up with a bit of code the other day to handle this. I use it in an ATL dialog project.
|
|
|
|
|
There is no ideal solution.
You can:
- Define a dialog template for every "standard resolution", change IDD to a class member instead of an enumeration. And set its value to the right dialog template ID according to the current resolution.
- You can predefined a dialog size for every "standard resolution", and change the dialog size according to the current resolution.
The last solution can be implemented like this:
#include "LayoutMgr.h"
#include <map>
#include <limits>
#include <atlmisc.h>
struct Resolution : public std::pair<int, int>
{
typedef std::pair<int, int> inherited;
Resolution(int cx = 0, int cy = 0)
: inherited(cx, cy) {}
int Distance(const Resolution &r) const
{
if ((r.first < first) || (r.second < second))
{
return INT_MAX;
}
return (r.first - first)*(r.first - first) + (r.second - second)*(r.second - second);
}
int Area() const
{
return first*second;
}
};
class ResolutionMap : public std::map<Resolution, SIZE>
{
typedef std::map<Resolution, SIZE> inherited;
public:
const_iterator FindClosestMatch(const Resolution &desiredRes) const
{
ATLASSERT(!empty());
const_iterator res = end(), r;
int d, min_dest = INT_MAX;
for(r = begin(); r != end(); r++)
{
d = r->first.Distance(desiredRes);
if (d < min_dest)
{
min_dest = d;
res = r;
}
}
if (res != end())
{
return res;
}
int a, amin = 0;
for(r = begin(); r != end(); r++)
{
a = r->first.Area();
if (a < amin)
{
amin = a;
res = r;
}
}
return res;
}
};
class MyDlg : public CResizableDialogImpl<MyDlg>
{
...
LRESULT OnInitDialog(UINT , WPARAM , LPARAM , BOOL& )
{
ResolutionMap res;
res[Resolution(640, 480)] = CSize(300, 300);
res[Resolution(1024, 768)] = CSize(450, 450);
res[Resolution(1280, 1024)] = CSize(800, 800);
ResolutionMap::const_iterator oRes;
CClientDC dc(m_hWnd);
Resolution aRes(dc.GetDeviceCaps(HORZRES), dc.GetDeviceCaps(VERTRES));
oRes = res.FindClosestMatch(aRes);
SetWindowPos(0, 0, 0, oRes->second.cx, oRes->second.cy, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
CenterWindow(GetParent());
return TRUE;
}
...
};
Serge
|
|
|
|
|
Thanks for the reply.
What I'm trying to do is to change the dialog
template font size during runtime. It'll
lead to the same result like when you change
the font size in the dialog resource editor.
The whole dialog and all controls will be
"scaled" according the font size. The font
size of buttons are changing accordingly.
The goal is, to improve the readability of
dialogs on high(est) resolution monitors. And
for this purpose the font size is the most
important criteria.
For me, scale and resize is not quite the
same. I scale the dialog before I am going
to display it and let the user resize it.
Code fragment of the MFC-solution:
int CScalableDialog::DoModal(float fDialogScale)
{
m_rcScreen = CRect(nLeft, nTop, nRight, nBottom);
m_rcScreen.NormalizeRect();
//if no resizing asked for
// do it as usual
if( fDialogScale <= 1)
{
return CDialog::DoModal();
}
m_fDialogScale = fDialogScale;
HGLOBAL hDialogTemplateOrig = m_hDialogTemplate;
HGLOBAL hDialogTemplate = NULL;
LPDLGTEMPLATE lpDialogTemplate = NULL;
int result;
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != NULL)
{
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
hDialogTemplate = LoadResource(hInst, hResource);
}
if (hDialogTemplate != NULL)
{
lpDialogTemplate = (LPDLGTEMPLATE)LockResource(hDialogTemplate);
CDialogTemplate tmpl(lpDialogTemplate);
UnlockResource(hDialogTemplate);
CString strFaceNameOrig;
WORD nFontSizeOrig = 0;
int nFontSize = 0;
if(tmpl.GetFont(strFaceNameOrig,nFontSizeOrig))
{
nFontSize = int((float)nFontSizeOrig*m_fDialogScale+.5);
nFontSize = max(8, nFontSize); //not less than 8 points font
if(nFontSize)
nFontSizeOrig = nFontSize;
tmpl.SetFont(strFaceNameOrig,nFontSizeOrig);
}
hDialogTemplate = tmpl.Detach();
if(hDialogTemplate)
{
// save original template name
LPCTSTR lpszOrgTemplateName = m_lpszTemplateName;
//make it zero to force base class to look at original resource
m_lpszTemplateName = 0;
//point to modified resource allocated by CDialogTemplate constructor
m_hDialogTemplate = hDialogTemplate;
result = CDialog::DoModal();
//free the copy created by CDialogTemplate constructor
GlobalFree(m_hDialogTemplate);
//restore original values of class members
m_lpszTemplateName = lpszOrgTemplateName;
m_hDialogTemplate = hDialogTemplateOrig;
}
}
return result;
}
Do something similiar in BOOL CScalableDialog::Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd /*= NULL*/) for non-modal dlgs.
|
|
|
|
|
If you want just to change the size of the font, you should override DoModal in your class.
Copy the original definition from CDialogImplBaseT::DoModal . Put your code for modifying the dialog template just before the call to DialogBoxParam . Replace the call to DialogBoxParam by a call to DialogBoxIndirectParam with your modified dialog template as a parameter.
This should work.
You can have also a look at AtlAxCreateDialogA which is used in CAxDialogImpl::DoModal.
Serge
|
|
|
|
|
... what about ActiveX control hosting support? What if my dialogs are based on CAxDialogImpl, not CDialogImpl?.. Do I understand it correctly that in order for it to work I have to modify LayoutMgr itself?
Maksym
|
|
|
|
|
You are absolutely right.
As it is an important feature, I've modified the code in order to allow activex control hosting.
Serge
|
|
|
|
|
Hi serge great code.
At the minute I start off displaying a CChildResizablePropertySheetImpl in the control but when a user presses a button I want to hide the property sheet and display a form view derived from CResizableFormViewImpl. the form gets displayed but doesn't resize within the control. I tried overriding CWindow::GetClientRect in MyFormView to get the client rect of the control but this hasn't worked.
Do you have any tips on how I can make this work ?
any thoughs would be appreciated
Peter Mulholland
|
|
|
|
|
When two MDI child windows are brought up and you choose Tile they kind of freak. Also there isn't the normal child minimize maximize buttons that mfc has. Any ideas?
|
|
|
|
|