Thanks
All code presented in this article uses WTL 8.0. Thanks to Nenad Stefanovic and all the WTL developers.
The desktop samples use, with his permission, Bjarke Viks�e's shell controls. Thanks for all Bjarke.
The color menu's colors are extracted from Chris Maunder's Office 97 style Colour Picker control.
Presentation
Using modal dialogs for user interaction is equivalent to issuing a blocking call to the user. DoModal() calls have a simple and unique exit point and, on return, carry the requested information including possible user's cancellation. They usually require the creation of a DIALOGTEMPLATE
resource and of an associated CDialogImpl
derived class.
The set of -unusual- modal dialog classes presented in this article does not require all that. It uses C++ templates, not DIALOGTEMPLATE resources.
It is designed for easy specialization with minimal, if any, specific coding. It constitutes a flexible toolset, allowing easy to write read and maintain applicative code and simple adaptations for multiple platforms specific requirements.
The base classes derive or specialize to non standard menus such as rectangular menus, to single control dialogs for user selection operations or in place text edition and to split dialogs for more complex tasks. They compile with any released MS compiler from VC6/eVC to VS2005 and VCExpress, target any Win32 or WinCE system, and specialize to platform specific classes requiring Windows Vista or Windows Mobile SDKs. Most of their C++ code, located in atldlgx.h, is simply declarative and does not produce any machine code.
This article is made of three parts:
- Guided tours shows what you can get from very few code lines, and gets you acquainted - if needed - with template specializing code.
- Reference describes the general purpose classes and the platform specialized Vista and Mobile classes.
- Samples shows some applicative usage through three similar DialogX applications, a pre-Vista and a Vista-enabled desktop application, and a sibling Windows Mobile application targetting -with platform adaptations- all mobile devices from SmartPhone 2003 and PPC 2002 to WM6.
With the WTL 8.0 AppWizard generate a simple SDI application with CPP code and no view class. Call it DlgTour.
#include <atlwin.h>
#include <atlmisc.h>
#include <atlframe.h>
// 1
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled);
/
#include "stdafx.h"
#include "resource.h"
#include "atldlgx.h" // 3
#include "aboutdlg.h"
#include "MainFrm.h"
LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
return 0;
}
If the Microsoft� Windows� Software Development Kit for Windows Vista� is not installed on your development system go ahead:
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#define _WIN32_IE 0x0600
#include "stdafx.h"
#include "resource.h"
#include <atlctrlx.h> // 5
#include "WtlAero.h" // 5
#include "atldlgx.h" // 3
Now we can start the tour itself.
Paste or type these two lines in CMainFrame::OnContextMenu
code:
LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
typedef CCellMenu<IDR_MAINFRAME, 4, 2> CToolMenu;
CToolMenu::TrackCellMenu(CPoint(lParam));
return 0;
}
Compile, run and right-click in the DlgTour window:
We defined CToolMenu
as a CCellMenu displaying the IDR_MAINFRAME
bitmap in 4 columns and 2 rows. We called its static TrackCellMenu()
member with a default positioning at CPoint(lParam)
, the mouse coordinate.
TrackCellMenu
returns the index of the selected cell or -1 if none, to get a functional menu we must act upon the return value:
LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
typedef CCellMenu<IDR_MAINFRAME, 4, 2> CToolMenu;
int iSel = CToolMenu::TrackCellMenu(CPoint(lParam));
static const UINT commands[] =
{
ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_EDIT_CUT,
ID_EDIT_COPY, ID_EDIT_PASTE, ID_FILE_PRINT, ID_APP_ABOUT
};
if(iSel != -1)
PostMessage(WM_COMMAND, commands[iSel]);
return 0;
}
The only functional command handler is ID_APP_ABOUT
. Select the question mark with the keyboard or click it to bring up the About dialog, perform any other mouse or keyboard action to dismiss the menu.
See CMyToolMenu and CMyColorMenu in the Samples section.
Type this into CMainFrame::OnContextMenu()
member:
LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
typedef CControlDialog<ID_TEST, CMonthCalendarCtrl> CDateDialog;
CPoint pt(lParam);
ScreenToClient(&pt);
CDateDialog dd(pt, CSize(300, 150));
dd.DoModal();
return 0;
}
Compile, run and right-click.
We defined CDateDialog
as a CControlDialog hosting a CMonthCalendarCtrl
, with ID_TEST
id and default styles.
We instanciated a CDateDialog
with origin at mouse position and an arbitrary size and called DoModal()
on it.
To auto-resize the dialog around the monthcalendar control, take CDateDialog
declaration out of CMainFrame::OnContextMenu()
and add some template specialization for it:
typedef CControlDialog<ID_TEST, CMonthCalendarCtrl> CDateDialog;
bool CDateDialog::ControlDialog::Init(LPARAM lParam)
{
DefInit(lParam);
CRect rCtrl;
m_Ctrl.GetMinReqRect(rCtrl);
ResizeClient(rCtrl.Width(), rCtrl.Height());
return true;
}
LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
We specialized a CDateDialog
parent class member, CControlDialogImpl<CDateDialog, ID_TEST, CMonthCalendarCtrl>::Init()
to (after default initialization) resize according to the CMonthCalendarCtrl
requested size.
More with CMyDateDialog in the Desktop sample section.
If you did not execute the additional Vista preliminaries skip this step.
Type this in MainFrm.cpp:
class CTestDialog: public aero::CEmptyDialogImpl<CTestDialog, ID_TEST,
CEmptyDlgTemplate<ID_TEST, CSplitDlgTraits> >
{
typedef aero::CEmptyDialogImpl<CTestDialog, ID_TEST,
CEmptyDlgTemplate<ID_TEST,
CSplitDlgTraits> > AeroEmptyDialog;
public:
CTestDialog(SIZE size): AeroEmptyDialog(size)
{}
};
LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
CTestDialog td(CSize(200, 200));
td.DoModal();
return 0;
}
Compile, run and right-click.
We defined a CTestDialog
class deriving from and behaving like aero::CEmptyDialogImpl with CSplitDialogTraits
(resizing border) style. We instantiated it and called its DoModal()
member.
More with CMyVistaShellDialog in the Vista sample section.
VS2005: with the WTL 8.0 Mobile AppWizard generate a simple SDI application targetting PPC2003 and SmartPhone2003, with CPP code, eVC compatibility and no view class. Call it DlgTour.
eVC4: download eVCDlgTour.zip, expand and open it with eVC.
#include "stdafx.h"
#ifdef WIN32_PLATFORM_PSPC
#include "resourceppc.h"
#else
#include "resourcesp.h"
#endif
#include "atldlgs.h" // 1
#include "atldlgx.h" // 1
#include "aboutdlg.h"
#include "DlgTourFrame.h"
Now we can take the tour.
Paste or type these six lines in CDlgTourFrame::OnAction
code:
LRESULT CDlgTourFrame::OnAction(WORD , WORD ,
HWND , BOOL& )
{
CRect rect;
GetClientRect(rect);
CPoint pt(rect.left, rect.bottom);
ClientToScreen(&pt);
typedef CCellMenu<IDR_MAINFRAME, 7, 1> CToolMenu;
CToolMenu::TrackCellMenu(pt, TPM_BOTTOMALIGN);
return 0;
}
Select SmartPhone 2003 platform, compile, run and push the Action button:
We defined a CCellMenu displaying the IDR_MAINFRAME
bitmap in 7 columns and 1 row.
We called its TrackCellMenu()
static member with a bottom (and default left) positioning at the bottom left corner of the frame
TrackCellMenu
returns the index of the selected cell or -1 if none, to get a functional menu we must use the return value. Add the following at the end of CDlgTourFrame::OnAction
:
LRESULT CDlgTourFrame::OnAction(WORD , WORD ,
HWND , BOOL& )
{
int iSel = CToolMenu::TrackCellMenu(pt, TPM_BOTTOMALIGN);
static const UINT commands[] =
{
ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_EDIT_CUT,
ID_EDIT_COPY, ID_EDIT_PASTE, ID_APP_ABOUT
};
if(iSel != -1)
PostMessage(WM_COMMAND, commands[iSel]);
return 0;
};
The only working handler is OnAppAbout
, if we select the question mark with the keypad, the ID_APP_ABOUT
command will indeed execute and pop out the About dialog.
More with Mobile CMyToolMenu in the Mobile sample section.
Paste or type this in CDlgTourFrame::OnAction
code:
LRESULT CDlgTourFrame::OnAction(WORD , WORD ,
HWND , BOOL& )
{
typedef CStdControlDialog<ID_TEST, CMonthCalendarCtrl> CDateDialog;
CDateDialog dd;
dd.DoModal();
return 0;
}
Select SmartPhone 2003 platform, compile, run and push the Action button:
We defined CDateDialog
as a CStdControlDialog hosting a CMonthCalendarCtrl
, with ID_TEST
ID.
We instantiated a CDateDialog
and called DoModal()
on it.
To use the selected date we should add some calling and some specializing code. More with Mobile CMyDateDialog in the Mobile Sample section.
-
CEmptyDlgTemplate is a WTL::CMemDlgTemplate
loading the dialog title from a resource string in its Create(POINT point, SIZE size)
member.
To fully define a CEmptyDlgTemplate
you need to specify a string resource ID, and optionally an ATL::CWinTraits
instance for the style.
-
CEmptyDlgTraits, CControlDlgTraits, CSplitDlgTraits, are dialog style for various CEmptyDlgTemplate
specializations.
-
CMenuDialog is a MI class: adding it to a modal dialog inheritance list and chaining it's message map enables the parent window and autocancels when losing activation, which gives a simple menulike behavior.
Definitions
As we do not yet benefit of the C++0x template typedefs CEmptyDialogImpl
and other classes definitions in atldlgx.h are not very easy to read. For readability each template argument is written on a different line and commented.
template
< class T,
UINT t_uIDD,
class TDlgTemplate
= CEmptyDlgTemplate<t_uIDD, CEmptyDlgTraits>,
class TBase
= ATL::CDialogImpl<T, ATL::CWindow>
>
class ATL_NO_VTABLE CEmptyDialogImpl: public CIndirectDialogImpl<
T,
TDlgTemplate,
TBase
>
{
typedef CEmptyDialogImpl<T, t_uIDD, TDlgTemplate, TBase> thisClass;
public:
typedef thisClass EmptyDialog;
The explicitly expanded template definitions allow maximum flexibility for class usage. TBase
defaults to ATL::CDialogImpl<>
, but mobile devices CStdEmptyDialogImpl
's TBase
is WTL::CStdDialogImpl<>
. If we want an Aero dialog as described in Have a glass with WTL!, TBase
should be WTL::aero::CDialogImpl<>
.
All constructors have a bool bDLU
parameter. When true, which is the default, the sizing and positioning parameters are converted to DLU in the generated DLGTEMPLATE
.
-
Empty constructor: derived class performs m_Template and m_Data initializations
CEmptyDialogImpl()
-
Sizing constructor: sets origin to {0,0}
CEmptyDialogImpl(SIZE size, bool bDLU = true)
-
Positioning constructor: sets origin to point and size to size
CEmptyDialogImpl(POINT point, SIZE size, bool bDLU = true)
-
Positioning constructor: uses rect for origin and size
CEmptyDialogImpl(RECT rect, bool bDLU = true)
Data Members
-
enum {IDD}
is defined from t_uIDD
for compatibility with other dialogs.
-
LPARAM m_Data
holds user data depending on the derived class.
-
HWND m_hWndToolBar
holds the toolbar control handle if any (Win32 platforms only).
Default Operation: Overrideable Members
These members are called by message handlers and may be overridden in derived classes. If not overridden they define the class behavior on receipt of the matching WM_xxx
message. The bool return value is used by the message handlers as the bHandled
value on return.
-
bool Init(LPARAM lParam)
calls DefInit(lParam)
.
-
bool Size(WPARAM ,LPARAM )
repaints the client area.
-
bool Paint(HDC )
does nothing.
-
void Close(INT iCmd)
ends the dialog returning iCmd
.
Helper Members
-
DefInit()
, the default initialization member copies the DoModal() lParam
to m_Data
, tries to create a toolbar, load a menu and set the dialog's icon from t_uIDD
matching resources, and finally frees the DLGTEMPLATE
memory. Setting the WM_INITDIALOG
as unhandled leaves open a possible followup in the derived class message map.
-
MoveToTemplatePix()
allows precise positioning at initialization time when the template position data have not been converted to DLUs at creationtime.
-
The PixToDLU()
members are used internally by the constructors.
Derived Classes
aero::CEmptyDialogImpl and CStdEmptyDialogImpl use a specific TBase
template argument.
CControlDialogImpl, CSplitDialogImpl are base classes derived from CEmptyDialogImpl
.
CCellMenu and CMyVistaShellDialog are user classes directly derived from CEmptyDialogImpl
.
CCellMenu
is intended to replace conventional menus in situations where a rectangular cell layout is appropriate. It uses CMenuDialogTraits
styles:
Definitions
CCellMenu
derives from CEmptyDialogImpl and CMenuDialog.
The class definition uses three template arguments, a bitmap resource ID, a number of columns and a number of rows:
typedef ATL::CWinTraits<WS_POPUP | WS_BORDER> CMenuDialogTraits;
class CMenuDlgTemplate: public CEmptyDlgTemplate<0, CMenuDialogTraits>
{};
template
< UINT t_uIDD,
INT t_nCol,
INT t_nRow
>
class CCellMenu: public CEmptyDialogImpl<
CCellMenu<t_uIDD, t_nCol, t_nRow>,
t_uIDD,
CMenuDlgTemplate
>,
public CMenuDialog<
CCellMenu<t_uIDD, t_nCol, t_nRow>
>
{
typedef CCellMenu<t_uIDD, t_nCol, t_nRow> thisClass;
public:
typedef CMenuDialog<thisClass> MenuDialog;
{
Constructor
CCellMenu
has a single constructor using a POINT
as the overrideables static const SIZE CCellMenu::CellSize()
and static const SIZE CCellMenu::MenuSize()
compute the respective sizes from the associated bitmap size and the number of rows and columns. If there is no bitmap with t_uIDD
id the cell size is computed from the system metrics SM_CXSMICON
and SM_CYSMICON
.
Default Operation
CCellMenu
uses m_Data
as the index of the selected cell and updates it on mouse or keyboard action.
The helper struct CCellMenu::CELL
provides various size and position computations.
CCellMenu::DoModal()
returns the selected cell index if the user clicked on a cell or hit return, or -1 if the dialog was cancelled or dismissed.
CCellMenu
is designed to be operated through it's static int CCellMenu::TrackCellMenu(POINT pt, UINT uFlags = 0, LPARAM lParam = 0, HWND hWndParent = GetActiveWindow())
-
pt
Is the positioning reference point.
-
uFlags
is a combination of TrackPopupMenu
(TPM_xxxALIGN
) flags.
-
lParam
is the initial selection index.
CCellMenu::TrackCellMenu()
generates a CCellMenu
positioned as indicated by uFlags
, calls it's DoModal()
member and returns the user selection index.
Overrideables
CCellMenu
painting and sizing members may be specialized.
void PrePaint(HDC hdc)
{
SIZE s = MenuSize();
RECT rect = {0, 0, s.cx, s.cy};
CDCHandle(hdc).FillSolidRect(&rect,
GetSysColor(COLOR_MENU));
}
void PaintCells(HDC hdc)
{
for (int iRow = 0; iRow < t_nRow; iRow++)
for (int iCol = 0; iCol < t_nCol; iCol++)
PaintCell(hdc, CELL(iCol, iRow));
}
void PaintCell(HDC hdc, CELL &cell)
{
GetImageList().Draw(hdc, cell.Index(), cell.Left(true),
cell.Top(true),
cell.Index() == (INT)m_Data ?
ILD_SELECTED: ILD_NORMAL);
}
void FocusSelection(HDC hdc)
{
RECT rect = CELL((INT)m_Data).Rect();
CDCHandle(hdc).DrawFocusRect(&rect);
}
bool Paint(HDC hdc)
{
PrePaint(hdc);
PaintCells(hdc);
FocusSelection(hdc);
return true;
}
Sample Usage
CMyColorMenu, CMyToolMenu.
CControlDialogImpl
implements a single control dialog, thus allowing to call DoModal()
on any control.
Definitions
typedef ATL::CWinTraits<WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU |
DS_CENTER, WS_EX_DLGMODALFRAME> CControlDlgTraits;
template
< class T,
UINT t_uIDD,
class TCtrl,
class TControlTraits
= ATL::CControlWinTraits,
class TDlgImpl
= CEmptyDialogImpl<T, t_uIDD, CControlDlgTraits>
>
class CControlDialogImpl: public TDlgImpl
{
typedef CControlDialogImpl<T, t_uIDD, TCtrl, TControlTraits,
TDlgImpl> thisClass;
public:
typedef thisClass ControlDialog;
Constructors
Same as CEmptyDialogImpl.
Data Member
CControlDialogImpl
has a m_Ctrl
member of type TCtrl
.
Default Operation: Overrideable Members
These members are called by message handlers and may be overriden in derived or specialized classes. If not overridden they define the class behavior on receipt of the matching WM_xxx
message. The bool return value is used by the message handlers as the bHandled
value on return.
-
bool CControlDialogImpl::Init(LPARAM lParam)
returns CControlDialogImpl::DefInit(lParam)
.
-
bool CControlDialogImpl::Size(WPARAM wParam,LPARAM lParam)
returns CControlDialogImpl::DefSize(wParam, lParam)
.
-
bool Notify(int idCtrl, LPNMHDR pnmhdr)
is called on WM_NOTIFY
message, does nothing and returns false
.
-
bool Command(WORD wNotifyCode, WORD wID, HWND hWndCtl)
is called on WM_COMMAND
message, does nothing and returns false
.
-
bool Key(UINT , WPARAM ,LPARAM )
is called on WM_KEYFIRST
to WM_KEYLAST
messages, does nothing and returns false
.
-
HBRUSH CtlColor(HDC )
is called on WM_xxxCOLOR
messages does nothing and returns NULL
.
Helper Members
bool DefInit(LPARAM lParam)
{
EmptyDialog::Init(lParam);
ControlInit();
return false;
}
void ControlInit()
{
RECT rCtrl;
GetClientRect(&rCtrl);
ATLVERIFY(m_Ctrl.Create(m_hWnd, rCtrl, NULL,
TControlTraits::GetWndStyle(0),
TControlTraits::GetWndExStyle(0), ATL_IDM_WINDOW_FIRST));
m_Ctrl.SetFocus();
}
bool DefSize(WPARAM wParam, LPARAM )
{
if (m_Ctrl.IsWindow() && wParam != SIZE_MINIMIZED)
{
RECT rClient;
GetClientRect(&rClient);
m_Ctrl.MoveWindow(&rClient);
}
return false;
}
Derived Classes
CControlDialog, aero::CControlDialog and the mobile CStdControlDialog instantiable classes derive directly from CControlDialogImpl
.
CInPlaceEditor derives from both CControlDialogImpl
and CMenuDialog.
CControlDialog
is a specializable CControlDialogImpl
.
Definition
template
< UINT t_uIDD,
class TCtrl,
class TControlTraits
= ATL::CControlWinTraits,
class TControlDlgTraits
= CControlDlgTraits
>
class CControlDialog: public CControlDialogImpl<
CControlDialog<t_uIDD, TCtrl, TControlTraits,
TControlDlgTraits>,
t_uIDD,
TCtrl,
TControlTraits,
CEmptyDialogImpl<
CControlDialog<t_uIDD, TCtrl,
TControlTraits, TControlDlgTraits>, t_uIDD,
CEmptyDlgTemplate<t_uIDD,
TControlDlgTraits>
>
>
{
typedef CControlDialog<t_uIDD, TCtrl, TControlTraits,
TControlDlgTraits> thisClass;
Constructors
Same as CEmptyDialogImpl.
Sample Usage
CMyDateDialog
CInPlaceEditor
is intended to allow in place editing of some known text in some known rectangle.
Definitions
CInPlaceEditor
derives from CControlDialogImpl and CMenuDialog.
The class definition uses three template arguments, the maximum length of the edited text, the edit control class which defaults to CEdit
, the edit control styles which default to CInPlaceEditTraits
. So the default is a single line EDIT
with ES_AUTOHSCROLL
.
Definitions and Construction
typedef CWinTraitsOR<ES_AUTOHSCROLL> CInPlaceEditTraits;
template <
UINT t_iLength,
class TEditCtrl
= CEdit,
class TEditTraits
= CInPlaceEditTraits
>
class CInPlaceEditor: public CControlDialogImpl<
CInPlaceEditor<t_iLength, TEditCtrl, TEditTraits>,
t_iLength,
TEditCtrl,
TEditTraits,
CEmptyDialogImpl<
CInPlaceEditor<t_iLength, TEditCtrl,
TEditTraits>,
t_iLength,
CMenuDlgTemplate
>
>,
public CMenuDialog<CInPlaceEditor<t_iLength, TEditCtrl,
TEditTraits> >
{
typedef CInPlaceEditor<t_iLength, TEditCtrl, TEditTraits> thisClass;
public:
CInPlaceEditor(RECT rect): ControlDialog(rect)
{}
Constructor
CInPlaceEditor
has a single constructor using a RECT
.
Default Operation
CInPlaceEditor
uses the DoModal() lParam
value as a text buffer of t_iLength
length. CInPlaceEditor::Init(LPARAM lParam)
copies lParam
to it's m_Data
member and sets the initial content of the edit control from what is there.
If the user hits return the content of the edit control is copied back to the m_Data
member.
CInPlaceEditor
is designed to be operated through it's static bool CInPlaceEditor::Edit(RECT& rEdit, LPTSTR sText, HWND hwndParent = GetActiveWindow())
Overrideables
bool Init(LPARAM lParam)
{
m_Data = lParam;
m_Template.Reset();
ControlInit();
if (!m_Ctrl.GetFont())
m_Ctrl.SetFont(AtlGetDefaultGuiFont());
m_Ctrl.LimitText(t_iLength);
if(lParam)
{
ATLASSERT(!IsBadWritePtr((LPVOID)lParam,
t_iLength * sizeof(TCHAR)));
m_Ctrl.SetWindowText((LPCTSTR)lParam);
m_Ctrl.SetSel((INT)t_iLength, t_iLength);
}
return false;
}
HBRUSH CtlColor(HDC )
{
return GetSysColorBrush(COLOR_HIGHLIGHT);
}
void Close(INT iCmd)
{
if((iCmd == IDOK) && m_Data)
m_Ctrl.GetWindowText((LPTSTR)m_Data, t_iLength);
EmptyDialog::Close(iCmd);
}
Sample Usage
CPaneEditor, CStatusPaneEditor.
CSplitDialogImpl
implements dual control split dialogs.
Definitions
typedef ATL::CWinTraitsOR<WS_THICKFRAME, 0,
CControlDlgTraits> CSplitDlgTraits;
typedef ATL::CWinTraitsOR<WS_TABSTOP> CSplitControlTraits;
template<UINT t_uIDD>
class CSplitDlgTemplate: public CEmptyDlgTemplate<t_uIDD,
CSplitDlgTraits>
{};
template
< class T,
UINT t_uIDD,
class TSplitImpl,
class TLeft,
class TRight,
class TLeftTraits
= CSplitControlTraits,
class TRightTraits
= CSplitControlTraits,
class TDlgImpl
= CEmptyDialogImpl<T, t_uIDD, CSplitDlgTemplate<t_uIDD> >
>
class ATL_NO_VTABLE CSplitDialogImpl: public TDlgImpl,
public TSplitImpl
{
typedef CSplitDialogImpl<T, t_uIDD, TSplitImpl, TLeft, TRight,
TLeftTraits, TRightTraits, TDlgImpl> thisClass;
public:
typedef thisClass SplitDialog;
typedef TSplitImpl Splitter;
Note that TSplitImpl
is not restricted to WTL::CSplitterImpl
. It may derive from it as WTL::aero::CSplitterImpl<> or be compatible as Miguel Hasse de Oliveira's WTL::CDynSplitterImpl.
Constructors
Same as CEmptyDialogImpl.
Data Members
TLeft m_Left;
TRight m_Right;
Default Operation: Overrideable Members
These members are called by message handlers and may be overridden in derived or specialized classes. If not overridden they define the class behavior on receipt of the matching WM_xxx
message. The bool
returned value is copied to bHandled
by the message handlers on return.
-
bool CSplitDialogImpl::Init(LPARAM lParam)
returns CSplitDialogImpl::DefInit(lParam)
.
-
bool CSplitDialogImpl::Size(WPARAM wParam,LPARAM lParam)
calls Splitter::SetSplitterRect()
.
-
bool Notify(int idCtrl, LPNMHDR pnmhdr)
is called on WM_NOTIFY
message, does nothing and returns false
.
-
HBRUSH CtlColor(HDC )
is called on WM_xxxCOLOR
messages does nothing and returns NULL
.
-
bool Command(WORD wNotifyCode, WORD wID, HWND hWndCtl)
returns CSplitDialogImpl::PaneCommand(wID)
.
Helper Members
bool DefInit(LPARAM lParam)
{
EmptyDialog::Init(lParam);
Splitter::GetSystemSettings(false);
m_Left.Create(m_hWnd, rcDefault, NULL, TLeftTraits::GetWndStyle(0),
TLeftTraits::GetWndExStyle(0), ATL_IDM_WINDOW_FIRST);
m_Right.Create(m_hWnd, rcDefault,
NULL, TRightTraits::GetWndStyle(0),
TRightTraits::GetWndExStyle(0), ATL_IDM_WINDOW_LAST);
Splitter::SetSplitterPanes(m_Left, m_Right);
Splitter::SetSplitterRect();
Splitter::SetSinglePaneMode();
Splitter::SetSplitterPos();
return false;
}
bool PaneCommand(WORD wID)
{
switch(wID)
{
case ID_WINDOW_SPLIT:
Splitter::SetSinglePaneMode(
Splitter::GetSinglePaneMode() == SPLIT_PANE_NONE ?
Splitter::GetActivePane(): SPLIT_PANE_NONE);
break;
case ID_NEXT_PANE:
case ID_PREV_PANE:
if (Splitter::GetSinglePaneMode() != SPLIT_PANE_NONE)
Splitter::SetSinglePaneMode(!Splitter::GetActivePane());
else
Splitter::ActivateNextPane();
break;
default:
return false;
}
#if defined(__ATLWINCE_H__)
SetFocus();
#endif
return true;
}
bool PaneCommand(WORD wID)
handles three predefined WTL command Ids:
-
ID_WINDOW_SPLIT
toggles single/dual pane mode.
-
ID_NEXT_PANE
and ID_PREV_PANE
change the active focused, and possibly single pane.
Derived Classes
CSplitDialog, CVSplitDialog, CHSplitDialog
, aero::CSplitDialog and the mobile CStdSplitDialog, CStdVSplitDialog, CStdHSplitDialog
instantiable classes derive directly from CSplitDialogImpl
.
CSplitDialog
is a specializable CSplitDialogImpl
class using any adequate TSplitImpl
splitter implementation.
Definition and Construction
template
< UINT t_uIDD,
class TSplitImpl,
ie WTL::CSplitterImpl<CMySplitDialog, true>
class TLeft,
class TRight,
class TLeftTraits
= CSplitControlTraits,
class TRightTraits
= CSplitControlTraits,
class TSplitDialogTraits
= CSplitDlgTraits
>
class CSplitDialog: public CSplitDialogImpl<
CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits>,
t_uIDD,
TSplitImpl,
TLeft,
TRight,
TLeftTraits,
TRightTraits,
CEmptyDialogImpl<
CSplitDialog<t_uIDD, TSplitImpl,
TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits>,
t_uIDD,
CEmptyDlgTemplate<t_uIDD,
TSplitDialogTraits>
>
>
{
typedef CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits> thisClass;
public:
CSplitDialog(){}
CSplitDialog(SIZE size, bool bDLU = true): SplitDialog(size, bDLU){}
CSplitDialog(POINT point, SIZE size,
bool bDLU = true): SplitDialog(point, size, bDLU){}
CSplitDialog(RECT rect, bool bDLU = true): SplitDialog(rect, bDLU){}
};
CVSplitDialog
and CHSplitDialog
are specializable CSplitDialogImpl classes where TSplitImpl
is WTL::CSplitterImpl<T, true>
and WTL::CSplitterImpl<T, false>
.
Definition and Construction
template
< UINT t_uIDD,
class TLeft, class TRight,
class TLeftTraits = ATL::CControlWinTraits,
class TRightTraits = ATL::CControlWinTraits,
class TSplitDialogTraits = CSplitDlgTraits
>
class CVSplitDialog: public CSplitDialogImpl<
CVSplitDialog<t_uIDD, TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits>,
t_uIDD,
CSplitterImpl<
CVSplitDialog<t_uIDD, TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits>,
true
>,
TLeft, TRight, TLeftTraits, TRightTraits,
CEmptyDialogImpl<
CVSplitDialog<t_uIDD, TLeft, TRight,
TLeftTraits, TRightTraits,TSplitDialogTraits>, t_uIDD,
CEmptyDlgTemplate<t_uIDD,
TSplitDialogTraits>
>
>
{
typedef CVSplitDialog<t_uIDD, TLeft, TRight, TLeftTraits,
TRightTraits, TSplitDialogTraits> thisClass;
public:
CVSplitDialog(){}
CVSplitDialog(SIZE size, bool bDLU = true): SplitDialog(size, bDLU){}
CVSplitDialog(POINT point, SIZE size,
bool bDLU = true): SplitDialog(point, size, bDLU){}
CVSplitDialog(RECT rect, bool bDLU = true): SplitDialog(rect, bDLU){}
};
CHSplitDialog
is the same with false
as CSplitterImpl
second template parameter.
Sample Usage
CMyFileDialog, CMyShellDialog.
To compile these classes your development platform needs access to The Microsoft� Windows� Software Development Kit for Windows Vista� and your project must #include WtlAero.h.
See Have a glass with WTL! for the description of the WTL::aero
namespace classes.
aero::CEmptyDialogImpl
is a WTL::CEmptyDialogImpl using aero::CDialogImpl as TBase
template parameter.
If a toolbar is attached to the t_uIDD
template parameter, it must use a 32bit bitmap. Note that this is not a Vista requirement, but the WTL8.0 code will not load the toolbar bitmap with correct flags for aero operation if it is not 32 bits color.
Definitions
template
< class T,
UINT t_uIDD,
class TDlgTemplate
= CEmptyDlgTemplate<t_uIDD, CEmptyDlgTraits>
>
class ATL_NO_VTABLE CEmptyDialogImpl: public WTL::CEmptyDialogImpl<
T,
t_uIDD,
TDlgTemplate,
aero::CDialogImpl<T>
>
{
typedef aero::CEmptyDialogImpl<T, t_uIDD, TDlgTemplate> thisClass;
public:
typedef aero::CDialogImpl<T> AeroDialog;
typedef WTL::CEmptyDialogImpl<T, t_uIDD,
TDlgTemplate, AeroDialog> BaseEmptyDialog;
typedef thisClass EmptyDialog;
Constructors
Same as WTL::CEmptyDialogImpl.
Data Members
The aero::CToolBarCtrl m_ATB
member is used for subclassing the toolbar when running under Vista.
Default Operation: Overrideable Members
-
bool aero::CEmptyDialogImpl::Init(LPARAM lParam)
performs WTL::CEmptyDialogImpl::Init(LPARAM lParam)
and if a toolbar is present conditionally subclasses it to a aero::CToolBarCtrl
.
-
void aero::CEmptyDialogImpl::Paint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest)
does nothing. Note that this is the member you should override or specialize if you want to paint the empty dialog area.
aero::CControlDialog
is the aero enabled equivalent of WTL::CControlDialog. It derives from WTL::CControlDialogImpl and indirectly through the TDlgImpl
template parameter from aero::CEmptyDialogImpl.
The TCtrl
class must be aero enabled to have a correct rendering under Vista.
Definition and Construction
template
< UINT t_uIDD,
menu (if matching resources exist)
class TCtrl,
class TControlTraits
= ATL::CControlWinTraits,
class TControlDlgTraits
= CControlDlgTraits
>
class CControlDialog: public WTL::CControlDialogImpl<
aero::CControlDialog<t_uIDD, TCtrl, TControlTraits,
TControlDlgTraits>,
t_uIDD,
TCtrl,
TControlTraits,
aero::CEmptyDialogImpl<
aero::CControlDialog<t_uIDD, TCtrl,
TControlTraits, TControlDlgTraits>, t_uIDD,
CEmptyDlgTemplate<t_uIDD,
TControlDlgTraits>
>
>
{
typedef aero::CControlDialog<t_uIDD, TCtrl, TControlTraits,
TControlDlgTraits> thisClass;
public:
CControlDialog(){}
CControlDialog(SIZE size,
bool bDLU = true): ControlDialog(size, bDLU){}
CControlDialog(POINT point,
SIZE size, bool bDLU = true): ControlDialog(point, size, bDLU){}
CControlDialog(RECT rect,
bool bDLU = true): ControlDialog(rect, bDLU){}
};
aero::CSplitDialog
is the aero enabled equivalent of WTL::CSplitDialog. It derives from WTL::CSplitDialogImpl and indirectly through the TDlgImpl
template parameter from aero::CEmptyDialogImpl.
Both TLeft
and TRight
classes must be aero enabled to have a correct rendering under Vista.
Definition and Construction
template
< UINT t_uIDD,
class TSplitImpl,
class TLeft,
class TRight,
class TLeftTraits
= CSplitControlTraits,
class TRightTraits
= CSplitControlTraits,
class TSplitDialogTraits
= CSplitDlgTraits
>
class CSplitDialog: public WTL::CSplitDialogImpl<
aero::CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits>,
t_uIDD,
TSplitImpl,
TLeft,
TRight,
TLeftTraits,
TRightTraits,
aero::CEmptyDialogImpl<
aero::CSplitDialog<t_uIDD, TSplitImpl,
TLeft, TRight,
TLeftTraits, TRightTraits,
TSplitDialogTraits>,
t_uIDD,
CEmptyDlgTemplate<t_uIDD,
TSplitDialogTraits>
>
>
{
typedef aero::CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight,
TLeftTraits, TRightTraits, TSplitDialogTraits> thisClass;
public:
CSplitDialog(){}
CSplitDialog(SIZE size, bool bDLU = true): SplitDialog(size, bDLU){}
CSplitDialog(POINT point,
SIZE size, bool bDLU = true): SplitDialog(point, size, bDLU){}
CSplitDialog(RECT rect, bool bDLU = true): SplitDialog(rect, bDLU){}
};
Sample Code
CAeroShellDialog
These classes derive from CStdDialogImpl
in atlwince.h, so they are the mobile full screen equivalent to the all-purpose classes.
-
CStdEmptyDialogImpl is the only class with operating code.
-
CStdControlDialog
, CStdSplitDialog
, CStdHSplitDialog
, CStdVSplitDialog
have only definition code.
CStdEmptyDialogImpl
is a specialized CEmptyDialogImpl using CStdDialogImpl
as TBase
template parameter.
Definition
typedef ATL::CWinTraits<WS_VISIBLE |
DS_CENTER | WS_POPUP> CStdEmptyDlgTraits;
template <UINT t_uIDS>
class CStdEmptyDlgTemplate: public CEmptyDlgTemplate<t_uIDS,
CStdEmptyDlgTraits>
{};
template
< class T,
UINT t_uIDD,
UINT t_shidiFlags
= WTL_STD_SHIDIF,
class TDlgTemplate
= CStdEmptyDlgTemplate<t_uIDD>
>
class ATL_NO_VTABLE CStdEmptyDialogImpl: public CEmptyDialogImpl<
T,
t_uIDD,
TDlgTemplate,
CStdDialogImpl<T, t_shidiFlags, true>
>
{
typedef CStdEmptyDialogImpl<T, t_uIDD, t_shidiFlags,
TDlgTemplate> thisClass;
public:
typedef CStdDialogImpl<T, t_shidiFlags, true> Std;
typedef thisClass EmptyDialog;
typedef CEmptyDialogImpl<T, t_uIDD,
TDlgTemplate, Std> BaseEmptyDialog;
Constructor
CStdEmptyDialogImpl
has a single argumentless constructor, all sizing is handled by the t_shidiFlags
template parameter.
Data Members
None.
Default Operation: Overrideable Members
CStdEmptyDialogImpl::Init(LPARAM lParam)
returns CStdEmptyDialogImpl::DefInit(LPARAM lParam)
.
All other default operations are handled by CEmptyDialogImpl
accessible with BaseEmptyDialog::
name.
Helper Members
-
DefInit()
, the default initialization member copies the DoModal() lParam
to m_Data
, frees the DLGTEMPLATE
memory, creates a MenuBar (from the t_uIDD SHMENUBAR
resource if found, from ATL_IDM_MENU_DONECANCEL
otherwise), and finally performs CStdDialogBase
initializations. DefInit()
returns false for eVC/ATL3 compatibility.
MenuBar command buttons helpers:
-
BOOL SetMenuBarCommand(INT iID, LPCTSTR psText, bool bRight = false)
-
BOOL SetMenuBarCommand(INT iID, bool bRight = false)
-
BOOL SetMenuBarCommand(UINT uOldId, UINT uNewId, LPTSTR sText = NULL)
Derived Classes
CStdControlDialog
, CStdSplitDialog
, CStdVSplitDialog
, CStdHSplitDialog
are indirectly derived from CStdEmptyDialogImpl
.
CStdControlDialog
is a CControlDialog
using CStdEmptyDialogImpl
as TDlgImpl
template parameter.
Definitions
template
< UINT t_uIDD,
class TCtrl,
class TControlTraits
= ATL::CControlWinTraits,
UINT t_shidiFlags
= WTL_STD_SHIDIF,
class TDlgTemplate
= CStdEmptyDlgTemplate<t_uIDD>
>
class CStdControlDialog: public CControlDialogImpl<
CStdControlDialog<t_uIDD, TCtrl, TControlTraits,
t_shidiFlags, TDlgTemplate>,
t_uIDD,
TCtrl,
TControlTraits,
CStdEmptyDialogImpl<
CStdControlDialog<t_uIDD, TCtrl, TControlTraits,
t_shidiFlags, TDlgTemplate>, t_uIDD,
t_shidiFlags,
TDlgTemplate
>
>
{
typedef CStdControlDialog<t_uIDD, TCtrl, TControlTraits, t_shidiFlags,
TDlgTemplate> thisClass;
};
Sample Code
CMyDateDialog
CStdSplitDialog
is a specialized CSplitDialog using CStdEmptyDialogImpl as TDlgImpl
template parameter.
Definitions
typedef ATL::CWinTraitsOR<0, WS_EX_CLIENTEDGE,
CStdEmptyDlgTraits> CStdSplitDlgTraits;
template <UINT t_uIDS>
class CStdSplitDlgTemplate: public CEmptyDlgTemplate<t_uIDS,
CStdSplitDlgTraits>
{};
template
< UINT t_uIDD,
class TSplitImpl,
class TLeft,
class TRight,
class TLeftTraits
= CSplitControlTraits,
class TRightTraits
= CSplitControlTraits,
UINT t_shidiFlags
= WTL_STD_SHIDIF,
class TDlgTemplate
= CStdSplitDlgTemplate<t_uIDD>
>
class CStdSplitDialog: public CSplitDialogImpl<
CStdSplitDialog<t_uIDD,
TSplitImpl, TLeft, TRight,
TLeftTraits, TRightTraits, t_shidiFlags, TDlgTemplate>,
t_uIDD,
TSplitImpl,
TLeft,
TRight,
TLeftTraits,
TRightTraits,
CStdEmptyDialogImpl <
CStdSplitDialog<t_uIDD,
TSplitImpl, TLeft,
TRight, TLeftTraits, TRightTraits, t_shidiFlags,
TDlgTemplate>,
t_uIDD,
t_shidiFlags,
TDlgTemplate
>
>
{
typedef CStdSplitDialog<t_uIDD, TSplitImpl, TLeft,
TRight, TLeftTraits,
TRightTraits, t_shidiFlags, TDlgTemplate> thisClass;
};
CStdHSplitDialog
and CStdVSplitDialog
are specialized CHSplitDialog
and CVSplitDialog using CStdEmptyDialogImpl as TDlgImpl
template parameter.
Definition
template
< UINT t_uIDD,
class TLeft, class TRight,
class TLeftTraits = CSplitControlTraits,
class TRightTraits = CSplitControlTraits,
UINT t_shidiFlags = WTL_STD_SHIDIF,
class TDlgTemplate = CStdSplitDlgTemplate<t_uIDD>
>
class CStdHSplitDialog: public CSplitDialogImpl<
CStdHSplitDialog<t_uIDD, TLeft, TRight,
TLeftTraits, TRightTraits, t_shidiFlags, TDlgTemplate>,
t_uIDD,
CSplitterImpl<
CStdHSplitDialog<t_uIDD, TLeft, TRight,
TLeftTraits, TRightTraits,
t_shidiFlags, TDlgTemplate>,
false
>,
TLeft,
TRight,
TLeftTraits,
TRightTraits,
CStdEmptyDialogImpl <
CStdHSplitDialog<t_uIDD, TLeft, TRight,
TLeftTraits, TRightTraits,
t_shidiFlags, TDlgTemplate>,
t_uIDD,
t_shidiFlags,
TDlgTemplate
>
>
{
typedef CStdHSplitDialog<t_uIDD, TLeft, TRight, TLeftTraits,
TRightTraits, t_shidiFlags, TDlgTemplate> thisClass;
};
Sample Code
CMyFileDialog
Three similar DialogX applications shows applicative usage of the referenced classes, a pre-Vista and a Vista-enabled desktop application, and a sibling Windows Mobile application targetting -with platform adaptations- all mobile devices from SmartPhone 2003 and PPC 2002 to WM6.
To access the samples, download and expand DialogX.zip to your choice of <location>.
VS2005 and VCExpress: Open <location>\DialogX\DialogX.sln to work with <location>\DialogX\Desktop\Desktop.vcproj and (VS2005 only) <location>\DialogX\Mobile\DialogX.vcproj.
VC.NET 2003: Open <location>\DialogX\Desktop\DialogX.sln to work with <location>\DialogX\Desktop\DialogX.vcproj.
eVC 3/4: Open <location>\DialogX\Mobile\DialogX.vcw to work with <location>\DialogX\Mobile\DialogX.vcp.
VC6 or VC.NET 2002: Create with the WTL AppWizard a simple SDI DialogX project without CPP files and without view class, copy the generated project file to <location>\DialogX\Desktop\ and open it.
Much of the applicative code for the three samples is common, so it is grouped in <location>\DialogX\DialogX.h.
CMyToolMenu
instantiates a CCellMenu with the toolbar BITMAP
resource.
typedef CCellMenu<IDR_MAINFRAME32, 6, 1> CMyToolMenu;
CMainFrame::ToolMenu(POINT pt)
calls CMyToolMenu::TrackCellMenu(pt)
using the provided POINT
with default TPM_LEFTALIGN | TPM_TOPALIGN
positioning. When 32 bit colors are not suported the 8 bit colors CCellMenu<IDR_MAINFRAME, 6, 1>
is used instead.
void ToolMenu(POINT pt)
{
static const UINT commands[] = {ID_SHELLDIALOG, ID_DATEDIALOG,
ID_COLORMENU, ID_EDIT_STATUS, ID_TOOLMENU, ID_APP_ABOUT};
int iSel;
if (RunTimeHelper::IsCommCtrl6())
iSel = CMyToolMenu::TrackCellMenu(pt);
else
iSel = CCellMenu<IDR_MAINFRAME, 6, 1>::TrackCellMenu(pt);
if(iSel != -1)
PostMessage(WM_COMMAND, commands[iSel]);
}
CMyColorMenu
specializes CCellMenu::PaintCell
to paint a color table.
#ifndef ID_COLORMENU
#define ID_COLORMENU 1001
#endif
typedef CCellMenu<ID_COLORMENU, 8, 5> CMyColorMenu;
__declspec(selectany) COLORREF colors[] =
{
};
void CMyColorMenu::PaintCell(HDC hdc, CELL& cell)
{
CBrush br = CreateSolidBrush(colors[cell.Index()]);
CDCHandle dc(hdc);
CBrushHandle brOld = dc.SelectBrush(br);
RECT rPaint = cell.Rect(true);
dc.Rectangle(&rPaint);
dc.SelectBrush(brOld);
}
CMainFrame::ColorMenu(POINT pt)
calls CMyColorMenu::TrackCellMenu(pt, TPM_TOPALIGN, m_icolor)
with the index of the current background color and updates the background if a selection occured.
void ColorMenu(POINT pt)
{
int iSel = CMyColorMenu::TrackCellMenu(pt, TPM_TOPALIGN, m_icolor);
if (iSel != -1)
{
m_icolor = iSel;
Invalidate();
}
}
CMyDateDialog
instantiates a CControlDialog with month calendar control specialization.
The specialized bool CMyDateDialog::ControlDialog::Notify(int, LPNMHDR pnmh)
processes the Month calendar control's MCN_SELECT
notification as a validation.
The specialized bool CMyDateDialog::ControlDialog::Command(WORD , WORD wID, HWND )
copies the selected date to m_Data
.
#ifndef ID_DATEDIALOG
#define ID_DATEDIALOG 1000
#endif
typedef CWinTraitsOR<MCS_NOTODAY> CMyDateCtrlTraits;
#ifdef _WIN32_WCE
typedef CStdControlDialog<ID_DATEDIALOG,
CMonthCalendarCtrl, CMyDateCtrlTraits>
CMyDateDialog;
#else
typedef CControlDialog<ID_DATEDIALOG, CMonthCalendarCtrl, CMyDateCtrlTraits>
CMyDateDialog;
bool CMyDateDialog::ControlDialog::Init(LPARAM lParam)
{
ATLASSERT(!lParam || !IsBadWritePtr((LPVOID)lParam,
sizeof(SYSTEMTIME)));
DefInit(lParam);
CRect rCtrl;
m_Ctrl.GetMinReqRect(rCtrl);
ResizeClient(rCtrl.Width(), rCtrl.Height());
CenterWindow(GetParent());
return true;
};
#endif
bool CMyDateDialog::ControlDialog::Notify(int, LPNMHDR pnmh)
{
if(pnmh->code == MCN_SELECT)
PostMessage(WM_COMMAND, IDOK);
return false;
};
bool CMyDateDialog::ControlDialog::Command(WORD , WORD wID,
HWND )
{
if((wID == IDOK) && m_Data)
m_Ctrl.GetCurSel((LPSYSTEMTIME)m_Data);
return false;
};
CMainFrame::DateDialog()
instantiates a CMyDateDialog
and a SYSTEMTIME
structure, calls DoModal()
with the address of the SYSTEMTIME
and updates the status bar pane 1 if a selection occurred.
void DateDialog()
{
CMyDateDialog dlg(CSize(150, 150));
SYSTEMTIME st = {0};
if(dlg.DoModal(m_hWnd, (LPARAM)&st) == IDOK)
{
TCHAR szDate[32] = {0};
::GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st,
NULL, szDate, 32);
CStatusBarCtrl(m_hWndStatusBar).SetText(1, szDate);
}
}
#define STATUS_LENGTH 127
typedef CInPlaceEditor<STATUS_LENGTH> CPaneEditor;
CStatusPaneEditor
is a simple class constructed from a status bar control HWND
, with a single member: bool EditPane(int iPane)
.
EditPane
computes a suitable rectangle for editing the pane, gets the pane text, calls CPaneEditor::Edit
with these parameters, and updates the pane with the edited text if edition occurred.
class CStatusPaneEditor
{
public:
CStatusPaneEditor(HWND hWndStatusBar): m_sb(hWndStatusBar)
{}
CStatusBarCtrl m_sb;
bool EditPane(int iPane)
{
ATLASSERT(m_sb.IsWindow());
CRect rPane;
ATLVERIFY(m_sb.GetRect(iPane, rPane));
rPane.InflateRect(-1,-1);
rPane.OffsetRect(0,-1);
m_sb.MapWindowPoints(m_sb.GetParent(), rPane);
CTempBuffer<TCHAR> sPane(STATUS_LENGTH);
m_sb.GetText(iPane, sPane);
bool bRes = CPaneEditor::Edit(rPane, sPane);
if (bRes)
m_sb.SetText(iPane, sPane);
return bRes;
}
};
CMainFrame::EditStatusPane(int iPane)
instantiates a CStatusPaneEditor
with m_hWndStatusBar
and calls EditPane(iPane)
.
void EditStatusPane(int iPane)
{
CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane);
}
CMyShellDialog
is a CVSplitDialog using, with his permission, Bjarke Viks�e's Shell controls.
typedef CWinTraitsOR
<TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT |
TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER |
WS_TABSTOP> CShellTreeTraits;
#ifndef LVS_EX_DOUBLEBUFFER
#define LVS_EX_DOUBLEBUFFER 0
#endif
typedef CWinTraitsOR
<LVS_SINGLESEL | LVS_SHOWSELALWAYS |
LVS_AUTOARRANGE | LVS_NOCOLUMNHEADER | WS_BORDER |
WS_TABSTOP, LVS_EX_DOUBLEBUFFER> CShellListTraits;
#if !defined __WTL_AERO_H__
typedef CVSplitDialog<ID_SHELLDIALOG, CShellTreeCtrl, CShellListCtrl,
CShellTreeTraits, CShellListTraits> CMyShellDialog;
#else
The ID_SHELLDIALOG TOOLBAR
resource implements CSplitDialogImpl
handled commands.
ID_SHELLDIALOG TOOLBAR 16, 16
BEGIN
BUTTON ID_WINDOW_SPLIT
BUTTON ID_NEXT_PANE
BUTTON ID_PREV_PANE
BUTTON IDOK
END
CMyShellDialog
specializes three SplitDialog
members:
bool CMyShellDialog::SplitDialog::Init(LPARAM lParam)
{
ATLASSERT(!lParam ||
!IsBadWritePtr((LPVOID)lParam, MAX_PATH * sizeof(TCHAR)));
SplitDialog::DefInit(lParam);
#ifdef __ATLTHEME_H__
m_Left.SetWindowTheme(L"explorer", NULL);
m_Right.SetWindowTheme(L"explorer", NULL);
#endif
m_Right.SetExtendedListViewStyle(CShellListTraits::GetWndExStyle(0));
m_Left.Populate();
SetActivePane(SPLIT_PANE_LEFT);
SetSplitterPosPct(40);
CToolBarCtrl tb(m_hWndToolBar);
#if (_WIN32_WINNT >= 0x0501)
tb.SetWindowTheme(L"explorer");
#endif
tb.CheckButton(ID_WINDOW_SPLIT, TRUE);
tb.EnableButton(ID_NEXT_PANE, TRUE);
tb.EnableButton(ID_PREV_PANE, FALSE);
return true;
}
bool CMyShellDialog::SplitDialog::Command(WORD , WORD wID,
HWND )
{
if (PaneCommand(wID))
{
if (wID == ID_WINDOW_SPLIT)
CToolBarCtrl(m_hWndToolBar).CheckButton(
ID_WINDOW_SPLIT,
GetSinglePaneMode() == SPLIT_PANE_NONE);
return true;
}
else if ((wID == IDOK) && m_Data)
{
int iItem = m_Right.GetSelectedIndex();
if (!m_Right.GetItemPath(iItem, (LPTSTR)m_Data))
m_Right.GetItemText(iItem, 0,
(LPTSTR)m_Data, MAX_PATH);
}
return false;
}
bool CMyShellDialog::SplitDialog::Notify(int idCtrl, LPNMHDR pnmh)
{
switch(pnmh->code)
{
case NM_SETFOCUS:
SetDefaultActivePane(pnmh->hwndFrom);
{
CToolBarCtrl tb(m_hWndToolBar);
tb.EnableButton(ID_NEXT_PANE,
GetDefaultActivePane() == SPLIT_PANE_RIGHT);
tb.EnableButton(ID_PREV_PANE,
GetDefaultActivePane() == SPLIT_PANE_LEFT);
}
break;
case NM_CLICK:
if (idCtrl != ATL_IDM_WINDOW_LAST)
return false;
else
{
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)pnmh;
CPidl pidl;
m_Right.GetItemPidl(pnmlv->iItem, πdl);
m_Left.SelectPidl(pidl);
break;
}
case TVN_SELCHANGED:
{
CWaitCursor cursor;
CPidl pidl;
m_Left.GetItemPidl((
(LPNMTREEVIEW)pnmh)->itemNew.hItem,
pidl);
m_Right.Populate(pidl);
m_Right.SelectItem(0);
}
break;
default:
return false;
}
return true;
}
CBjarkeShellDialog
derives from CMyShellDialog and implements a right pane context menu.
class CBjarkeShellDialog: public CMyShellDialog
{
public:
CBjarkeShellDialog(SIZE sz): CMyShellDialog(sz)
{}
CExplorerMenu m_menu;
BEGIN_MSG_MAP(CBjarkeShellDialog)
MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
CHAIN_MSG_MAP_MEMBER(m_menu)
CHAIN_MSG_MAP(CMyShellDialog)
END_MSG_MAP()
LRESULT OnContextMenu(UINT , WPARAM , LPARAM lParam,
BOOL& )
{
if (::GetFocus() == m_Right)
{
CPidl pidl;
if (m_Right.GetItemPidl(m_Right.GetSelectedIndex(), πdl)
!= FALSE)
m_menu.TrackPopupMenu(pidl, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam), m_hWnd);
}
return 0;
}
};
void CMainFrame::ShellDialog()
allocates a CString
of size MAX_PATH
, instantiates a CBjarkeShellDialog
and calls DoModal
with the string as lParam
. On return, the status bar pane 2 is updated with the selected path if selection occurred.
void ShellDialog()
{
int iRet = IDCANCEL;
CString sSelect;
LPTSTR sbuf = sSelect.GetBuffer(MAX_PATH);
#if (_WIN32_WINNT >= 0x0600) && defined(NTDDI_VERSION) && \
(NTDDI_VERSION >= NTDDI_LONGHORN)
endif
(NTDDI_VERSION >= NTDDI_LONGHORN)
{
CBjarkeShellDialog dlg(CSize(600, 400));
iRet = dlg.DoModal(m_hWnd, (LPARAM)sbuf);
}
sSelect.ReleaseBuffer();
if (iRet == IDOK)
CStatusBarCtrl(m_hWndStatusBar).SetText(2, sSelect);
}
When this sample runs under Vista, the Explore command allows user to select between an aero enabled CBjarkeShellDialog
or a CMyVistaShellDialog
using the Vista IExplorerBrowser
interface. Under pre-Vista systems there is no difference.
CMyVistaShellDialog
is a CEmptyDialogImpl operating through the Vista IExplorerBrowser
interface.
#ifndef ID_VISTA_SHELLDIALOG
#ifndef ID_SHELLDIALOG
#define ID_VISTA_SHELLDIALOG 1000
#else
#define ID_VISTA_SHELLDIALOG ID_SHELLDIALOG
#endif
#endif
typedef CEmptyDlgTemplate<ID_VISTA_SHELLDIALOG, CSplitDlgTraits>
CVistaShellDlgTemplate;
class CMyVistaShellDialog: public CEmptyDialogImpl<CMyVistaShellDialog,
ID_VISTA_SHELLDIALOG, CVistaShellDlgTemplate>
{
public:
CMyVistaShellDialog(SIZE size): EmptyDialog(size, true)
{}
CComPtr<IExplorerBrowser> m_pIEB;
For better user interaction, CMyVistaShellDialog
should implement the IExplorerBrowserEvents
interface, which is out of this article bounds, and left to you as a homework.
CAeroShellTreeCtrl
and CAeroShellListCtrl
are aero::CCtrlImpl adaptations of Bjarke Viks�e's CShellTreeCtrl
and CShellListCtrl
.
#if !defined __WTL_AERO_H__
#else
class CAeroShellTreeCtrl: public aero::CCtrlImpl<CAeroShellTreeCtrl,
CShellTreeCtrl>
{
public:
CAeroShellTreeCtrl(): CCtrlImpl(VSCLASS_TREEVIEW)
{}
void DoPaint(HDC hdc, RECT& rect)
{
DefWindowProc(WM_PAINT, (WPARAM) hdc, NULL);
if (!m_BufferedPaint.IsNull())
m_BufferedPaint.MakeOpaque(&rect);
}
};
class CAeroShellListCtrl: public aero::CCtrlImpl<CAeroShellListCtrl,
CShellListCtrl>
{
public:
CAeroShellListCtrl(): CCtrlImpl(VSCLASS_LISTVIEW)
{}
void DoPaint(HDC hdc, RECT& rect)
{
DefWindowProc(WM_PAINT, (WPARAM) hdc, NULL);
if (!m_BufferedPaint.IsNull())
m_BufferedPaint.MakeOpaque(&rect);
}
};
CAeroShellDialog
instanciates aero::CSplitDialog using aero::CSplitterImpl<CAeroShellDialog, true>
as TSplitImpl
parameter, CAeroShellTreeCtrl
as TLeft
parameter, and CAeroShellListCtrl
as TRight
parameter.
typedef class CAeroShellDialog: public aero::CSplitDialog<
ID_VISTASHELLDIALOG,
aero::CSplitterImpl<
CAeroShellDialog,
true
>,
CAeroShellTreeCtrl, CAeroShellListCtrl,
CShellTreeTraits, CShellListTraits
>
{
public:
CAeroShellDialog(SIZE size): CSplitDialog(size)
{}
} CMyShellDialog;
#endif
When compiled with the Vista SDK, and running under Vista, CMainFrame::ShellDialog()
calls int CMainFrame::SelectShellDialog()
which instantiates a WTL::CTaskDialog
allowing selection of one of CBjarkeShellDialog or CMyVistaShellDialog.
#if (_WIN32_WINNT >= 0x0600) && defined(NTDDI_VERSION) && \
(NTDDI_VERSION >= NTDDI_LONGHORN)
int SelectShellDialog()
{
CTaskDialog td(m_hWnd);
td.ModifyFlags(0, TDF_POSITION_RELATIVE_TO_WINDOW |
TDF_USE_COMMAND_LINKS);
td.SetWindowTitle(ID_VISTA_SHELLDIALOG);
td.SetMainIcon(TD_INFORMATION_ICON);
td.SetMainInstructionText(L"Select shell browser dialog:");
TASKDIALOG_BUTTON tdb[2] =
{
{ID_SHELLDIALOG, L"CBjarkeShellDialog"},
{ID_SHELLDIALOG + 1, L"CMyVistaShellDialog"}
};
td.SetButtons(tdb,2);
td.SetCommonButtons(TDCBF_CANCEL_BUTTON);
int iRes = 0;
ATLVERIFY(td.DoModal(m_hWnd, &iRes) == S_OK);
return iRes == IDCANCEL ? -1: iRes - ID_SHELLDIALOG;
}
#endif
(NTDDI_VERSION >= NTDDI_LONGHORN)
void ShellDialog()
{
int iRet = IDCANCEL;
CTempBuffer<TCHAR> sbuf(MAX_PATH);
#if (_WIN32_WINNT >= 0x0600) && defined(NTDDI_VERSION) && \
(NTDDI_VERSION >= NTDDI_LONGHORN)
int iSel =
RunTimeHelper::IsVista() ? SelectShellDialog(): 0;
if (iSel == -1)
return;
else if (iSel == 1)
{
CMyVistaShellDialog dlg(CSize(600, 400));
iRet = dlg.DoModal(m_hWnd, (LPARAM)(LPTSTR)sbuf);
}
else
#endif
(NTDDI_VERSION >= NTDDI_LONGHORN)
{
CBjarkeShellDialog dlg(CSize(600, 400));
iRet = dlg.DoModal(m_hWnd, (LPARAM)(LPTSTR)sbuf);
}
if (iRet == IDOK)
CStatusBarCtrl(m_hWndStatusBar).SetText(2, sbuf);
}
This sample is a functional equivalent of the desktop DialogX, tagetting all Windows Mobile devices from PPC 2002 and SmartPhone 2003 to Windows Mobile 6. Some classes are slightly different, but the main differences are in the frame window code.
CMyToolMenu
instantiates a CCellMenu with the toolbar BITMAP
resource and, when compiled with VS 2005, uses the DRA namespace functions to scale the cells and bitmap according to screen resolution.
#ifndef _WIN32_WCE
#else
typedef CCellMenu<IDR_MAINFRAME, 6, 1> CMyToolMenu;
#ifdef _WTL_CE_DRA
const SIZE CMyToolMenu::CellSize()
{
SIZE size;
CBitmap bm = AtlLoadBitmap(IDR_MAINFRAME);
ATLASSERT(!bm.IsNull());
SIZE sbm;
bm.GetSize(sbm);
size.cx = DRA::SCALEX(sbm.cx / (t_nCol * t_nRow)) + 2 * CELL::Cxy();
size.cy = DRA::SCALEY(sbm.cy) + 2 * CELL::Cxy();
return size;
}
CImageList& CMyToolMenu::GetImageList()
{
static CImageList iml;
if (iml.IsNull())
iml = DRA::ImageList_LoadImage(
ModuleHelper::GetResourceInstance(),
MAKEINTRESOURCE(IDR_MAINFRAME),
DRA::UNSCALEX(CELL::Size().cx - 2 * CELL::Cxy()),
0, CLR_DEFAULT, IMAGE_BITMAP, 0);
ATLASSERT(!iml.IsNull());
return iml;
}
#endif
#endif
CDialogXFrame::ToolMenu(POINT pt)
calls CMyToolMenu::TrackCellMenu(pt)
using the provided POINT
with TPM_BOTTOMALIGN
positioning.
void ToolMenu(POINT pt)
{
static const UINT commands[] =
{
ID_FILEDIALOG, ID_DATEDIALOG, ID_COLORMENU,
ID_EDIT_STATUS, ID_TOOLMENU, ID_APP_ABOUT
};
int iSel = CMyToolMenu::TrackCellMenu(pt, TPM_BOTTOMALIGN);
if(iSel != -1)
PostMessage(WM_COMMAND, commands[iSel]);
}
See the desktop CMyColorMenu description.
CDialogXFrame::ColorMenu()
computes the bottom center point, pushes the ID_COLORMENU
button (exists only on PPC2003), calls CMyColorMenu::TrackCellMenu(pt, TPM_CENTERALIGN | TPM_BOTTOMALIGN, m_icolor)
with the index of the current background color, releases the button and updates the background if a selection occurred.
void ColorMenu()
{
CRect rect;
GetClientRect(rect);
CPoint pt(rect.Size().cx / 2, rect.bottom);
ClientToScreen(&pt);
CMenuBarCtrl mb(m_hWndCECommandBar);
mb.PressButton(ID_COLORMENU, TRUE);
int iSel = CMyColorMenu::TrackCellMenu(pt,
TPM_CENTERALIGN | TPM_BOTTOMALIGN, m_icolor);
mb.PressButton(ID_COLORMENU, FALSE);
if (iSel != -1)
{
m_icolor = iSel;
Invalidate();
}
}
The only difference with the desktop implementation is that CMyDateDialog
instantiates a CStdControlMenu with month calendar control specialization.
The mobile CPaneEditor
uses a specific CEditPaneCtrl
to deal with some WM5 differences in EDIT
control behavior, and allow up an down keys to escape.
On PPC platforms CPaneEditor
requests the SIP from the system.
#define STATUS_LENGTH 127
#ifdef _WIN32_WCE
class CEditPaneCtrl: public CWindowImpl<CEditPaneCtrl, CEdit>
{
public:
DECLARE_WND_SUPERCLASS(L"EditCtrl", CEdit::GetWndClassName())
BEGIN_MSG_MAP(CEditPaneCtrl)
MESSAGE_RANGE_HANDLER(WM_KEYFIRST, WM_KEYLAST, OnKey)
END_MSG_MAP()
LRESULT OnKey(UINT uMsg, WPARAM wParam, LPARAM ,
BOOL& bHandled)
{
if (uMsg == WM_KEYUP)
switch (wParam)
{
case VK_TUP:
case VK_TDOWN:
::PostMessage(GetParent(), WM_COMMAND,
IDCANCEL, 0);
break;
#if defined WIN32_PLATFORM_PSPC
case VK_RETURN:
::PostMessage(GetParent(), WM_COMMAND,
IDOK, 0);
break;
#elif defined WIN32_PLATFORM_WFSP
case VK_TBACK:
SendMessage(WM_CHAR, VK_BACK);
#endif
}
bHandled = FALSE;
return 1;
}
};
typedef CInPlaceEditor<STATUS_LENGTH, CEditPaneCtrl> CPaneEditor;
#ifdef WIN32_PLATFORM_PSPC
bool CPaneEditor::Init(LPARAM lParam)
{
SHSipPreference(m_hWnd, SIP_UP);
return CInPlaceEditor::DefInit(lParam);
}
#endif
For PPC platforms CDialogXFrame::EditStatusPane(int iPane)
raises the SIP before calling CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane)
and lowers it afterwards.
class CDialogXFrame:
{
void EditStatusPane(int iPane)
{
#ifdef WIN32_PLATFORM_PSPC
SHSipPreference(m_hWnd, SIP_UP);
CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane);
SHSipPreference(m_hWnd, SIP_DOWN);
#else
CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane);
#endif
}
CFileTreeCtl and CFileListCtrl
These implementations of subclassed WTL::CTreeViewCtrlEx
and WTL::CSortListViewCtrlImpl
showing file items are very similar to many other, except for the TFilter
and TFinder
template parameters. Both classes require:
-
a class TFilter
template parameter which can be any class exposing bool operator()(TFinder& ff)
. This design makes it very easy to select files on any condition, including runtime conditions, external permissions or file content.
-
a class TFinder
template parameter which defaults to WTL::CFindFile
, but may be a similar or derived class accessing files through different ways (such as the device remote API CeFindxxxFile()
).
template <class TFilter, class TFinder = WTL::CFindFile>
class CFileTreeCtrlF: public CWindowImpl<CFileTreeCtrlF<TFilter,
TFinder>, CTreeViewCtrlEx>
{
typedef CFileTreeCtrlF<CDirFilter> CDirTreeCtrl;
template <class TFilter, class TFinder = WTL::CFindFile>
class CFileListCtrlF: public CSortListViewCtrlImpl<
CFileListCtrlF< TFilter, TFinder> >
{
typedef CFileListCtrlF<CFileFilter> CFileListCtrl;
Both classes are located in <location>\DialogX\Device\FileControls.h and are not actually restricted to Windows Mobile platforms.
CMyFileDialog
is a CStdHSplitDialog using CDirTreeCtrl
as TLeft
parameter, and CFileListCtrl
as TRight
parameter.
#if _WIN32_WCE < 0x500 && defined(WIN32_PLATFORM_WFSP)
typedef CFileTreeTraits CFileDialogTreeTraits;
typedef CFileListTraits CFileDialogListTraits;
#else
typedef CWinTraitsOR<WS_TABSTOP, 0, CFileTreeTraits> CFileDialogTreeTraits;
typedef CWinTraitsOR<WS_TABSTOP, 0, CFileListTraits> CFileDialogListTraits;
#endif
typedef CStdHSplitDialog<ID_FILEDIALOG, CDirTreeCtrl, CFileListCtrl,
CFileDialogTreeTraits, CFileDialogListTraits> CMyFileDialog;
CMyFileDialog
specializes, on PPC platforms one EmptyDialog
member, and always three SplitDialog
members:
-
For PPC platforms, bool CMyFileDialog::EmptyDialog::Init(LPARAM lParam)
loads a graphic MenuBar, if running on a PPC 2002/2003 platform, before default SplitDialog
initializations..
-
bool CMyFileDialog::SplitDialog::Command(WORD , WORD wID, HWND )
copies the selected path to m_Data
if the command is IDOK
, returns SplitDialog::PaneCommand(wID)
otherwise.
-
bool CMyFileDialog::SplitDialog::Notify(int idCtrl, LPNMHDR pnmh)
uses NM_SETFOCUS
to adjust the PPC2003 menu bar buttons and the other platforms left button text, and handles NM_RETURN
, NM_CLICK
and FTCN_SELECT
notifications.
#ifdef WIN32_PLATFORM_PSPC
bool CMyFileDialog::EmptyDialog::Init(LPARAM lParam)
{
if (Device::Is2003())
return DefInit(lParam, ID_FILEDIALOG2003, 4);
else
return DefInit(lParam);
}
#endif
bool CMyFileDialog::SplitDialog::Init(LPARAM lParam)
{
ATLASSERT(!lParam ||
!IsBadWritePtr((LPVOID)lParam, MAX_PATH * sizeof(TCHAR)));
SplitDialog::DefInit(lParam);
m_Right.SetExtendedListViewStyle
(CFileDialogListTraits::GetWndExStyle(0));
m_Right.Init(Device::IsSmartPhone() ? LVS_SMALLICON: LVS_REPORT);
m_Left.Init((LPCTSTR)lParam);
SetActivePane(SPLIT_PANE_LEFT);
return false;
}
bool CMyFileDialog::SplitDialog::Notify(int idCtrl, LPNMHDR pnmhdr)
{
switch(pnmhdr->code)
{
case NM_SETFOCUS:
SetDefaultActivePane(pnmhdr->hwndFrom);
if (Device::Is2003() && !Device::IsSmartPhone())
{
CMenuBarCtrl mb(::SHFindMenuBar(m_hWnd));
mb.HideButton(ID_NEXT_PANE,
GetDefaultActivePane() == SPLIT_PANE_RIGHT);
mb.HideButton(ID_PREV_PANE,
GetDefaultActivePane() == SPLIT_PANE_LEFT);
mb.PressButton(ID_WINDOW_SPLIT,
GetSinglePaneMode() == SPLIT_PANE_NONE);
}
else
SetMenuBarCommand(ID_NEXT_PANE, ID_NEXT_PANE,
GetDefaultActivePane() == SPLIT_PANE_RIGHT ?
L"Folders": L"Files");
break;
case NM_RETURN:
case NM_DBLCLK:
PostMessage(WM_COMMAND, IDOK);
break;
case FTCN_SELECT:
m_Right.SetFiles(m_Left.GetFileTreePath());
break;
default:
return false;
}
return true;
}
bool CMyFileDialog::SplitDialog::Command(WORD ,
WORD wID, HWND )
{
if(wID == IDOK)
{
if (m_Data)
{
CString sPath;
m_Right.GetFileListFullName(sPath);
SecureHelper::strcpyW_x((LPTSTR)m_Data,
MAX_PATH, sPath);
}
return false;
}
return SplitDialog::PaneCommand(wID);
};
|
|
void CDialogXFrame::ShellDialog()
creates a CString
containing a backslash, instanciates a CMyFileDialog
and calls DoModal
with the string buffer expanded to MAX_PATH
as lParam
. On return, the status bar is updated with the selected path if selection occurred.
Conclusion
It is likely that we have to wait some more years before MS compilers implement C++0x.
Meanwhile, with the power of the present implementation of C++ and the help of the WTL library, it is possible to write efficient reusable code accessing the Win32 API for many different platforms.
Revision History
- 6 Nov 2007: Released
- 12 Nov 2007: Article edited and moved to the main CodeProject.com article base