テクニカル ノート 26: DDX ルーチンおよび DDV ルーチン
Note
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
このノートでは、ダイアログ データ エクスチェンジ (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
をいつでも呼び出して、データ エクスチェンジと検証を実行できます。 既定では、UpdateData
(TRUE) は既定の CDialog::OnOK
ハンドラー内で呼び出され、UpdateData
(FALSE) は既定の CDialog::OnInitDialog
内で呼び出されます。
DDV_ ルーチンは、その "フィールド" の DDX_ ルーチンの直後に配置する必要があります。
しくみ
ダイアログ データを使用するのに、次を理解する必要はありません。 ただし、これがバックグラウンドでどのように動作しているかを理解すると、実際のエクスチェンジまたは検証プロシージャを記述する際に役立ちます。
DoDataExchange
メンバー関数は、Serialize
メンバー関数によく似ています。外部フォーム (この場合はダイアログ内のコントロール) とクラスのメンバー データとの間でデータを取得または設定します。 pDX パラメーターは、データ エクスチェンジを行うためのコンテキストであり、CObject::Serialize
に対する CArchive
パラメーターと似ています。 pDX (CDataExchange
オブジェクト) には、CArchive
に方向フラグがあるのと同様に、方向フラグがあります。
!m_bSaveAndValidate
の場合は、データの状態をコントロールに読み込みます。m_bSaveAndValidate
の場合は、データの状態をコントロールから設定します。
検証は、m_bSaveAndValidate
が設定されている場合にのみ行われます。 m_bSaveAndValidate
の値は、CWnd::UpdateData
に対する BOOL パラメーターによって決まります。
他にも 3 つの興味深い CDataExchange
メンバーがあります。
m_pDlgWnd
: コントロールを含むウィンドウ (通常はダイアログ)。 これは、DDX_ および DDV_ グローバル関数の呼び出し元が、すべての DDX/DDV ルーチンに "this" を渡す必要をなくすためです。PrepareCtrl
およびPrepareEditCtrl
: データ エクスチェンジ用のダイアログ コントロールを準備します。 検証が失敗した場合にフォーカスを設定するためのコントロールのハンドルを格納します。PrepareCtrl
は非編集コントロールに使用し、PrepareEditCtrl
は編集コントロールに使用します。Fail
: ユーザーに入力エラーを警告するメッセージ ボックスを表示した後に呼び出されます。 このルーチンは、最後のコントロール (PrepareCtrl
またはPrepareEditCtrl
の最後の呼び出し) にフォーカスを復元し、例外をスローします。 このメンバー関数は、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);
Note
そうした任意の式は 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);
Note
上記のように、このようなコードは ClassWizard で編集することはできません。また、特殊な形式のコメントの外部でのみ使用する必要があります。
ClassWizard のサポート
ClassWizard では、自前の DDX_ と DDV_ ルーチンを ClassWizard ユーザー インターフェイスに統合できるようにすることで、DDX/DDV のカスタマイズのサブセットをサポートしています。 これを行うと、特定の DDX と DDV ルーチンを 1 つのプロジェクトまたは多数のプロジェクトで再利用する予定がある場合にのみ費用対効果があります。
これを行うには、DDX.CLW (以前のバージョンの Visual C++ では、この情報は APSTUDIO.INI に格納されます) またはプロジェクトの .CLW ファイル内に特殊なエントリを作成します。 特殊なエントリは、プロジェクトの .CLW ファイルの [General Info] セクション、または \Program Files\Microsoft Visual Studio\Visual C++\bin ディレクトリにある DDX.CLW ファイルの [ExtraDDX] セクションに入力できます。 DDX.CLW ファイルが存在しない場合は、それを作成する必要があることがあります。 特定のプロジェクト内でのみカスタム DDX_/DDV_ ルーチンを使用する予定の場合は、代わりにプロジェクトの .CLW ファイルの [General Info] セクションにエントリを追加します。 多数のプロジェクトでルーチンを使用する予定の場合は、DDX.CLW の [ExtraDDX] セクションにエントリを追加します。
これらの特殊なエントリの一般的な形式は次のとおりです。
ExtraDDXCount=n
n はフォームの、後に続く ExtraDDX? 行の数です
ExtraDDX?=keys; vb-keys; prompt; type; initValue; DDX_Proc [; DDV_Proc; prompt1; arg1 [; prompt2; fmt2]]
? は、定義しているリスト内の DDX 型を示す 1 - n の数値です。
各フィールドは、";" 文字で区切られています。 フィールドとその目的について以下に説明します。
keys
この変数型が許可されるダイアログ コントロールを示す 1 文字のリスト。
文字 許可されるコントロール E edit C 2 つの状態のチェック ボックス c 3 つの状態のチェック ボックス R グループ内の最初のラジオ ボタン L 並べ替えなしのリスト ボックス l 並べ替えありのリスト ボックス M コンボ ボックス (編集項目を含む) N 並べ替えなしのドロップダウン リスト n 並べ替えありのドロップダウン リスト 1 DDX の挿入をリストの先頭に追加する必要がある場合 (既定では末尾に追加します)。これは、通常、"Control" プロパティを転送する DDX ルーチンに使用します。 vb-keys
このフィールドは、VBX コントロールの 16 ビット製品でのみ使用します (VBX コントロールは 32 ビット製品ではサポートされません)
prompt
[プロパティ] コンボ ボックスに配置する文字列 (引用符なし)
type
ヘッダー ファイルに出力する型の単一識別子。 上記の DDX_Time の例では、これを CTime に設定しています。
vb-keys
このバージョンでは使用されません。常に空にする必要があります
initValue
初期値 - 0 または空白。 これが空白の場合、実装ファイルの //{{{AFX_DATA_INIT セクションに初期化行が書き込まれません。 空白のエントリは、正しい初期化を保証するコンストラクターがある C++ オブジェクト (
CString
、CTime
など) に使用する必要があります。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
引数の型の書式文字。次のいずれかです。
文字 Type d int u unsigned int D long int (つまり、long) U long unsigned (つまり、DWORD) f float F 倍精度浮動小数点 s string