TN026: rotinas DDX e DDV

Observação

A nota técnica a seguir não foi atualizada desde que foi incluída pela primeira vez na documentação online. Como resultado, alguns procedimentos e tópicos podem estar desatualizados ou incorretos. Para obter as informações mais recentes, é recomendável que você pesquise o tópico de interesse no índice de documentação online.

Esta nota descreve a arquitetura de DDX (troca de dados da caixa de diálogo) e DDV (validação de dados da caixa de diálogo). Ela também descreve como escrever um procedimento de DDX_ ou DDV_ e como você pode estender o ClassWizard para usar suas rotinas.

Visão geral da Troca de Dados da Caixa de Diálogo

Todas as funções de dados da caixa de diálogo são feitas com código C++. Não há recursos especiais ou macros mágicas. O cerne do mecanismo é uma função virtual que é substituída em todas as classes da caixa de diálogo que fazem a troca e a validação de dados da caixa de diálogo. Ela é sempre encontrada nesta forma:

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
}

Os comentários AFX em formato especial permitem que o ClassWizard localize e edite o código nessa função. O código que não seja compatível com ClassWizard deve ser colocado fora dos comentários de formato especial.

No exemplo acima, <data_exchange_function_call> está no formulário:

DDX_Custom(pDX, nIDC, field);

e <data_validation_function_call> é opcional e está no formulário:

DDV_Custom(pDX, field, ...);

Mais de um par DDX_/DDV_ pode ser incluído em cada função DoDataExchange.

Consulte “afxdd_.h” para obter uma lista de todas as rotinas de troca de dados da caixa de diálogo e rotinas de validação de dados da caixa de diálogo fornecidas com o MFC.

Os dados da caixa de diálogo são exatamente isso: dados de membro na classe CMyDialog. Eles não são armazenados em um struct ou algo semelhante.

Observações

Embora chamemos isso de "dados da caixa de diálogo", todos os recursos estão disponíveis em qualquer classe derivada de CWnd e não se limitam a apenas a caixas de diálogo.

Os valores iniciais dos dados são definidos no construtor C++ padrão, geralmente em um bloco com comentários //{{AFX_DATA_INIT e //}}AFX_DATA_INIT.

CWnd::UpdateData é a operação que faz a inicialização e o tratamento de erros em torno da chamada para DoDataExchange.

Você pode chamar CWnd::UpdateData a qualquer momento para executar a troca de dados e a validação. Por padrão UpdateData(TRUE) é chamado no manipulador CDialog::OnOK padrão e UpdateData(FALSE) é chamado no padrão CDialog::OnInitDialog.

A rotina de DDV_ deve seguir imediatamente a rotina de DDX_ para esse campo.

Como funciona

Você não precisa entender o seguinte para usar os dados da caixa de diálogo. No entanto, entender como isso funciona nos bastidores ajudará você a escrever seu próprio procedimento de troca ou validação.

A função membro DoDataExchange é muito parecida com a função membro Serialize – ela é responsável por obter ou definir dados de/para um formulário externo (nesse caso, controles em uma caixa de diálogo) de/para dados de membro na classe. O parâmetro pDX é o contexto para fazer a troca de dados e é semelhante ao parâmetro CArchive para o CObject::Serialize. O pDX (um objeto CDataExchange) tem um sinalizador de direção muito parecido com como CArchive tem um sinalizador de direção:

  • Se !m_bSaveAndValidate, carregue o estado dos dados nos controles.

  • Se m_bSaveAndValidate, defina o estado dos dados dos controles.

A validação ocorre somente quando m_bSaveAndValidate estiver definido. O valor de m_bSaveAndValidate é determinado pelo parâmetro BOOL para CWnd::UpdateData.

Há outros três membros CDataExchange interessantes:

  • m_pDlgWnd: a janela (geralmente uma caixa de diálogo) que contém os controles. Isso é para impedir que os chamadores das funções globais DDX_ e DDV_ precisem passar "this" para todas as rotinas DDX/DDV.

  • PrepareCtrl e PrepareEditCtrl: prepara um controle da caixa de diálogo para troca de dados. Armazena essa alça de controle para definir o foco se uma validação falhar. PrepareCtrl é usado para controles de não edição e PrepareEditCtrl é usado para controles de edição.

  • Fail: chamado depois de abrir uma caixa de mensagem alertando o usuário para o erro de entrada. Essa rotina restaurará o foco para o último controle (a última chamada para PrepareCtrl ou PrepareEditCtrl) e lançará uma exceção. Essa função membro pode ser chamada de rotinas DDX_ e DDV_.

Extensões de usuário

Há várias maneiras de estender o mecanismo DDX/DDV padrão. Você poderá:

  • Adicionar novos tipos de dados.

    CTime
    
  • Adicionar novos procedimentos de troca (DDX_).

    void PASCAL DDX_Time(CDataExchange* pDX, int nIDC, CTime& tm);
    
  • Adicionar novos procedimentos de validação (DDV_).

    void PASCAL DDV_TimeFuture(CDataExchange* pDX, CTime tm, BOOL bFuture);
    // make sure time is in the future or past
    
  • Passe expressões arbitrárias para os procedimentos de validação.

    DDV_MinMax(pDX, age, 0, m_maxAge);
    

    Observação

    Essas expressões arbitrárias não podem ser editadas pelo ClassWizard e, portanto, devem ser movidas para fora dos comentários de formato especial (//{{AFX_DATA_MAP(CMyClass)).

Fazer com que a função membro DoDataExchange inclua condicionais ou quaisquer outras instruções C++ válidas com chamadas de função de troca e validação combinadas.

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

Observação

Conforme mostrado acima, esse código não pode ser editado pelo ClassWizard e deve ser usado somente fora dos comentários de formato especial.

Suporte do ClassWizard

O ClassWizard dá suporte a um subconjunto de personalizações de DDX/DDV, permitindo que você integre suas próprias rotinas de DDX_ e DDV_ à interface do usuário ClassWizard. Fazer isso só será benéfico se você planeja reutilizar rotinas específicas de DDX e DDV em um projeto ou em muitos projetos.

Para fazer isso, entradas especiais são feitas em DDX.CLW (versões anteriores do Visual C++ armazenaram essas informações em APSTUDIO.INI) ou no arquivo .CLW do seu projeto. As entradas especiais podem ser inseridas na seção [General Info] do arquivo CLW do seu projeto ou na seção [ExtraDDX] do arquivo DDX.CLW no diretório \Program Files\Microsoft Visual Studio\Visual C++\bin. Talvez seja necessário criar o arquivo DDX.CLW se ele ainda não existir. Se você planeja usar as rotinas personalizadas de DDX_/DDV_ apenas em um determinado projeto, adicione as entradas à seção [General Info] do arquivo CLW do seu projeto. Se você planeja usar as rotinas em muitos projetos, adicione as entradas à seção [ExtraDDX] do DDX.CLW.

O formato geral dessas entradas especiais é:

ExtraDDXCount=n

na qual n é o número de linhas ExtraDDX? a seguir, do formulário

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

na qual ? é um número 1 - n indicando qual tipo DDX na lista que está sendo definido.

Cada campo é delimitado por um caractere ';'. Os campos e sua finalidade estão descritos abaixo.

  • keys

    Uma lista de caracteres únicos que indicam para quais controles de caixa de diálogo esse tipo de variável é permitido.

    Character Controle permitido
    E edit
    C caixa de seleção de dois estados
    c caixa de seleção de três estados
    R primeiro botão de opção em um grupo
    L caixa de listagem não classificada
    l caixa de listagem classificada
    M caixa de combinação (com item de edição)
    N lista suspensa não classificada
    n lista suspensa classificada
    1 se a inserção DDX deve ser adicionada ao cabeçalho da lista (o padrão é adicionar à parte final). Isso geralmente é usado para rotinas DDX que transferem a propriedade “Control”.
  • vb-keys

    Esse campo é usado apenas no produto de 16 bits para controles VBX (não há suporte para controles VBX no produto de 32 bits)

  • prompt

    Cadeia de caracteres a ser colocada na caixa de combinação Propriedade (sem aspas)

  • tipo

    Identificador único para tipo a ser emitido no arquivo de cabeçalho. Em nosso exemplo acima com DDX_Time, isso seria definido como CTime.

  • vb-keys

    Não usado nesta versão e deve estar sempre vazio

  • initValue

    Valor inicial – 0 ou em branco. Se estiver em branco, nenhuma linha de inicialização será gravada na seção //{{AFX_DATA_INIT do arquivo de implementação. Uma entrada em branco deve ser usada para objetos C++ (como CString, CTime e assim por diante) que têm construtores que garantem a inicialização correta.

  • DDX_Proc

    Identificador único para o procedimento DDX_. O nome da função C++ deve começar com "DDX_", mas não incluir "DDX_" no identificador <DDX_Proc>. No exemplo acima, o identificador <DDX_Proc> seria Time. Quando ClassWizard grava a chamada de função no arquivo de implementação na seção {{AFX_DATA_MAP, ele acrescenta esse nome a DDX_, chegando assim a DDX_Time.

  • comment

    Comentário a ser mostrado na caixa de diálogo para variável com este DDX. Coloque qualquer texto desejado aqui e, de preferência, forneça algo que descreva a operação executada pelo par DDX/DDV.

  • DDV_Proc

    A parte DDV da entrada é opcional. Nem todas as rotinas DDX têm rotinas DDV correspondentes. Geralmente, é mais conveniente incluir a fase de validação como parte integrante da transferência. Geralmente, esse é o caso quando sua rotina DDV não exige parâmetros, porque o ClassWizard não dá suporte a rotinas DDV sem parâmetros.

  • arg

    Identificador único para o procedimento DDV_. O nome da função C++ deve começar com "DDV_", mas não incluir "DDX_" no identificador <DDX_Proc>.

    arg é seguido por 1 ou 2 argumentos DDV:

    • promptN

      Cadeia de caracteres a ser colocada acima do item de edição (com & para acelerador).

    • fmtN

      Formatar caractere para o tipo arg, um de:

      Character Tipo
      d int
      u unsigned int
      D long int (ou seja, long)
      U long unsigned (ou seja, DWORD)
      f float
      F double
      s string

Confira também

Observações técnicas por número
Observações técnicas por categoria