Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Unusual but Useful Dialog-based Classes Designed for Specialization

0.00/5 (No votes)
11 Nov 2007 1  
Featuring cell menu, in-place editor, single control and split dialogs, Vista shell and Mobile file controls and selection dialogs
Vista split dialog SmartPhone 2003 split dialog

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.

Guided Tours

Desktop Preliminaries

With the WTL 8.0 AppWizard generate a simple SDI application with CPP code and no view class. Call it DlgTour.

  • Open the resource editor and add a string resource 'Test dialog' with ID_TEST id.

  • Edit stdafx.h to include atlmisc.h as we will use CPoint and CString:

// stdafx.h: include file for standard system include files, 

// ... 


#include <atlwin.h>

 
#include <atlmisc.h>
#include <atlframe.h>

// ...  // 1 
  • Declare a context menu handler in CMainFrame...

// MainFrm.h: interface of the CMainFrame class 

// ...

BEGIN_MSG_MAP(CMainFrame)
    MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) // 2 

// ...

LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, 
        BOOL& bHandled); // 2 

// ... 
  • Extract atldlgx.h from atldlgx.zip to your <DlgTour> directory, and include it in mainframe.cpp

// MainFrm.cpp: implementation of the CMainFrame class 

// 

////////////////////////////////////////////////////////////////////////////

/ 

#include "stdafx.h" 

#include "resource.h" 


#include "atldlgx.h" // 3 


#include "aboutdlg.h" 

#include "MainFrm.h" 
  • Define OnContextMenu in MainFrm.cpp

// ...

LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, 
        BOOL& bHandled) // 3

{
  return 0;
}
// ...

Additional Vista Preliminaries

If the Microsoft� Windows� Software Development Kit for Windows Vista� is not installed on your development system go ahead:

  • Edit the constants in stdafx.h:

// stdafx.h: include file for standard system include files,

//  ...

// Change these values to use different versions

#define WINVER          0x0600 // 4

#define _WIN32_WINNT    0x0600 // 4

#define _WIN32_IE                0x0600 // 4
  • Download WtlAero.zip, extract WtlAero.h to to your <DlgTour> directory and include atlctrlx.h and WtlAero.h before atldlgx.h in mainframe.cpp:

// MainFrm.cpp: implmentation of the CMainFrame class

//

////////////////////////////////////////////////////////////////////////////


#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.

Step 1: A Rectangular Graphic Menu

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:

Screenshot - DesktopDlgTour.jpg

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.

Step 2: A Date Selector

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.

Screenshot - DesktopDate1.jpg

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)
{
        //typedef CControlDialog<ID_TEST, CMonthCalendarCtrl> CDateDialog;

// ...
Screenshot - DesktopDate2.jpg

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.

Step 3: A Glass Dialog

If you did not execute the additional Vista preliminaries skip this step.

Type this in MainFrm.cpp:

// MainFrm.cpp: implmentation of the CMainFrame class

// ...

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.

Mobile Preliminaries

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.

  • Open the IDE resource View

    • Select DlgToursp.rc

    • Import the res\toolbar.bmp bitmap with IDR_MAINFRAME id

    • Add a string resource 'Test dialog' with ID_TEST id

  • Extract atldlgx.h from atldlgx.zip to your <DlgTour> directory, and include it after atldlgs.h in DlgTourFrame.cpp

// DlgTourFrame.cpp: implementation of the CDlgTourFrame class

//

/////////////////////////////////////////////////////////////////////////////


#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.

Step 1: A SmartPhone Graphic Menu

Paste or type these six lines in CDlgTourFrame::OnAction code:

LRESULT CDlgTourFrame::OnAction(WORD /*wNotifyCode*/, WORD /*wID*/, 
HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
        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:

Screenshot - DlgTourSP1.jpg

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 /*wNotifyCode*/, WORD /*wID*/, 
        HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
// ...

        //CToolMenu::TrackCellMenu(pt, TPM_BOTTOMALIGN); 

        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.

Step 2: A Date Selection Dialog

Paste or type this in CDlgTourFrame::OnAction code:

LRESULT CDlgTourFrame::OnAction(WORD /*wNotifyCode*/, WORD /*wID*/,
    HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
        typedef CStdControlDialog<ID_TEST, CMonthCalendarCtrl> CDateDialog;

        CDateDialog dd;
        dd.DoModal();

        return 0;
}

Select SmartPhone 2003 platform, compile, run and push the Action button:

Screenshot - DlgTourSP2.jpg

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.

Reference

Ancillaries

  • 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.

CEmptyDialogImpl

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,      // Actual dialog class: ie CMyEmptyDialog  

        UINT t_uIDD,    // Dialog IDD, title, icon, toolbar, 

            //menu (if matching resources exist) 

        class TDlgTemplate      // In memory dialog template

                = CEmptyDlgTemplate<t_uIDD, CEmptyDlgTraits>, 
            // default for TDlgTemplate

        class TBase     // CIndirectDialogImpl::TBase class

                = ATL::CDialogImpl<T, ATL::CWindow>  // default for TBase 

        >
class ATL_NO_VTABLE CEmptyDialogImpl: public CIndirectDialogImpl<
        T,
        TDlgTemplate, 
        TBase
        > // CIndirectDialogImpl

{
        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<>.

Constructors

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 /*wParam*/,LPARAM /*lParam*/) repaints the client area.

  • bool Paint(HDC /*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

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,  // Menu dialog IDD and bitmap 

            //(if matching resource exists)

        INT t_nCol,     // Number of columns

        INT t_nRow      // Number of rows

        >
class CCellMenu: public CEmptyDialogImpl<
                /*thisClass*/CCellMenu<t_uIDD, t_nCol, t_nRow>, 
                t_uIDD,
                /*TDlgTemplate*/CMenuDlgTemplate
                >, // CEmptyDialogImpl

        public CMenuDialog<
                /*thisClass*/CCellMenu<t_uIDD, t_nCol, t_nRow> 
                > // CMenuDialog

{
        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.

// Specializables

        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

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,      // Actual dialog class: ie CMyControlDialog

        UINT t_uIDD,    // Dialog IDD, title, icon, toolbar, 

            // menu (if matching resources exist) 

        class TCtrl,    // Control class 

        class TControlTraits // Control styles 

                = ATL::CControlWinTraits,       // default control styles

        class TDlgImpl  // Empty dialog base class

                = CEmptyDialogImpl<T, t_uIDD, CControlDlgTraits> 
                    // default for TDlgImpl

        >
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 /*uMsg*/, WPARAM /*wParam*/,LPARAM /*lParam*/) is called on WM_KEYFIRST to WM_KEYLAST messages, does nothing and returns false.

  • HBRUSH CtlColor(HDC /*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 /*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

CControlDialog is a specializable CControlDialogImpl.

Definition

template
        < UINT t_uIDD,  // Dialog IDD, title, icon, toolbar, 

            // menu (if matching resources exist)

        class TCtrl,            // Control class

        class TControlTraits    // Control styles

                = ATL::CControlWinTraits,// default for TControlTraits   

        class TControlDlgTraits          // Dialog styles

                = CControlDlgTraits      // default for TControlDlgTraits

        >
class CControlDialog: public CControlDialogImpl< 
                /*thisClass*/CControlDialog<t_uIDD, TCtrl, TControlTraits,
            TControlDlgTraits>, 
                t_uIDD, 
                TCtrl, 
                TControlTraits, 
                /*TDlgImpl*/CEmptyDialogImpl< 
                        /*thisClass*/CControlDialog<t_uIDD, TCtrl, 
                TControlTraits, TControlDlgTraits>, t_uIDD, 
                        /*TDlgTemplate*/CEmptyDlgTemplate<t_uIDD, 
                TControlDlgTraits> 
                > // CEmptyDialogImpl

        > // CControlDialogImpl

{
        typedef CControlDialog<t_uIDD, TCtrl, TControlTraits, 
        TControlDlgTraits> thisClass;   

Constructors

Same as CEmptyDialogImpl.

Sample Usage

CMyDateDialog

CInPlaceEditor

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, 
        // length of text buffer passed as lParam in DoModal() call

        class TEditCtrl // edit control class

                = CEdit,        // default for TEditCtrl

        class TEditTraits       // edit control styles

                = CInPlaceEditTraits // default for TEditTraits

        >
class CInPlaceEditor: public CControlDialogImpl<
            /*thisClass*/CInPlaceEditor<t_iLength, TEditCtrl, TEditTraits>,
            /*t_uIDD*/t_iLength, 
            /*TCtrl*/TEditCtrl, 
            /*TCtrlTraits*/TEditTraits,
            /*TDlgImpl*/CEmptyDialogImpl<
                    /*thisClass*/CInPlaceEditor<t_iLength, TEditCtrl, 
                    TEditTraits>,
                    /*t_uIDD*/t_iLength, 
                    /*TDlgTemplate*/CMenuDlgTemplate
                    > // CEmptyDialogImpl

                >, // CControlDialogImpl

        public CMenuDialog<CInPlaceEditor<t_iLength, TEditCtrl, 
        TEditTraits> >
{
        typedef CInPlaceEditor<t_iLength, TEditCtrl, TEditTraits> thisClass;
public:
        // Constructor

        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())

  • rEdit is the positioning rectangle in CInPlaceEditor's parent coordinates.

  • sText is the address of a TCHAR buffer of size t_iLength containing the text to edit.

Overrideables

bool Init(LPARAM lParam)
{
        m_Data = lParam;
        m_Template.Reset(); // free DLGTEMPLATE memory

        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 /*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

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,       // Actual dialog class: ie CMySplitDialog

        UINT t_uIDD,    // Dialog IDD, title, icon, toolbar, 

            // menu (if matching resources exist)

        class TSplitImpl,    // Splitter implementation class: 

        // ie WTL::CSplitterImpl<CMySplitDialog, true> 

        class TLeft,    // Left (or top) control class

        class TRight,   // Right (or bottom) control class

        class TLeftTraits       // Left (or top) control styles

                = CSplitControlTraits,  // default for TLeftTraits

        class TRightTraits      // Right (or bottom) control styles 

                = CSplitControlTraits,  // default for TRightTraits

        class TDlgImpl  // Empty dialog base class 

                = CEmptyDialogImpl<T, t_uIDD, CSplitDlgTemplate<t_uIDD> >
            // default for TDlgImpl 

        >               
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

// Control members

        TLeft m_Left;   // Left or top control member

        TRight m_Right; // Right or bottom control member

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 /*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;// WM_INITDIALOG not handled

}

// Pane related commands

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

CSplitDialog is a specializable CSplitDialogImpl class using any adequate TSplitImpl splitter implementation.

Definition and Construction

/////////////////////////////////////////////////////////////////

// CSplitDialog - Generic split dialog

// 


template 
        < UINT t_uIDD,  // Dialog IDD, title, icon, toolbar, 

        // menu (if matching resources exist) 

        class TSplitImpl, // Splitter implementation class: 

        ie WTL::CSplitterImpl<CMySplitDialog, true> 
        class TLeft,            // Left (or top) control class

        class TRight,           // Right (or bottom) control class

        class TLeftTraits       // Left (or top) control styles

                = CSplitControlTraits,  // default for TLeftTraits

        class TRightTraits      // Right (or bottom) control styles 

                = CSplitControlTraits,  // default for TRightTraits

        class TSplitDialogTraits        // Dialog styles

                = CSplitDlgTraits       // default for TSplitDialogTraits

        >
class CSplitDialog: public CSplitDialogImpl<
                /*thisClass*/CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight,
            TLeftTraits, TRightTraits, TSplitDialogTraits>, 
                t_uIDD, 
                TSplitImpl, 
                TLeft, 
                TRight, 
                TLeftTraits, 
                TRightTraits,
                /*TDlgImpl*/CEmptyDialogImpl< 
                        /*thisClass*/CSplitDialog<t_uIDD, TSplitImpl, 
                TLeft, TRight, 
                TLeftTraits, TRightTraits, TSplitDialogTraits>, 
                        t_uIDD,
                        /*TDlgTemplate*/CEmptyDlgTemplate<t_uIDD, 
                TSplitDialogTraits> 
                > // CEmptyDialogImpl 

        > // CSplitDialogImpl

{
        typedef CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight, 
        TLeftTraits, TRightTraits, TSplitDialogTraits> thisClass;
public:
// Constructors

        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, CHSplitDialog

CVSplitDialog and CHSplitDialog are specializable CSplitDialogImpl classes where TSplitImpl is WTL::CSplitterImpl<T, true> and WTL::CSplitterImpl<T, false>.

Definition and Construction

/////////////////////////////////////////////////////////////////

// CVSplitDialog - Vertical WTL::CSplitterImpl based split dialog

// 


template // see CSplitDialog template parameters description

        < UINT t_uIDD,
        class TLeft, class TRight, 
        class TLeftTraits = ATL::CControlWinTraits, 
        class TRightTraits = ATL::CControlWinTraits, 
        class TSplitDialogTraits = CSplitDlgTraits
        >
class CVSplitDialog: public CSplitDialogImpl<
                /*thisClass*/CVSplitDialog<t_uIDD, TLeft, TRight, 
            TLeftTraits, TRightTraits, TSplitDialogTraits>, 
                t_uIDD, 
                /*TSplitImpl*/CSplitterImpl<
                        /*thisClass*/CVSplitDialog<t_uIDD, TLeft, TRight, 
                TLeftTraits, TRightTraits, TSplitDialogTraits>,
                        true
                        >, // CSplitterImpl

                TLeft, TRight, TLeftTraits, TRightTraits,
                /*TDlgImpl*/CEmptyDialogImpl< 
                        /*thisClass*/CVSplitDialog<t_uIDD, TLeft, TRight, 
                    TLeftTraits, TRightTraits,TSplitDialogTraits>, t_uIDD,
                        /*TDlgTemplate*/CEmptyDlgTemplate<t_uIDD, 
                    TSplitDialogTraits> 
                > // CEmptyDialogImpl  

        > // CSplitDialogImpl

{
        typedef CVSplitDialog<t_uIDD, TLeft, TRight, TLeftTraits, 
        TRightTraits, TSplitDialogTraits> thisClass;
public:
// Constructors

        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.

Aero-enabled Classes

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

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,      // Actual dialog class: ie CMyAeroEmptyDialog  

        UINT t_uIDD,    // Dialog IDD, title, icon, toolbar, 

            //menu (if matching resources exist) 

        class TDlgTemplate      // In memory dialog template

                = CEmptyDlgTemplate<t_uIDD, CEmptyDlgTraits> 
        // default for TDlgTemplate

        >
class ATL_NO_VTABLE CEmptyDialogImpl: public WTL::CEmptyDialogImpl<
        T,
        t_uIDD, 
        TDlgTemplate,
        aero::CDialogImpl<T>
        > // WTL::CEmptyDialogImpl

{
        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

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,          // Dialog IDD, title, icon, toolbar, 

        menu (if matching resources exist)
        class TCtrl,            // Aero enabled Control class

        class TControlTraits    // Control styles

                = ATL::CControlWinTraits,   // default for TControlTraits   

        class TControlDlgTraits         // Dialog styles

                = CControlDlgTraits     // default for TControlDlgTraits

        >
class CControlDialog: public WTL::CControlDialogImpl< 
        /*thisClass*/aero::CControlDialog<t_uIDD, TCtrl, TControlTraits, 
        TControlDlgTraits>, 
                t_uIDD, 
                TCtrl, 
                TControlTraits, 
                /*TDlgImpl*/aero::CEmptyDialogImpl< 
                        /*thisClass*/aero::CControlDialog<t_uIDD, TCtrl, 
                        TControlTraits, TControlDlgTraits>, t_uIDD, 
                        /*TDlgTemplate*/CEmptyDlgTemplate<t_uIDD, 
                        TControlDlgTraits> 
                > // aero::CEmptyDialogImpl

        > // WTL::CControlDialogImpl

{
        typedef aero::CControlDialog<t_uIDD, TCtrl, TControlTraits, 
            TControlDlgTraits> thisClass;
public:
        // Constructors

        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

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,  // Dialog IDD, title, icon, toolbar, 

            // menu (if matching resources exist) 

        class TSplitImpl,   // Aero enabled splitter implementation class: 

                // ie aero::CSplitterImpl<CMySplitDialog, true> 

        class TLeft,        // Aero enabled reft (or top) control class

        class TRight,       // Aero enabled right (or bottom) control class

        class TLeftTraits   // Left (or top) control styles

                = CSplitControlTraits,  // default for TLeftTraits

        class TRightTraits  // Right (or bottom) control styles 

                = CSplitControlTraits,  // default for TRightTraits

        class TSplitDialogTraits        // Dialog styles

                = CSplitDlgTraits       // default for TSplitDialogTraits

        >
class CSplitDialog: public WTL::CSplitDialogImpl<
        /*thisClass*/aero::CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight,
            TLeftTraits, TRightTraits, TSplitDialogTraits>, 
                t_uIDD, 
                TSplitImpl, 
                TLeft, 
                TRight, 
                TLeftTraits, 
                TRightTraits,
                /*TDlgImpl*/aero::CEmptyDialogImpl< 
                        /*thisClass*/aero::CSplitDialog<t_uIDD, TSplitImpl,
                TLeft, TRight, 
                TLeftTraits, TRightTraits, 
                TSplitDialogTraits>, 
                        t_uIDD,
                        /*TDlgTemplate*/CEmptyDlgTemplate<t_uIDD, 
                TSplitDialogTraits> 
                > // aero::CEmptyDialogImpl 

        > // WTL::CSplitDialogImpl

{
        typedef aero::CSplitDialog<t_uIDD, TSplitImpl, TLeft, TRight, 
        TLeftTraits, TRightTraits, TSplitDialogTraits> thisClass;
public:
// Constructors

        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

Mobile Adapted Classes

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

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,      // Actual dialog class: ie CMyStdEmptyDialog 

        UINT t_uIDD,    // Dialog IDD, title, MenuBar 

            // (if matching resources exist) 

        UINT t_shidiFlags       // Position flags for ::SHInitDialog() 

                = WTL_STD_SHIDIF,       // default for t_shidiFlags 

        class TDlgTemplate      // In memory dialog template 

                = CStdEmptyDlgTemplate<t_uIDD> // default for TDlgTemplate 

        >
class ATL_NO_VTABLE CStdEmptyDialogImpl: public CEmptyDialogImpl<
                T, 
                t_uIDD, 
                TDlgTemplate, 
                /*TBase*/CStdDialogImpl<T, t_shidiFlags, true> 
        > // CEmptyDialogImpl

{
        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

CStdControlDialog is a CControlDialog using CStdEmptyDialogImpl as TDlgImpl template parameter.

Definitions

template 
< UINT t_uIDD,  // Dialog IDD, title, MenuBar (if matching resources exist)

class TCtrl,    // Control class 

class TControlTraits    // Control styles

        = ATL::CControlWinTraits,       // default for TControlTraits 

UINT t_shidiFlags       // Position flags for ::SHInitDialog() 

        = WTL_STD_SHIDIF,       // default for t_shidiFlags 

class TDlgTemplate      // In memory dialog template 

        = CStdEmptyDlgTemplate<t_uIDD> // default for TDlgTemplate 

>
class CStdControlDialog: public CControlDialogImpl< 
        /*thisClass*/CStdControlDialog<t_uIDD, TCtrl, TControlTraits, 
                t_shidiFlags, TDlgTemplate>, 
        t_uIDD, 
        TCtrl, 
        TControlTraits,
                /*TDlgImpl*/CStdEmptyDialogImpl<
                /*thisClass*/CStdControlDialog<t_uIDD, TCtrl, TControlTraits,
                        t_shidiFlags, TDlgTemplate>, t_uIDD, 
                t_shidiFlags, 
                TDlgTemplate
                > //  CStdEmptyDialogImpl 

        > // CControlDialogImpl

{
typedef CStdControlDialog<t_uIDD, TCtrl, TControlTraits, t_shidiFlags, 
        TDlgTemplate> thisClass;
};

Sample Code

CMyDateDialog

CStdSplitDialog

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,  // Dialog IDD, title, MenuBar (

            // if matching resources exist)  

        class TSplitImpl, // Splitter implementation class: ie 

                        // WTL::CSplitterImpl<CMyStdSplitDialog, true> 

        class TLeft,            // Left (or top) control class

        class TRight,           // Right (or bottom) control class

        class TLeftTraits       // Left (or top) control styles

                = CSplitControlTraits,  // default for TLeftTraits

        class TRightTraits      // Right (or bottom) control styles 

                = CSplitControlTraits,  // default for TRightTraits

        UINT t_shidiFlags       // Position flags for ::SHInitDialog()

                = WTL_STD_SHIDIF,       // default for t_shidiFlags

        class TDlgTemplate      // In memory dialog template 

                = CStdSplitDlgTemplate<t_uIDD> // default for TDlgTemplate

        >
class CStdSplitDialog: public CSplitDialogImpl<
                /*thisClass*/CStdSplitDialog<t_uIDD, 
                    TSplitImpl, TLeft, TRight, 
                    TLeftTraits, TRightTraits, t_shidiFlags, TDlgTemplate>,
                t_uIDD, 
                TSplitImpl, 
                TLeft, 
                TRight, 
                TLeftTraits, 
                TRightTraits, 
                /*TDlgImpl*/CStdEmptyDialogImpl <
                        /*thisClass*/CStdSplitDialog<t_uIDD, 
                        TSplitImpl, TLeft, 
                        TRight, TLeftTraits, TRightTraits, t_shidiFlags, 
                        TDlgTemplate>, 
                        t_uIDD, 
                        t_shidiFlags, 
                        TDlgTemplate
                        > //  CStdEmptyDialogImpl

                > // CSplitDialogImpl

{
        typedef CStdSplitDialog<t_uIDD, TSplitImpl, TLeft, 
                TRight, TLeftTraits, 
                TRightTraits, t_shidiFlags, TDlgTemplate> thisClass;
};

CStdVSplitDialog, CStdHSplitDialog

CStdHSplitDialog and CStdVSplitDialog are specialized CHSplitDialog and CVSplitDialog using CStdEmptyDialogImpl as TDlgImpl template parameter.

Definition

template // see CStdSplitDialog template parameters description 

        < 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<
                /*thisClass*/CStdHSplitDialog<t_uIDD, TLeft, TRight, 
            TLeftTraits, TRightTraits, t_shidiFlags, TDlgTemplate>, 
                t_uIDD, 
                /*TSplitImpl*/CSplitterImpl<
                        /*thisClass*/CStdHSplitDialog<t_uIDD, TLeft, TRight,
                        TLeftTraits, TRightTraits, 
                        t_shidiFlags, TDlgTemplate>, 
                        false
                        >, // CSplitterImpl 

                TLeft, 
                TRight, 
                TLeftTraits, 
                TRightTraits, 
                /*TDlgImpl*/CStdEmptyDialogImpl <
                        /*thisClass*/CStdHSplitDialog<t_uIDD, TLeft, TRight,
                        TLeftTraits, TRightTraits, 
                        t_shidiFlags, TDlgTemplate>, 
                        t_uIDD, 
                        t_shidiFlags, 
                        TDlgTemplate
                        > // CEmptyDialogImpl 

                > // CSplitDialogImpl

{
        typedef CStdHSplitDialog<t_uIDD, TLeft, TRight, TLeftTraits, 
                TRightTraits, t_shidiFlags, TDlgTemplate> thisClass;
};

Sample Code

CMyFileDialog

Samples

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

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.

// MainFrame.h

// ...  

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

CMyColorMenu specializes CCellMenu::PaintCell to paint a color table.

////////////////////////////////////////////////////////////

// CMyColorMenu


#ifndef ID_COLORMENU
        #define ID_COLORMENU 1001
#endif 

typedef CCellMenu<ID_COLORMENU, 8, 5> CMyColorMenu;

// Color table

__declspec(selectany) COLORREF colors[] = /* from Chris Maunders */
        { 
        // ...

        };

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.

// MainFrame.h

// ...  

void ColorMenu(POINT pt)
{
        int iSel = CMyColorMenu::TrackCellMenu(pt, TPM_TOPALIGN, m_icolor);
        if (iSel != -1)
        {
                m_icolor = iSel;
                Invalidate();
        }
}

CMyDateDialog

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 /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/) copies the selected date to m_Data.

////////////////////////////////////////////////////////////

// CMyDateDialog


#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 /*wNotifyCode*/, WORD wID,
    HWND /*hWndCtl*/)
{
        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.

// MainFrame.h

// ...  

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);
        }
}

CPaneEditor

#define STATUS_LENGTH 127 // Max length of pane text

// ...

typedef CInPlaceEditor<STATUS_LENGTH> CPaneEditor;

CStatusPaneEditor

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

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;

// Common controls V6 compatibility

#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;


//////////////////////////////////////////////////////////

// CMyShellDialog


#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) performs default SplitDialog initializations, specific Shell controls initializations, toolbar initialization and returns true.

  • bool CMyShellDialog::SplitDialog::Command(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/) adjusts the state of the ID_WINDOW_SPLIT button and copies the selected path to m_Data if the command was IDOK.

  • bool CMyShellDialog::SplitDialog::Notify(int idCtrl, LPNMHDR pnmh) handles the NM_SETFOCUS, NM_CLICK and TVN_SELCHANGED notifications.

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 /*wNotifyCode*/, WORD wID, 
    HWND /*hWndCtl*/)
{
        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, &#960;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

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 /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, 
        BOOL& /*bHandled*/)
{
  if (::GetFocus() == m_Right) 
      {
              CPidl pidl;
              if (m_Right.GetItemPidl(m_Right.GetSelectedIndex(), &#960;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 // (_WIN32_WINNT >= 0x0600) && defined(NTDDI_VERSION) && \

(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);
}

DialogX Vista Edition

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

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.

CAeroShellDialog and AeroShell Controls

CAeroShellTreeCtrl and CAeroShellListCtrl are aero::CCtrlImpl adaptations of Bjarke Viks�e's CShellTreeCtrl and CShellListCtrl.

#if !defined __WTL_AERO_H__
// ...

#else

//////////////////////////////////////////////////////////

// AeroShell controls


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.

//////////////////////////////////////////////////////////

// CAeroShellDialog


typedef class CAeroShellDialog: public aero::CSplitDialog<
                /*t_uIDD*/ID_VISTASHELLDIALOG, 
                /*TSplitImpl*/aero::CSplitterImpl<
                        /*thisClass*/CAeroShellDialog,
                        true
                        >, // aero::CSplitterImpl

                CAeroShellTreeCtrl, CAeroShellListCtrl, 
                CShellTreeTraits, CShellListTraits
                > // CAeroEmptyDialogImpl  

{
public:
// Constructor

        CAeroShellDialog(SIZE size): CSplitDialog(size)
        {}

} CMyShellDialog;

#endif // !defined __WTL_AERO_H__

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.

// MainFrm.h: interface of the CMainFrame class 

// ...

#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 // (_WIN32_WINNT >= 0x0600) && defined(NTDDI_VERSION) && \

    (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 // (_WIN32_WINNT >= 0x0600) && defined(NTDDI_VERSION) && \

    (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);
        }

Mobile DialogX

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.

Mobile CMyToolMenu

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 // resolution awareness


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 // _WTL_CE_DRA

#endif // _WIN32_WCE

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]);
}

Mobile CMyColorMenu

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);
        // for PPC2003 menubar

        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();
        }
}

Mobile CMyDateDialog

The only difference with the desktop implementation is that CMyDateDialog instantiates a CStdControlMenu with month calendar control specialization.

Mobile CPaneEditor

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 // Max length of pane text


#ifdef _WIN32_WCE

// CEditPaneCtrl, a CEdit with vertical keys sending IDCANCEL to parent +

//              PPC: 'usual' VK_RETURN handling for WM5 

//              SmartPhone: handling VK_TBACK as VK_BACK

//


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 /*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 // WIN32_PLATFORM_PSPC

Mobile CStatusPaneEditor

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

CMyFileDialog is a CStdHSplitDialog using CDirTreeCtrl as TLeft parameter, and CFileListCtrl as TRight parameter.

#if _WIN32_WCE < 0x500 && defined(WIN32_PLATFORM_WFSP)
// No WS_TABSTOP for SmartPhone 2003 controls

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::Init(LPARAM lParam) performs default SplitDialog initializations, specific File controls initializations and returns false for eVC/ATL3 compatibility.

  • bool CMyFileDialog::SplitDialog::Command(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/) 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())
        { // PPC2003 MenuBar

            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: // PPC2005 will do that

    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 /*wNotifyCode*/, 
    WORD wID, HWND /*hWndCtl*/)
{
    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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here