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






4.89/5 (40投票s)
包含单元格菜单、就地编辑器、单个控件和分割对话框、Vista Shell以及移动设备文件控件和选择对话框
![]() |
![]() |
谢谢
本文中展示的所有代码均使用 WTL 8.0。感谢 Nenad Stefanovic 和所有 WTL 开发者。
桌面示例在获得许可的情况下使用了 Bjarke Viksøe 的 shell 控件。感谢 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,因为我们将使用
CPoint
和CString
。
// 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.h 从 atldlgx.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.h 和 WtlAero.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 窗口。

我们定义 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
。使用键盘选择问号或单击它来调出“关于”对话框,执行任何其他鼠标或键盘操作来关闭菜单。
请参阅示例部分的 CMyToolMenu 和 CMyColorMenu。
步骤 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;
}
编译、运行并右键单击。

我们定义 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;
// ...

我们**专门化**了 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.h 从 atldlgx.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 按钮。

我们定义了一个 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 按钮。

我们定义 CDateDialog
为一个 CStdControlDialog,它托管了一个 CMonthCalendarCtrl
,ID 为 ID_TEST
。
我们实例化了一个 CDateDialog
并对其调用了 DoModal()
。
要使用选定的日期,我们应该添加一些调用和专门化代码。在移动设备示例部分,更多关于 Mobile CMyDateDialog 的内容。
参考
-
平台独立类
辅助类
-
CEmptyDlgTemplate 是一个
WTL::CMemDlgTemplate
,它在其Create(POINT point, SIZE size)
成员中从资源字符串加载对话框标题。要完全定义一个
CEmptyDlgTemplate
,您需要指定一个字符串资源 ID,以及可选的ATL::CWinTraits
实例作为样式。 -
CEmptyDlgTraits、CControlDlgTraits、CSplitDlgTraits 是各种
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<>
,但移动设备的 CStdEmptyDialogImpl
的 TBase
是 WTL::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::CEmptyDialogImpl 和 CStdEmptyDialogImpl 使用特定的 TBase
模板参数。
CControlDialogImpl, CSplitDialogImpl 是派生自 CEmptyDialogImpl
的基类。
CCellMenu 和 CMyVistaShellDialog 是直接派生自 CEmptyDialogImpl
的用户类。
CCellMenu
CCellMenu
旨在取代传统的菜单,适用于矩形单元格布局的场景。它使用 CMenuDialogTraits
样式。
定义
CCellMenu
派生自 CEmptyDialogImpl 和 CMenuDialog。
该类定义使用了三个模板参数:一个位图资源 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_CXSMICON
和 SM_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
是定位参考点。 -
uFlags
是TrackPopupMenu
(TPM_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;
}
示例用法
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
有一个类型为 TCtrl
的 m_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_KEYFIRST
到WM_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 同时派生自 CControlDialogImpl
和 CMenuDialog。
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 相同。
示例用法
CInPlaceEditor
CInPlaceEditor
旨在允许在特定矩形中就地编辑已知文本。
定义
CInPlaceEditor
派生自 CControlDialogImpl 和 CMenuDialog。
该类定义使用了三个模板参数:编辑文本的最大长度,编辑控件类(默认为 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())
进行操作。
-
rEdit
是CInPlaceEditor
父坐标中的定位矩形。 -
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_PANE
和ID_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
CVSplitDialog
和 CHSplitDialog
是可专门化的 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::CControlDialog
是 WTL::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::CSplitDialog
是 WTL::CSplitDialog 的 Aero 启用版本。它派生自 WTL::CSplitDialogImpl,并通过 TDlgImpl
模板参数间接派生自 aero::CEmptyDialogImpl。
TLeft
和 TRight
类都必须启用 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){}
};
示例代码
移动设备适配类
这些类派生自 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;
};
示例代码
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
CStdHSplitDialog
和 CStdVSplitDialog
是专门化的 CHSplitDialog
和 CVSplitDialog,它们使用 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 下载并解压到您选择的<位置>。
VS2005 和 VCExpress:打开 <位置>\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 中。
-
DialogX:如果您的开发环境无法访问 Vista SDK,您将编译并运行此示例。
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_hWndStatusBar
的 CStatusPaneEditor
并调用 EditPane(iPane)
。
void EditStatusPane(int iPane)
{
CStatusPaneEditor(m_hWndStatusBar).EditPane(iPane);
}

CMyShellDialog
CMyShellDialog
是一个 CVSplitDialog,它使用了 Bjarke Viksøe 的 Shell 控件(经许可)。
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_SETFOCUS
、NM_CLICK
和TVN_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, π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(), πdl)
!= FALSE)
m_menu.TrackPopupMenu(pidl, GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam), m_hWnd);
}
return 0;
}
};

void CMainFrame::ShellDialog()
分配一个大小为 MAX_PATH
的 CString
,实例化一个 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 控件
CAeroShellTreeCtrl
和 CAeroShellListCtrl
是 aero::CCtrlImpl 对 Bjarke Viksøe 的 CShellTreeCtrl
和 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
实例化 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
,允许用户选择 CBjarkeShellDialog 或 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
此示例的功能等同于桌面 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::CTreeViewCtrlEx
和 WTL::CSortListViewCtrlImpl
来显示文件项,与许多其他实现非常相似,除了 TFilter
和 TFinder
模板参数。两个类都需要
-
一个
class TFilter
模板参数,它可以是任何暴露bool operator()(TFinder& ff)
的类。这种设计使得根据任何条件(包括运行时条件、外部权限或文件内容)选择文件变得非常容易。 -
一个
class TFinder
模板参数,它默认为WTL::CFindFile
,但可以是类似的或派生类,通过其他方式访问文件(例如设备远程 APICeFindxxxFile()
)。
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_RETURN
、NM_CLICK
和FTCN_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 的主要文章库