テクニカル ノート 26: DDX ルーチンおよび DDV ルーチン

更新 : 2007 年 11 月

57weza95.alert_note(ja-jp,VS.90).gifメモ :

次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。

ここでは、ダイアログ データ エクスチェンジ (DDX: Dialog Data eXchange) とダイアログ データ バリデーション (DDV: Dialog Data Validation) のアーキテクチャについて説明します。また、DDX_ プロシージャと DDV_ プロシージャを記述する方法、ClassWizard を拡張して独自のルーチンを使用する方法についても説明します。

ダイアログ データ エクスチェンジの概要

ダイアログ データ エクスチェンジを行う関数はすべて C++ コードで記述されています。特殊なマクロやリソースは使用されていません。DDX/DDV 機構の中心は仮想関数で、ダイアログ データの交換と検証を行うすべてのダイアログ ボックス クラスでオーバーライドされます。これらの関数は次の形式で記述します。

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 (Microsoft Foundation Class) で使用できる DDX/DDV 関数については、「afxdd_.h」を参照してください。

ダイアログ データとは、CMyDialog クラスのメンバ データのことです。このデータは、struct 型などのデータ構造には格納されていません。

メモ

ダイアログ データの機能は、ダイアログ クラスだけではなく、すべての CWnd 派生クラスで使用できます。

データの初期値は C++ 標準コンストラクタによって (通常はコメント ブロック //{{AFX_DATA_INIT、//}}AFX_DATA_INIT の中で) 設定されます。

CWnd::UpdateData は、DoDataExchange の呼び出しに関連した初期化とエラー処理を行います。

CWnd::UpdateData は、データの交換および検証を行うために任意の時点で呼び出せます。既定では、既定の CDialog::OnOK ハンドラの中で UpdateData (TRUE) が呼び出され、既定の CDialog::OnInitDialog の中で UpdateData (FALSE) が呼び出されます。

DDX_ ルーチンの直後に、同じ field に対する DDV_ ルーチンを呼び出します。

動作原理

ダイアログ データを使うだけの場合は、この後の内容を理解する必要はありません。しかし、処理の背後にある動作原理を理解することによって、独自の DDX/DDV 処理を作成できるようになります。

DoDataExchange メンバ関数は Serialize メンバ関数と同じように、外部フォーム (ダイアログ中のコントロール) とクラスのメンバとの間でデータを取得したり設定したりします。パラメータ pDX はデータ交換のコンテキストを指定します。これは CObject::SerializeCArchive パラメータに相当します。pDX (CDataExchange オブジェクト) は、CArchive と同じように、データの転送方向を示すフラグを持っています。

  • !m_bSaveAndValidate が指定されている場合、コントロールにデータの状態が読み込まれます。

  • m_bSaveAndValidate が指定されている場合、コントロールのデータがクラス メンバに設定されます。

データ検証は、m_bSaveAndValidate フラグが設定されている場合だけ実行されます。m_bSaveAndValidate の値は、CWnd::UpdateData に渡す BOOL パラメータによって決定されます。

この他に、CDataExchange には次に示す 3 種類のメンバがあります。

  • 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);
    
    57weza95.alert_note(ja-jp,VS.90).gifメモ :

    ClassWizard ではこのような任意の式を含むコードを処理できないため、このようなコードは特殊コメント ブロック (//{{AFX_DATA_MAP(CMyClass)) の外側に記述します。

DoDialogExchange メンバ関数の中に、条件分岐やその他の 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);
57weza95.alert_note(ja-jp,VS.90).gifメモ :

ClassWizard では上の例のようなコードを編集できないので、このようなコードは特殊コメント ブロックの外側に記述します。

ClassWizard のサポート

ClassWizard は DDX/DDV のカスタマイズを一部サポートしているので、独自の DDX_/DDV_ ルーチンを ClassWizard のユーザー インターフェイスに組み込むことができます。これにより、1 つあるいは複数のプロジェクトで同じ DDX / DDV ルーチンを繰り返して使用する場合は費用効果が上がります。

カスタマイズするには、DDX.CLW やプロジェクトの .CLW ファイルの中に専用エントリを作成します。以前のバージョンの Visual C++ ではこの情報は APSTUDIO.INI に格納していました。この専用エントリは、プロジェクトの .CLW ファイルの [General Info] セクションか、\Program Files\Microsoft Visual Studio\Visual C++\bin ディレクトリの DDX.CLW ファイルの [ExtraDDX] セクションに記述できます。DDX.CLW ファイルが存在しない場合、このファイルを作成する必要があります。カスタム DDX_/DDV_ ルーチンを 1 つのプロジェクトの中だけで使用する場合は、そのプロジェクトの .CLW ファイルの [General Info] セクションにエントリを記述します。複数のプロジェクトで DDX_/DDV_ ルーチンを使用するときは、DDX.CLW の [ExtraDDX] セクションにエントリを記述します。

これらの専用エントリは次の形式で記述します。

ExtraDDXCount=n

n は ExtraDDX? 行の総数です。

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

? は 1 から n までの数値で、リスト中の定義する DDX 型を示します。

各フィールドはセミコロン (;) で区切ります。各フィールドの内容を次に示します。

  • <keys>
    = この変数型を使えるダイアログ コントロールを示す単一文字の一覧

    E = エディット

    C = 2 ステート チェック ボックス

    c = 3 ステート チェック ボックス

    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_" を除いた部分を指定します。上の例では <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_" が付きますが、このフィールドでは "DDV_" を除いた部分を指定します。

この後に DDV 引数を 1 つあるいは 2 つ指定します。

  • <promptX>
    エディット項目の上に表示される文字列。アクセラレータを指定するときは & を付けます。

  • <fmtX>
    引数型を表す文字。次の中から 1 種類を指定します。

    d = int

    u = unsigned

    D = long int (つまり long)

    U = long unsigned (つまり DWORD)

    f = float

    F = double

    s = string

参照

その他の技術情報

番号順テクニカル ノート

カテゴリ別テクニカル ノート