TN026:DDX 和 DDV 例程

注意

以下技术说明在首次包括在联机文档中后未更新。 因此,某些过程和主题可能已过时或不正确。 要获得最新信息,建议你在联机文档索引中搜索热点话题。

本说明介绍对话框数据交换 (DDX) 和对话框数据验证 (DDV) 体系结构。 还介绍如何编写 DDX_ 或 DDV_ 过程,以及如何扩展 ClassWizard 以使用例程。

对话框数据交换概述

所有对话框数据函数都使用 C++ 代码完成。 没有特殊的资源或神奇的宏。 该机制的核心是在每个对话框类中重写的虚函数,它完成对话框数据交换和验证。 它的形式始终如下:

void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);   // call base class

    //{{AFX_DATA_MAP(CMyDialog)
        <data_exchange_function_call>
        <data_validation_function_call>
    //}}AFX_DATA_MAP
}

特殊格式 AFX 注释允许 ClassWizard 在此函数中查找和编辑代码。 与 ClassWizard 不兼容的代码应放置在特殊格式注释之外。

在上面的示例中, <data_exchange_function_call> 采用以下形式:

DDX_Custom(pDX, nIDC, field);

<data_validation_function_call> 是可选的,形式如下:

DDV_Custom(pDX, field, ...);

每个 DoDataExchange 函数中可以包含多个 DDX_/DDV_ 对。

有关 MFC 提供的所有对话框数据交换例程和对话框数据验证例程的列表,请参阅“afxdd_.h”。

对话框数据只是:CMyDialog 类中的成员数据。 它不存储在结构或类似内容中。

说明

虽然我们称之为“对话框数据”,但所有功能都可用于派生自 CWnd 的任何类,而不仅限于对话框。

数据的初始值在标准 C++ 构造函数中设置,通常在具有 //{{AFX_DATA_INIT//}}AFX_DATA_INIT 注释的块中设置。

CWnd::UpdateData 操作围绕 DoDataExchange 调用执行初始化和错误处理。

可以随时调用 CWnd::UpdateData 以执行数据交换和验证。 默认情况下,在默认 CDialog::OnOK 处理程序中调用 UpdateData(TRUE),在默认 CDialog::OnInitDialog 中调用 UpdateData(FALSE)。

对于该字段 DDV_ 例程应紧跟 DDX_ 例程

工作原理

无需了解以下内容即可使用对话框数据。 但了解幕后工作原理有助于编写自己的交换或验证过程。

DoDataExchange 成员函数非常类似于 Serialize 成员函数 - 它负责为外部窗体(本例中为对话框控件)获取类中成员数据,或者将外部窗体(本例中为对话框控件)数据设置为类中成员数据。 pDX 参数是执行数据交换的上下文,类似于 CObject::SerializeCArchive 参数。 pDX(CDataExchange 对象)拥有方向标志,如同像 CArchive 拥有方向标志

  • 如果为 !m_bSaveAndValidate,将数据状态加载到控件。

  • 如果为 m_bSaveAndValidate,设置控件的数据状态。

仅当设置 m_bSaveAndValidate 时才进行验证。 m_bSaveAndValidate 的值由 CWnd::UpdateData 的 BOOL 参数确定。

还有三个有趣的 CDataExchange 成员:

  • m_pDlgWnd:包含控件的窗口(通常是对话框)。 这是为了防止 DDX_ 和 DDV_ 全局函数的调用方不得不将“this”传递给每个 DDX/DDV 例程。

  • PrepareCtrlPrepareEditCtrl:准备用于数据交换的对话框控件。 存储控件的句柄,以便在验证失败时设置焦点。 PrepareCtrl 用于非编辑控件,PrepareEditCtrl 用于编辑控件。

  • Fail:在显示消息框提醒用户输入错误后受到调用。 此例程将焦点还原到最近的控件(上次调用 PrepareCtrlPrepareEditCtrl)并引发异常。 可以从 DDX_ 和 DDV_ 例程调用此成员函数。

用户扩展

可通过多种方式扩展默认 DDX/DDV 机制。 可以:

  • 添加新数据类型。

    CTime
    
  • 添加新的交换过程 (DDX_)。

    void PASCAL DDX_Time(CDataExchange* pDX, int nIDC, CTime& tm);
    
  • 添加新的验证过程 (DDV_)。

    void PASCAL DDV_TimeFuture(CDataExchange* pDX, CTime tm, BOOL bFuture);
    // make sure time is in the future or past
    
  • 将任意表达式传递到验证过程。

    DDV_MinMax(pDX, age, 0, m_maxAge);
    

    注意

    此类任意表达式不能由 ClassWizard 编辑,因此应移出特殊格式注释 (//{{AFX_DATA_MAP(CMyClass))。

使 DoDataExchange 成员函数包含条件或包含具有混合交换和验证函数调用的任何其他有效 C++ 语句。

//{{AFX_DATA_MAP(CMyClass)
DDX_Check(pDX, IDC_SEX, m_bFemale);
DDX_Text(pDX, IDC_EDIT1, m_age);
//}}AFX_DATA_MAP
if (m_bFemale)
    DDV_MinMax(pDX, age, 0, m_maxFemaleAge);
else
    DDV_MinMax(pDX, age, 0, m_maxMaleAge);

注意

如上所示,此类代码不能由 ClassWizard 编辑,只应在特殊格式注释之外使用。

ClassWizard 支持

ClassWizard 让你能够将自己的 DDX_ 和 DDV_ 例程集成到 ClassWizard 用户界面,为 DDX/DDV 自定义的子集提供支持。 只有打算在一个或众多项目中重复使用特定 DDX 和 DDV 例程时,这样做才具有成本效益。

为此,在 DDX.CLW(以前版本的 Visual C++ 中将此信息存储在 APSTUDIO.INI)或项目的 .CLW 文件中生成特殊条目。 可以在项目 .CLW 文件 [常规信息] 部分或 \Program Files\Microsoft Visual Studio\Visual C++\bin 目录中 DDX.CLW 文件的 [ExtraDDX] 部分输入特殊条目。 如果 DDX.CLW 文件尚不存在,可能需要创建它。 如果打算仅在某个项目使用自定义 DDX_/DDV_ 例程,请改而将条目添加到项目 .CLW 文件的 [常规信息] 部分。 如果打算对许多项目使用例程,请将条目添加到 DDX.CLW 的 [ExtraDDX] 部分。

这些特殊条目的一般格式为:

ExtraDDXCount=n

其中 n 是后面的 ExtraDDX? 行数,其形式如下

ExtraDDX?=keys; vb-keys; prompt; type; initValue; DDX_Proc [; DDV_Proc; prompt1; arg1 [; prompt2; fmt2]]

where ? 是数字 1 - n,指示列表中正定义的 DDX 类型

每个字段都用“;”字符分隔。 下面介绍了这些字段及其用途。

  • keys

    单个字符的列表,指示哪些对话框控件允许此变量类型。

    字符 允许的控件
    E edit
    C 两态复选框
    c 三态复选框
    R 组中第一个单选按钮
    L 非排序列表框
    l 排序列表框
    M 组合框(含编辑项)
    N 非排序下拉列表
    n 排序下拉列表
    1 如果应将 DDX 插入添加到列表头(默认添加到尾)。这通常用于传输“Control”属性的 DDX 例程。
  • vb-keys

    此字段仅在 16 位产品中用于 VBX 控件(不支持在 32 位产品中使用 VBX 控件)

  • prompt

    要放入属性组合框中的字符串(没有引号)

  • type

    标头文件中要发出的类型的单个标识符。 在上面有 DDX_Time 的示例中,这会设为 CTime。

  • vb-keys

    此版本未使用,应始终为空

  • initValue

    初始值 - 0 或空白。 如果为空白,则不会在实现文件的 //{{AFX_DATA_INIT 部分中写入任何初始化行。 如果 C++ 对象具有保证正确初始化的构造函数(如 CStringCTime),则应对其使用空白条目。

  • DDX_Proc

    DDX_ 过程的单个标识符。 C++ 函数名称必须以“DDX_”开头,但不要在 <DDX_Proc> 标识符中包括“DDX_”。 在上面的示例中,<DDX_Proc> 标识符为 Time。 当 ClassWizard 将函数调用写入实现文件的 {{AFX_DATA_MAP 部分时,它将此名称追加到 DDX_,结果变为 DDX_Time。

  • comment

    要在对话框中为具有此 DDX 的变量而显示的注释。 在此处放入想要的任何文本,通常提供描述 DDX/DDV 对所执行操作的内容。

  • DDV_Proc

    条目的 DDV 部分是可选的。 并非所有 DDX 例程都有相应的 DDV 例程。 通常,更为便捷的做法是将验证阶段作为传输不可或缺的一部分包括在内。 这通常适用于 DDV 例程不需要任何参数的情况,因为 ClassWizard 不支持不使用任何参数的 DDV 例程。

  • arg

    DDV_ 过程的单个标识符。 C++ 函数名称必须以“DDV_”开头,但不要在 <DDX_Proc> 标识符中包括“DDX_”。

    arg 后跟 1 个或 2 个 DDV 参数:

    • promptN

      要放置在编辑项上方的字符串(包含用于加速器的 &)。

    • fmtN

      参数类型的格式字符,包括:

      字符 类型
      d int
      u unsigned int
      D long int(即 long)
      U long unsigned(即 DWORD)
      f float
      F double
      s string

另请参阅

按编号列出的技术说明
按类别列出的技术说明