65.9K
CodeProject 正在变化。 阅读更多。
Home

不寻常但有用的、专为专业化设计的对话框类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (40投票s)

2007年11月6日

CPOL

20分钟阅读

viewsIcon

101927

downloadIcon

2692

包含单元格菜单、就地编辑器、单个控件和分割对话框、Vista Shell以及移动设备文件控件和选择对话框

Vista split dialog SmartPhone 2003 split dialog

谢谢

本文中展示的所有代码均使用 WTL 8.0。感谢 Nenad Stefanovic 和所有 WTL 开发者。

桌面示例在获得许可的情况下使用了 Bjarke Viksøeshell 控件。感谢 Bjarke。

彩色菜单的颜色是从 Chris Maunder 的 Office 97 风格颜色选择器控件中提取的。

演示

使用模态对话框进行用户交互等同于向用户发出一个阻塞调用。DoModal() 调用具有一个简单且唯一的退出点,并在返回时携带请求的信息,包括用户可能取消的操作。它们通常需要创建一个 DIALOGTEMPLATE 资源和一个关联的 CDialogImpl 派生类。

本文介绍的(非同寻常的)模态对话框类集不需要所有这些。它使用 C++ 模板,而不是 DIALOGTEMPLATE 资源。

它旨在易于专门化,只需极少甚至无需任何特定编码。它构成了一个灵活的工具集,允许轻松编写、阅读和维护应用程序代码,并能简单地适应多平台特定需求。

基类派生或专门化为非标准菜单,例如矩形菜单,用于用户选择操作的单个控件对话框,或用于就地文本编辑的就地文本编辑,以及用于更复杂任务的分割对话框。它们可以与所有发布的 MS 编译器(从 VC6/eVC 到 VS2005 和 VCExpress)一起编译,可以定位任何 Win32 或 WinCE 系统,并专门化为需要 Windows Vista 或 Windows Mobile SDK 的平台特定类。它们的大部分 C++ 代码位于 atldlgx.h 中,代码只是声明性的,不产生任何机器代码。

本文由三部分组成

  • 导览展示了只需几行代码即可获得的效果,并让您(如果需要)熟悉模板专门化代码。
  • 参考部分描述了通用类以及平台专门化的 Vista 和 Mobile 类。
  • 示例部分通过三个相似的 DialogX 应用程序展示了应用用法:一个预 Vista 和一个支持 Vista 的桌面应用程序,以及一个支持所有移动设备(从 SmartPhone 2003 和 PPC 2002 到 WM6)的 Windows Mobile 应用程序(包含平台适配)。

导览

桌面初步

使用 WTL 8.0 AppWizard 生成一个简单的 SDI 应用程序,**包含 CPP 代码**,**无视图类**。将其命名为 *DlgTour*。

  • 打开资源编辑器,添加一个字符串资源 'Test dialog',其 ID 为 ID_TEST

  • 编辑 stdafx.h,包含 atlmisc.h,因为我们将使用 CPointCString

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

// ... 


#include <atlwin.h>

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

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

// ... 
  • atldlgx.hatldlgx.zip 解压到您的 <DlgTour> 目录,并在 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" 
  • MainFrm.cpp 中定义 OnContextMenu

// ...

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

{
  return 0;
}
// ...

Vista 附加初步

如果您的开发系统上未安装 Microsoft® Windows® Software Development Kit for Windows Vista™,请继续

  • 编辑 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
  • 下载 WtlAero.zip,将 WtlAero.h 解压到您的 <DlgTour> 目录,并在 mainframe.cpp 中在 atldlgx.h 之前包含 atlctrlx.hWtlAero.h

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

现在我们可以开始游览了。

步骤 1:一个矩形图形菜单

将这两行粘贴或键入到 CMainFrame::OnContextMenu 代码中。

LRESULT CMainFrame::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL& bHandled)
{
        typedef CCellMenu<IDR_MAINFRAME, 4, 2> CToolMenu; 
        CToolMenu::TrackCellMenu(CPoint(lParam)); 
        return 0;
}

编译、运行并右键单击 DlgTour 窗口。

Screenshot - DesktopDlgTour.jpg

我们定义 CToolMenu 为一个 CCellMenu,它以 4 列 2 行显示 IDR_MAINFRAME 位图。我们调用了它的静态成员 TrackCellMenu(),默认定位在鼠标坐标 CPoint(lParam)

TrackCellMenu 返回所选单元格的索引,如果没有选择则返回 -1。要使菜单功能化,我们必须根据返回值进行操作。

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

唯一的功能性命令处理程序是 ID_APP_ABOUT。使用键盘选择问号或单击它来调出“关于”对话框,执行任何其他鼠标或键盘操作来关闭菜单。

请参阅示例部分的 CMyToolMenuCMyColorMenu

步骤 2:一个日期选择器

将此代码键入 CMainFrame::OnContextMenu() 成员函数。

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

编译、运行并右键单击。

Screenshot - DesktopDate1.jpg

我们定义 CDateDialog 为一个 CControlDialog,它托管了一个 CMonthCalendarCtrl,ID 为 ID_TEST,并使用默认样式。

我们实例化了一个 CDateDialog,其原点位于鼠标位置,并指定了任意大小,然后对其调用了 DoModal()

要自动调整对话框大小以适应月历控件,请将 CDateDialog 的声明移出 CMainFrame::OnContextMenu(),并为其添加一些**模板专门化**。

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

我们**专门化**了 CDateDialog 的一个**父类**成员 CControlDialogImpl<<CDateDialog, ID_TEST, CMonthCalendarCtrl>::Init(),使其(在默认初始化后)根据 CMonthCalendarCtrl 请求的大小进行调整。

在桌面示例部分,更多关于 CMyDateDialog 的内容。

步骤 3:一个玻璃对话框

如果您没有执行 Vista 附加初步,请跳过此步骤。

将此代码键入 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;
}

编译、运行并右键单击。

我们定义了一个 CTestDialog 类,它派生自 aero::CEmptyDialogImpl 并与其行为一致,使用了 CSplitDialogTraits(调整边框)样式。我们实例化了它并调用了它的 DoModal() 成员。

在 Vista 示例部分,更多关于 CMyVistaShellDialog 的内容。

移动设备初步

VS2005:使用 WTL 8.0 Mobile AppWizard 生成一个简单的 **SDI** 应用程序,目标平台为 **PPC2003** 和 **SmartPhone2003**,包含 **CPP 代码**,支持 **eVC 兼容性**,**无视图类**。将其命名为 *DlgTour*。

eVC4:下载 eVCDlgTour.zip,解压并用 eVC 打开。

  • 打开 IDE 资源视图。

    • 选择 DlgToursp.rc

    • 导入 res\toolbar.bmp 位图,ID 为 IDR_MAINFRAME

    • 添加一个字符串资源 'Test dialog',ID 为 ID_TEST。

  • atldlgx.hatldlgx.zip 解压到您的 <DlgTour> 目录,并在 DlgTourFrame.cpp 中在 atldlgs.h 之后包含它。

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

现在我们可以开始游览了。

步骤 1:一个 SmartPhone 图形菜单

将这六行代码粘贴或键入到 CDlgTourFrame::OnAction 代码中。

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

选择 SmartPhone 2003 平台,编译、运行并按下 Action 按钮。

Screenshot - DlgTourSP1.jpg

我们定义了一个 CCellMenu,它显示 IDR_MAINFRAME 位图,包含 7 列 1 行。

我们调用了它的 TrackCellMenu() 静态成员,定位在框架的左下角(底部和默认左侧)。

TrackCellMenu 返回所选单元格的索引,如果没有选择则返回 -1。要使菜单功能化,我们必须使用返回值。在 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;
};

唯一工作的处理程序是 OnAppAbout。如果我们使用小键盘选择问号,ID_APP_ABOUT 命令将确实执行并弹出“关于”对话框。

在移动设备示例部分,更多关于 Mobile CMyToolMenu 的内容。

步骤 2:一个日期选择对话框

将此代码粘贴或键入到 CDlgTourFrame::OnAction 代码中。

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

        CDateDialog dd;
        dd.DoModal();

        return 0;
}

选择 *SmartPhone 2003* 平台,编译、运行并按下 Action 按钮。

Screenshot - DlgTourSP2.jpg

我们定义 CDateDialog 为一个 CStdControlDialog,它托管了一个 CMonthCalendarCtrl,ID 为 ID_TEST

我们实例化了一个 CDateDialog 并对其调用了 DoModal()

要使用选定的日期,我们应该添加一些调用和专门化代码。在移动设备示例部分,更多关于 Mobile CMyDateDialog 的内容。

参考

辅助类

  • CEmptyDlgTemplate 是一个 WTL::CMemDlgTemplate,它在其 Create(POINT point, SIZE size) 成员中从资源字符串加载对话框标题。

    要完全定义一个 CEmptyDlgTemplate,您需要指定一个字符串资源 ID,以及可选的 ATL::CWinTraits 实例作为样式。

  • CEmptyDlgTraitsCControlDlgTraitsCSplitDlgTraits 是各种 CEmptyDlgTemplate 专门化的对话框样式。

  • CMenuDialog 是一个 MI 类:将其添加到模态对话框的继承列表中并链接其消息映射,可以实现父窗口的响应,并在失去焦点时自动取消,从而提供简单的类似菜单的行为。

CEmptyDialogImpl

定义

由于我们尚未受益于 C++0x模板类型别名atldlgx.h 中的 CEmptyDialogImpl 和其他类的定义可能不易阅读。为了提高可读性,每个模板参数都写在不同的行上并进行了注释。

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;
// ...

显式展开的模板定义允许最大的类使用灵活性。TBase 默认为 ATL::CDialogImpl<>,但移动设备的 CStdEmptyDialogImplTBaseWTL::CStdDialogImpl<>。如果我们想要一个 **Aero 对话框**,如 **Have a glass with WTL!** 中所述,TBase 应该是 WTL::aero::CDialogImpl<>

构造函数

所有构造函数都有一个 bool bDLU 参数。当为 true(默认值)时,大小和定位参数在生成的 DLGTEMPLATE 中转换为 DLU

  • 空构造函数:派生类执行 m_Template 和 m_Data 的初始化。

    CEmptyDialogImpl()

  • 大小构造函数:将原点设置为 {0,0}

    CEmptyDialogImpl(SIZE size, bool bDLU = true)

  • 定位构造函数:将原点设置为 point,大小设置为 size。

    CEmptyDialogImpl(POINT point, SIZE size, bool bDLU = true)

  • 定位构造函数:使用 rect 作为原点和大小。

    CEmptyDialogImpl(RECT rect, bool bDLU = true)

数据成员

  • enum {IDD} 根据 t_uIDD 定义,以与其他对话框兼容。

  • LPARAM m_Data 存储用户数据,取决于派生类。

  • HWND m_hWndToolBar 存储工具栏控件的句柄(仅限 Win32 平台)。

默认操作:可重写成员

这些成员由消息处理程序调用,可以在派生类中重写。如果未重写,它们将定义类在收到匹配的 WM_xxx 消息时的行为。布尔返回值用于消息处理程序在返回时作为 bHandled 的值。

  • bool Init(LPARAM lParam) 调用 DefInit(lParam)

  • bool Size(WPARAM /*wParam*/,LPARAM /*lParam*/) 重绘客户端区域。

  • bool Paint(HDC /*hdc*/) 不执行任何操作。

  • void Close(INT iCmd) 结束对话框并返回 iCmd

辅助成员

  • DefInit(),默认初始化成员,将 DoModal() lParam 复制到 m_Data,尝试创建工具栏,加载菜单,并从匹配的 t_uIDD 资源设置对话框图标,最后释放 DLGTEMPLATE 内存。将 WM_INITDIALOG 设置为未处理状态,为派生类消息映射留下了后续处理的可能性。

  • MoveToTemplatePix() 允许在初始化时进行精确的定位,当模板位置数据在创建时未转换为 DLU 时。

  • PixToDLU() 成员由构造函数内部使用。

派生类

aero::CEmptyDialogImplCStdEmptyDialogImpl 使用特定的 TBase 模板参数。

CControlDialogImpl, CSplitDialogImpl 是派生自 CEmptyDialogImpl 的基类。

CCellMenuCMyVistaShellDialog 是直接派生自 CEmptyDialogImpl 的用户类。

CCellMenu

CCellMenu 旨在取代传统的菜单,适用于矩形单元格布局的场景。它使用 CMenuDialogTraits 样式。

定义

CCellMenu 派生自 CEmptyDialogImplCMenuDialog

该类定义使用了三个模板参数:一个位图资源 ID,列数和行数。

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

构造函数

CCellMenu 有一个构造函数,使用 POINT 作为可重写成员 static const SIZE CCellMenu::CellSize()static const SIZE CCellMenu::MenuSize(),它们根据关联的位图大小以及行数和列数来计算各自的大小。如果不存在 ID 为 t_uIDD 的位图,则单元格大小根据系统度量 SM_CXSMICONSM_CYSMICON 计算。

默认操作

CCellMenu 使用 m_Data 作为所选单元格的索引,并在鼠标或键盘操作时更新它。

辅助结构 struct CCellMenu::CELL 提供了各种大小和位置计算。

CCellMenu::DoModal() 返回用户单击单元格或按下回车键时所选单元格的索引,如果对话框被取消或关闭,则返回 -1。

CCellMenu 设计用于通过其 static int CCellMenu::TrackCellMenu(POINT pt, UINT uFlags = 0, LPARAM lParam = 0, HWND hWndParent = GetActiveWindow()) 进行操作。

  • pt 是定位参考点。

  • uFlagsTrackPopupMenuTPM_xxxALIGN)标志的组合。

  • lParam 是初始选择索引。

CCellMenu::TrackCellMenu() 生成一个按 uFlags 指定定位的 CCellMenu,调用其 DoModal() 成员,并返回用户选择的索引。

可重写成员

CCellMenu 的绘图和大小调整成员可以进行专门化。

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

示例用法

CMyColorMenu, CMyToolMenu

CControlDialogImpl

CControlDialogImpl 实现了一个单一控件的对话框,从而允许对任何控件调用 DoModal()

定义

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;
// ...

构造函数

CEmptyDialogImpl 相同。

数据成员

CControlDialogImpl 有一个类型为 TCtrlm_Ctrl 成员。

默认操作:可重写成员

这些成员由消息处理程序调用,可以在派生类或专门化类中重写。如果未重写,它们将定义类在收到匹配的 WM_xxx 消息时的行为。布尔返回值用于消息处理程序在返回时作为 bHandled 的值。

  • bool CControlDialogImpl::Init(LPARAM lParam) 返回 CControlDialogImpl::DefInit(lParam)

  • bool CControlDialogImpl::Size(WPARAM wParam,LPARAM lParam) 返回 CControlDialogImpl::DefSize(wParam, lParam)

  • bool Notify(int idCtrl, LPNMHDR pnmhdr)WM_NOTIFY 消息时调用,不执行任何操作并返回 false

  • bool Command(WORD wNotifyCode, WORD wID, HWND hWndCtl)WM_COMMAND 消息时调用,不执行任何操作并返回 false

  • bool Key(UINT /*uMsg*/, WPARAM /*wParam*/,LPARAM /*lParam*/)WM_KEYFIRSTWM_KEYLAST 消息时调用,不执行任何操作并返回 false

  • HBRUSH CtlColor(HDC /*hdc*/)WM_xxxCOLOR 消息时调用,不执行任何操作并返回 NULL

辅助成员

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

派生类

CControlDialog, aero::CControlDialog 和移动设备中的 CStdControlDialog 可实例化类直接派生自 CControlDialogImpl

CInPlaceEditor 同时派生自 CControlDialogImplCMenuDialog

CControlDialog

CControlDialog 是一个可专门化的 CControlDialogImpl

定义

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;   

构造函数

CEmptyDialogImpl 相同。

示例用法

CMyDateDialog

CInPlaceEditor

CInPlaceEditor 旨在允许在特定矩形中就地编辑已知文本。

定义

CInPlaceEditor 派生自 CControlDialogImplCMenuDialog

该类定义使用了三个模板参数:编辑文本的最大长度,编辑控件类(默认为 CEdit),以及编辑控件的样式(默认为 CInPlaceEditTraits)。因此,默认是一个单行 EDIT,带 ES_AUTOHSCROLL

定义和构造

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

构造函数

CInPlaceEditor 有一个使用 RECT 的构造函数。

默认操作

CInPlaceEditor 使用 DoModal() lParam 值作为长度为 t_iLength 的文本缓冲区。CInPlaceEditor::Init(LPARAM lParam)lParam 复制到其 m_Data 成员,并从那里设置编辑控件的初始内容。

如果用户按下回车键,编辑控件的内容将被复制回 m_Data 成员。

CInPlaceEditor 设计用于通过其 static bool CInPlaceEditor::Edit(RECT& rEdit, LPTSTR sText, HWND hwndParent = GetActiveWindow()) 进行操作。

  • rEditCInPlaceEditor 父坐标中的定位矩形。

  • sText 是一个 TCHAR 缓冲区地址,其大小为 t_iLength,包含要编辑的文本。

可重写成员

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

示例用法

CPaneEditor, CStatusPaneEditor

CSplitDialogImpl

CSplitDialogImpl 实现双控件分割对话框。

定义

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;

请注意,TSplitImpl 不仅限于 WTL::CSplitterImpl。它可以像 WTL::aero::CSplitterImpl<> 一样派生自它,或者像 Miguel Hasse de Oliveira 的 WTL::CDynSplitterImpl 一样兼容。

构造函数

CEmptyDialogImpl 相同。

数据成员

// Control members

        TLeft m_Left;   // Left or top control member

        TRight m_Right; // Right or bottom control member

默认操作:可重写成员

这些成员由消息处理程序调用,可以在派生类或专门化类中重写。如果未重写,它们将定义类在收到匹配的 WM_xxx 消息时的行为。bool 返回值在返回时被消息处理程序复制到 bHandled

  • bool CSplitDialogImpl::Init(LPARAM lParam) 返回 CSplitDialogImpl::DefInit(lParam)

  • bool CSplitDialogImpl::Size(WPARAM wParam,LPARAM lParam) 调用 Splitter::SetSplitterRect()

  • bool Notify(int idCtrl, LPNMHDR pnmhdr)WM_NOTIFY 消息时调用,不执行任何操作并返回 false

  • HBRUSH CtlColor(HDC /*hdc*/)WM_xxxCOLOR 消息时调用,不执行任何操作并返回 NULL

  • bool Command(WORD wNotifyCode, WORD wID, HWND hWndCtl) 返回 CSplitDialogImpl::PaneCommand(wID)

辅助成员

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) 处理三个预定义的 WTL 命令 ID。

  • ID_WINDOW_SPLIT 切换单/双窗格模式。

  • ID_NEXT_PANEID_PREV_PANE 更改活动焦点,以及可能的单窗格。

派生类

CSplitDialog, CVSplitDialog, CHSplitDialog, aero::CSplitDialog 以及移动设备中的 CStdSplitDialog, CStdVSplitDialog, CStdHSplitDialog 可实例化类直接派生自 CSplitDialogImpl

CSplitDialog

CSplitDialog 是一个可专门化的 CSplitDialogImpl 类,它使用任何合适的 TSplitImpl 分割器实现。

定义和构造

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

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

CVSplitDialogCHSplitDialog 是可专门化的 CSplitDialogImpl 类,其中 TSplitImpl 分别是 WTL::CSplitterImpl<T, true>WTL::CSplitterImpl<T, false>

定义和构造

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

// 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 同样如此,但将 false 作为 CSplitterImpl 的第二个模板参数。

示例用法

CMyFileDialog, CMyShellDialog

启用 Aero 的类

要编译这些类,您的开发平台需要访问 Microsoft® Windows® Software Development Kit for Windows Vista™,并且您的项目必须包含 WtlAero.h

请参阅 Have a glass with WTL! 以了解 WTL::aero 命名空间类的描述。

aero::CEmptyDialogImpl

aero::CEmptyDialogImpl 是一个 WTL::CEmptyDialogImpl,它使用 aero::CDialogImpl 作为 TBase 模板参数。

如果工具栏已附加到 t_uIDD 模板参数,则必须使用 32 位位图。请注意,这不是 Vista 的要求,但 WTL8.0 代码在 Aero 操作时将无法以正确的标志加载工具栏位图,除非它是 32 位颜色。

定义

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;

构造函数

与 WTL 的 CEmptyDialogImpl 相同。

数据成员

aero::CToolBarCtrl m_ATB 成员用于在 Vista 下运行时进行工具栏的子类化。

默认操作:可重写成员

  • bool aero::CEmptyDialogImpl::Init(LPARAM lParam) 执行 WTL::CEmptyDialogImpl::Init(LPARAM lParam),如果存在工具栏,则有条件地将其子类化为 aero::CToolBarCtrl

  • void aero::CEmptyDialogImpl::Paint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest) 不执行任何操作。请注意,如果您想绘制空对话框区域,则应该重写或专门化此成员。

aero::CControlDialog

aero::CControlDialogWTL::CControlDialog 的 Aero 启用版本。它派生自 WTL::CControlDialogImpl,并通过 TDlgImpl 模板参数间接派生自 aero::CEmptyDialogImpl

TCtrl 类必须启用 Aero 才能在 Vista 下正确渲染。

定义和构造

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::CSplitDialogWTL::CSplitDialog 的 Aero 启用版本。它派生自 WTL::CSplitDialogImpl,并通过 TDlgImpl 模板参数间接派生自 aero::CEmptyDialogImpl

TLeftTRight 类都必须启用 Aero 才能在 Vista 下正确渲染。

定义和构造

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

示例代码

CAeroShellDialog

移动设备适配类

这些类派生自 atlwince.h 中的 CStdDialogImpl,因此它们是所有通用类的移动全屏等效版本。

  • CStdEmptyDialogImpl 是唯一具有可执行代码的类。

  • CStdControlDialog, CStdSplitDialog, CStdHSplitDialog, CStdVSplitDialog 只有定义代码。

CStdEmptyDialogImpl

CStdEmptyDialogImpl 是一个专门化的 CEmptyDialogImpl,它使用 CStdDialogImpl 作为 TBase 模板参数。

定义

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;

构造函数

CStdEmptyDialogImpl 有一个不带参数的构造函数,所有大小调整都由 t_shidiFlags 模板参数处理。

数据成员

无。

默认操作:可重写成员

CStdEmptyDialogImpl::Init(LPARAM lParam) 返回 CStdEmptyDialogImpl::DefInit(LPARAM lParam)

所有其他默认操作均由 CEmptyDialogImpl 处理,可通过 BaseEmptyDialog:: 名称访问。

辅助成员

  • DefInit(),默认初始化成员,将 DoModal() lParam 复制到 m_Data,释放 DLGTEMPLATE 内存,创建一个菜单栏(如果找到 t_uIDD SHMENUBAR 资源,则从该资源创建,否则从 ATL_IDM_MENU_DONECANCEL 创建),最后执行 CStdDialogBase 初始化。为了兼容 eVC/ATL3,DefInit() 返回 false。

菜单栏命令按钮辅助函数

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

派生类

CStdControlDialog, CStdSplitDialog, CStdVSplitDialog, CStdHSplitDialog 间接派生自 CStdEmptyDialogImpl

CStdControlDialog

CStdControlDialog 是一个 CControlDialog,它使用 CStdEmptyDialogImpl 作为 TDlgImpl 模板参数。

定义

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

示例代码

CMyDateDialog

CStdSplitDialog

CStdSplitDialog 是一个专门化的 CSplitDialog,它使用 CStdEmptyDialogImpl 作为 TDlgImpl 模板参数。

定义

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

CStdHSplitDialogCStdVSplitDialog 是专门化的 CHSplitDialogCVSplitDialog,它们使用 CStdEmptyDialogImpl 作为 TDlgImpl 模板参数。

定义

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

示例代码

CMyFileDialog

示例

三个相似的 DialogX 应用程序展示了引用类别的应用用法:一个预 Vista 和一个支持 Vista 的桌面应用程序,以及一个支持所有移动设备(从 SmartPhone 2003 和 PPC 2002 到 Windows Mobile 6)的 Windows Mobile 应用程序(包含平台适配)。

要访问示例,请将 DialogX.zip 下载并解压到您选择的<位置>

VS2005VCExpress:打开 <位置>\DialogX\DialogX.sln 来处理 <位置>\DialogX\Desktop\Desktop.vcproj 和(仅限 VS2005)<位置>\DialogX\Mobile\DialogX.vcproj

VC.NET 2003:打开 <位置>\DialogX\Desktop\DialogX.sln 来处理 <位置>\DialogX\Desktop\DialogX.vcproj

eVC 3/4:打开 <位置>\DialogX\Mobile\DialogX.vcw 来处理 <位置>\DialogX\Mobile\DialogX.vcp

VC6 VC.NET 2002:使用 WTL AppWizard 创建一个简单的 SDI DialogX 项目,不含 CPP 文件且无视图类,将生成的项目文件复制到 <位置>\DialogX\Desktop\ 并打开它。

三个示例的大部分应用代码是通用的,因此它被分组在 <位置>\DialogX\DialogX.h 中。

CMyToolMenu

CMyToolMenu 实例化了一个带有工具栏 BITMAP 资源的 CCellMenu

typedef CCellMenu<IDR_MAINFRAME32, 6, 1> CMyToolMenu;

CMainFrame::ToolMenu(POINT pt) 调用 CMyToolMenu::TrackCellMenu(pt),使用提供的 POINT 进行默认的 TPM_LEFTALIGN | TPM_TOPALIGN 定位。当不支持 32 位颜色时,将使用 8 位颜色的 CCellMenu<IDR_MAINFRAME, 6, 1>

// 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 专门化 CCellMenu::PaintCell 以绘制颜色表。

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

// 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) 调用 CMyColorMenu::TrackCellMenu(pt, TPM_TOPALIGN, m_icolor),传入当前背景颜色的索引,并在发生选择时更新背景。

// MainFrame.h

// ...  

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

CMyDateDialog

CMyDateDialog 实例化了一个带有月历控件专门化的 CControlDialog

专门化的 bool CMyDateDialog::ControlDialog::Notify(int, LPNMHDR pnmh) 将月历控件的 MCN_SELECT 通知作为验证进行处理。

专门化的 bool CMyDateDialog::ControlDialog::Command(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/) 将选定的日期复制到 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() 实例化一个 CMyDateDialog 和一个 SYSTEMTIME 结构,用 SYSTEMTIME 的地址调用 DoModal(),并在发生选择时更新状态栏的第 1 个窗格。

// 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 是一个简单的类,它由状态栏控件的 HWND 构建,只有一个成员:bool EditPane(int iPane)

EditPane 计算一个适合编辑窗格的矩形,获取窗格文本,用这些参数调用 CPaneEditor::Edit,并在编辑发生时用编辑后的文本更新窗格。

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) 实例化一个带有 m_hWndStatusBarCStatusPaneEditor 并调用 EditPane(iPane)

void EditStatusPane(int iPane)
{
        CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane);
}

CMyShellDialog

CMyShellDialog 是一个 CVSplitDialog,它使用了 Bjarke ViksøeShell 控件(经许可)。

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

ID_SHELLDIALOG TOOLBAR 资源实现了 CSplitDialogImpl 处理的命令。

ID_SHELLDIALOG TOOLBAR  16, 16
BEGIN
    BUTTON      ID_WINDOW_SPLIT
    BUTTON      ID_NEXT_PANE
    BUTTON      ID_PREV_PANE
    BUTTON      IDOK
END

CMyShellDialog 专门化了三个 SplitDialog 成员:

  • bool CMyShellDialog::SplitDialog::Init(LPARAM lParam) 执行默认的 SplitDialog 初始化、特定的 Shell 控件初始化、工具栏初始化并返回 true。

  • bool CMyShellDialog::SplitDialog::Command(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/) 调整 ID_WINDOW_SPLIT 按钮的状态,并在命令是 IDOK 时将选定的路径复制到 m_Data

  • bool CMyShellDialog::SplitDialog::Notify(int idCtrl, LPNMHDR pnmh) 处理 NM_SETFOCUSNM_CLICKTVN_SELCHANGED 通知。

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 派生自 CMyShellDialog 并实现了一个右窗格上下文菜单。

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() 分配一个大小为 MAX_PATHCString,实例化一个 CBjarkeShellDialog,并使用字符串作为 lParam 调用 DoModal。返回后,如果发生了选择,状态栏的第 2 个窗格将使用选定的路径进行更新。

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 版

当此示例在 Vista 下运行时,Explore 命令允许用户在支持 Aero 的 CBjarkeShellDialog 或使用 Vista IExplorerBrowser 接口的 CMyVistaShellDialog 之间进行选择。在 Vista 之前的系统上,没有区别。

CMyVistaShellDialog

CMyVistaShellDialog 是一个通过 Vista IExplorerBrowser 接口操作的 CEmptyDialogImpl

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

为了更好的用户交互,CMyVistaShellDialog 应该实现 IExplorerBrowserEvents 接口,这超出了本文的范围,留给您作为家庭作业。

CAeroShellDialog 和 AeroShell 控件

CAeroShellTreeCtrlCAeroShellListCtrlaero::CCtrlImplBjarke ViksøeCShellTreeCtrlCShellListCtrl 的适配。

#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 实例化 aero::CSplitDialog,使用 aero::CSplitterImpl<CAeroShellDialog, true> 作为 TSplitImpl 参数,CAeroShellTreeCtrl 作为 TLeft 参数,CAeroShellListCtrl 作为 TRight 参数。

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

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

当使用 Vista SDK 编译并在 Vista 上运行时,CMainFrame::ShellDialog() 调用 int CMainFrame::SelectShellDialog(),它实例化一个 WTL::CTaskDialog,允许用户选择 CBjarkeShellDialogCMyVistaShellDialog

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

此示例的功能等同于桌面 DialogX,目标是所有 Windows Mobile 设备,从 PPC 2002 和 SmartPhone 2003 到 Windows Mobile 6。一些类略有不同,但主要区别在于框架窗口代码。

Mobile CMyToolMenu

CMyToolMenu 实例化了一个带有工具栏 BITMAP 资源的 CCellMenu,并在使用 VS 2005 编译时,使用 DRA 命名空间函数根据屏幕分辨率缩放单元格和位图。

#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) 调用 CMyToolMenu::TrackCellMenu(pt),使用提供的 POINT 进行 TPM_BOTTOMALIGN 定位。

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

请参阅桌面 CMyColorMenu 的描述。

CDialogXFrame::ColorMenu() 计算底部中心点,按下 ID_COLORMENU 按钮(仅存在于 PPC2003 上),使用当前背景颜色的索引调用 CMyColorMenu::TrackCellMenu(pt, TPM_CENTERALIGN | TPM_BOTTOMALIGN, m_icolor),释放按钮并在发生选择时更新背景。

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

桌面实现的唯一区别是 CMyDateDialog 实例化了一个带有月历控件专门化的 CStdControlMenu

Mobile CPaneEditor

移动设备中的 CPaneEditor 使用一个特殊的 CEditPaneCtrl 来处理 EDIT 控件行为在某些 WM5 中的差异,并允许使用上/下键来退出。

在 PPC 平台上,CPaneEditor 会从系统请求 SIP。

#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

对于 PPC 平台,CDialogXFrame::EditStatusPane(int iPane) 在调用 CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane) 之前显示 SIP,之后将其隐藏。

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 和 CFileListCtrl

这些实现的子类化 WTL::CTreeViewCtrlExWTL::CSortListViewCtrlImpl 来显示文件项,与许多其他实现非常相似,除了 TFilterTFinder 模板参数。两个类都需要

  • 一个 class TFilter 模板参数,它可以是任何暴露 bool operator()(TFinder& ff) 的类。这种设计使得根据任何条件(包括运行时条件、外部权限或文件内容)选择文件变得非常容易。

  • 一个 class TFinder 模板参数,它默认为 WTL::CFindFile,但可以是类似的或派生类,通过其他方式访问文件(例如设备远程 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;   

这两个类都位于 <位置>\DialogX\Device\FileControls.h 中,并且不严格限制于 Windows Mobile 平台。

CMyFileDialog

CMyFileDialog 是一个 CStdHSplitDialog,它使用 CDirTreeCtrl 作为 TLeft 参数,使用 CFileListCtrl 作为 TRight 参数。

#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 专门化了 PPC 平台的一个 EmptyDialog 成员,以及始终三个 SplitDialog 成员。

  • 对于 PPC 平台,bool CMyFileDialog::EmptyDialog::Init(LPARAM lParam) 在默认的 SplitDialog 初始化之前加载一个图形菜单栏(如果运行在 PPC 2002/2003 平台上)。

  • bool CMyFileDialog::SplitDialog::Init(LPARAM lParam) 执行默认的 SplitDialog 初始化、特定的文件控件初始化,并为了兼容 eVC/ATL3 返回 false

  • bool CMyFileDialog::SplitDialog::Command(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/) 如果命令是 IDOK,则将选定的路径复制到 m_Data,否则返回 SplitDialog::PaneCommand(wID)

  • bool CMyFileDialog::SplitDialog::Notify(int idCtrl, LPNMHDR pnmh) 使用 NM_SETFOCUS 来调整 PPC2003 菜单栏按钮和其他平台左侧按钮的文本,并处理 NM_RETURNNM_CLICKFTCN_SELECT 通知。

#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() 创建一个包含反斜杠的 CString,实例化一个 CMyFileDialog,并使用扩展到 MAX_PATH 的字符串缓冲区作为 lParam 调用 DoModal。返回后,如果发生了选择,状态栏将使用选定的路径进行更新。

结论

我们可能还需要等待几年,MS 编译器才能实现 C++0x

在此期间,借助当前 C++ 实现和 WTL 库的帮助,可以为许多不同的平台编写高效的可重用代码来访问 Win32 API。

修订历史

  • 2007 年 11 月 6 日:发布
  • 2007 年 11 月 12 日:文章编辑并移至 CodeProject.com 的主要文章库
© . All rights reserved.