[MFC] demande de précisions sur DDX/DDV

demande de précisions sur DDX/DDV [MFC] - C++ - Programmation

Marsh Posté le 25-06-2005 à 18:38:10    

Comment appeler le code de validation/échange de données spécifique à un seul contrôle de dialogue sachant que j'ai plusieurs DDX_xxx déclarés dans dans DoDataExchange()?  :??:

Reply

Marsh Posté le 25-06-2005 à 18:38:10   

Reply

Marsh Posté le 26-06-2005 à 11:27:18    

tu n'as pas à appeler toi même DoDataExchange().
tu veux faire quoi exactement ?

Reply

Marsh Posté le 26-06-2005 à 14:26:37    

Je vais préciser (cas concret vécu au taf)
 
J'ai un dialogue (modal) contenant plusieurs champs de saisie.
J'utilise le mécanisme DDX pour l'échange attributs/contrôle de saisie. J'implémente donc DoDataExchange() de la façon adhoc.
 
A la modification d'un contrôle (donc dans le gestionnaire d'événement, par ex EN_KILLFOCUS), je souhaite contrôler puis récupérer la valeur de ce contrôle et uniquement de celui-là. Je ne peux donc pas appeler UpdateData(TRUE) sinon les autres contrôles sont testés/échangés.


Message édité par slash33 le 26-06-2005 à 14:27:28
Reply

Marsh Posté le 27-06-2005 à 09:36:19    

He bien personne peut répondre. Harkonnen t'avait l'air sur la bonne voie...

Reply

Marsh Posté le 27-06-2005 à 12:16:31    

y'a un truc que je comprends pas... par le DDX, ton controle est mappé à un objet normalement non ? (CString dans ton cas). pourquoi ne pas utiliser ses méthodes ?


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 27-06-2005 à 13:40:25    

Parce qu'il faut passer un CDataExchange en paramètre mais si c'est la seule solution je vais le construire ce CDataExchange  :o
 
Edit: après relecture je sais pas si ton "ses méthodes" s'applique à DDX_xxx ou à CString?


Message édité par slash33 le 27-06-2005 à 13:42:21
Reply

Marsh Posté le 27-06-2005 à 14:41:48    

comprends rien... tu me dis si je me trompe :
- tu as une form contenant plusieurs champs de saisie (des TextBox je suppose ?)
- au KILLFOCUS d'un textbox, tu veux récupérer la valeur de ce textbox.
 
donc, ta fonction DoDataExchange doit ressembler à ceci (ID_TBOX est l'ID de ton textbox)

Code :
  1. void CApp:DoDataExchange(CDataExchange* pDX)
  2. {
  3.    //{{AFX_DATA_MAP(CAppView)
  4.    DDX_Control(pDX, ID_TBOX, m_TextBox);
  5.    //………………………………………………….
  6.    //}}AFX_DATA_MAP  
  7. }


la variable m_TextBox est une variable membre de type CString, qui contient donc le contenu de ton TextBox. tu sais surement que pour mettre à jour le contenu de la variable avec celui du TextBox, tu dois appeler UpdateData(TRUE), et que pour mettre le TextBox à jour avec la valeur de la variable, tu appelles UpdateData(FALSE)
 
donc, à partir de là, tout ce que tu as à faire dans le handler rattaché à EN_KILLFOCUS est ceci :

Code :
  1. UpdateData(TRUE); // on récupère le contenu de tous les controles dans les différentes variables membres
  2. CString txt = m_TextBox; // la variable txt contient le contenu de ton TextBox


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 27-06-2005 à 18:52:06    

C'est tout à fait exact. Bon maintenant j'expose le problème:
 
Je n'ai pas qu'un seul TextBox dans ma form (connaissait pas ce nom là mais ok).
Ma DoDataExchange c'est plutôt ce genre là:

Code :
  1. void CApp:DoDataExchange(CDataExchange* pDX)
  2. {
  3. //{{AFX_DATA_MAP(CAppView)
  4. DDX_Control(pDX, ID_TBOX, m_TextBox);
  5. DDX_Control(pDX, ID_ANOTHERTBOX, m_AnotherTextBox);
  6. DDX_Control(pDX, ID_ONEMORETBOX, m_OneMoreTextBox);
  7. //}}AFX_DATA_MAP   
  8. }


DONC si j'appelle UpdateData(TRUE), je valide TOUS les champs.
MAIS je veux uniquement valider un des TextBox.

Reply

Marsh Posté le 28-06-2005 à 11:16:25    

Bon alors?

Reply

Marsh Posté le 28-06-2005 à 12:49:56    

Ben alors dans ce cas, t'as qu'une chose à faire : subclasser le controle "à la main" dés que tu veux le valider, avec la fonction SubclassDlgItem()
 
Plus d'infos, avec exemple ici :
http://msdn.microsoft.com/library/ [...] lgitem.asp


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 28-06-2005 à 12:49:56   

Reply

Marsh Posté le 28-06-2005 à 13:43:31    

Tu pourrais par exemple créer une variable de type m_nLastModifiedID que tu mets à jours sur EN_KILLFOCUS et ensuite tu appelles une fonction du genre
 

Code :
  1. void MyDialog::MyUpdateData()
  2. {
  3. switch(m_nLastModifiedID)
  4. {
  5. case ID_TBOX:
  6.  DDX_Control(pDX, ID_TBOX, m_TextBox);
  7.  break;
  8. case ID_ANOTHERTBOX:
  9.  DDX_Control(pDX, ID_ANOTHERTBOX, m_AnotherTextBox);
  10.  break;
  11. case ID_ONEMORETBOX:
  12.  DDX_Control(pDX, ID_ONEMORETBOX, m_OneMoreTextBox);
  13.  break;
  14. //...
  15. }
  16. }

Reply

Marsh Posté le 28-06-2005 à 13:56:13    

Ouais j'y avais pensé en utilisant m_nLastModifiedID de cette façon:
Si m_nLastModifiedID == -1 alors je traite tous les champs sinon m_nLastModifiedID est l'ID du champ à traiter.
 
Ma question portait plutôt sur l'existence ou non d'un méthode miracle. J'ai regardé du côté de CWnd::UpdateData mais j'ai estimé ne pas pouvoir en faire grand chose (à moins de créer un contrôle à la main bien sûr)
 
Pour le SubclassDlgItem, je connais j'ai déja utilisé mais je ne comprend pas l'utilisation que tu veux en faire Harkonnen. Explique moi STP.

Reply

Marsh Posté le 28-06-2005 à 16:52:50    

Pourquoi tu n'utilises pas GetDlgItem() pour récupérer directement le control et getWindowText pour avoir la valeur ?
 
CString resultat;
((CEdit*)GetDlgItem(ID_MONTEXTBOX))->GetWindowText(resultat);

Reply

Marsh Posté le 28-06-2005 à 18:23:57    

slash33 a écrit :


Pour le SubclassDlgItem, je connais j'ai déja utilisé mais je ne comprend pas l'utilisation que tu veux en faire Harkonnen. Explique moi STP.


Quand tu appelles UpdateData(), SubclassDlgItem() est appelée afin de remplacer la procédure WndProc du controle (ici le TextBox) par celle que tu as défini dans le DDX_CONTROL.
Donc ce que tu peux faire, c'est virer le DDX_CONTROL du TextBox de la fonction DoDataExchange(), afin que tout appel à UpdateData soit sans effet sur ce TextBox.
Ensuite, tu créé une classe CMyEdit, qui hérite de la classe CEdit (qui est la classe MFC qui encapsule le controle TextBox), et dans laquelle tu créé le handler qui intercepte EN_KILLFOCUS.
 
Enfin, dans la fonction OnInitDialog() de la fenêtre contenant tes TextBox, tu sous classes le TextBox que tu souhaites :
 
dans le .h de ta Dialog :

Code :
  1. CMyEdit m_textbox;


 
dans la fonction OnInitDialog de ton .cpp :

Code :
  1. m_textbox.SubclassDlgItem(ID_TBOX, this);


Ainsi, l'interception de EN_KILLFOCUS se fera dans l'objet m_textbox, et pas dans la classe de dialog principale.


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 28-06-2005 à 18:58:26    

ChristianS_ a écrit :

Pourquoi tu n'utilises pas GetDlgItem() pour récupérer directement le control et getWindowText pour avoir la valeur ?
 
CString resultat;
((CEdit*)GetDlgItem(ID_MONTEXTBOX))->GetWindowText(resultat);


1. Je ne me contente pas de CString et des DDX_Text, c'était juste un exemple. Dans le problème qui me préoccupe c'est en fait un DDX_CBIndex...
Au passage, tu sais qu'il existe GetDlgItemText()?
2. les validation DDX/DDV tu les fais comment??
 
Harkonnen:
 
Je comprends ce que tu proposes mais je ne vois pas où tu appelles les DDX_ et DDV_? Dans le gestionnaire EN_KILLFOCUS de la classe spécialisée CMyText?
Mon intérêt étant d'employer les méthodes DDX_ et DDV_ pas de tout refaire à la main.
 
Enfin c'est pas grave, je vais probablement opter pour la solution de XtremDev qui n'est pas trop contraignante. Il y a même peut être moyen de spécialiser CDataExchange pour mon besoin.


Message édité par slash33 le 28-06-2005 à 19:08:08
Reply

Marsh Posté le 28-06-2005 à 20:04:13    

slash33 a écrit :


Je comprends ce que tu proposes mais je ne vois pas où tu appelles les DDX_ et DDV_? Dans le gestionnaire EN_KILLFOCUS de la classe spécialisée CMyText?
Mon intérêt étant d'employer les méthodes DDX_ et DDV_ pas de tout refaire à la main.


mais vu que tu subclasses, t'as plus besoin de DDX !
si tu vires le DDX_CONTROL du TextBox de la classe principale, UpdateData() n'aura plus aucun effet sur lui.
donc, au KILLFOCUS, c'est le gestionnaire de la classe spécialisée qui sera appelé, et uniquement celui ci. les autres textbox ne seront pas concernés si tu ne leur codes pas de gestionnaire dans la classe principale.
c'est ce que tu veux, non ?

Reply

Marsh Posté le 29-06-2005 à 10:11:25    

On va prendre un exemple concret:
 

Code :
  1. // Fichier d'entête (.hpp)
  2. class CMyDialog : public CDialog
  3. {
  4. public:
  5.   CMyDialog(CWnd* pParent = NULL);
  6. protected:
  7.   CString m_strSomeText;
  8.   UINT m_nInteger;
  9. public:
  10.   virtual void DoDataExchange(CDataExchange *pDX);
  11. };
  12. // Fichier source (.cpp)
  13. CMyDialog::CMyDialog(CWnd* pParent /* == NULL */)
  14. :CDialog(IDD_MYDIALOG)
  15. {
  16.   m_strSomeText = _T("This text is too long!" );
  17.   m_nInteger = 0; // entre 0 et 9 compris
  18. }
  19. void CMyDialog::DoDataExchange(CDataExchange *pDX)
  20. {
  21.   DDX_Text(pDX, IDC_SOMETEXT, m_strSomeText);
  22.   DDV_MaxChars(pDX, m_strSomeText, 7); // 7 car. max
  23.   // IDC_INTEGER reçoit un entier compris entre 0 et 9.
  24. >  DDX_Text(pDX, IDC_INTEGER, m_nInteger);
  25. >  DDV_MinMaxChars(m_nInteger, 0, 9);
  26. }


Dans l'exemple, il faut pouvoir extraire et valider la valeur de IDC_INTEGER de façon isolée.
 
Conformément à ta proposition, je retire les lignes 30 à 32 de DoDataExchange et je crée une spécialisation de CEdit.
 
Le code de CMyDialog

Code :
  1. // Fichier d'entête (.hpp)
  2. class CMyDialog : public CDialog
  3. {
  4. public:
  5.   CMyDialog(CWnd* pParent = NULL);
  6. public:
  7.   // public pour être aisément acessible par CMyEdit
  8.   UINT m_nInteger;
  9. protected:
  10.   CString m_strSomeText;
  11.   CMyEdit m_myedit;
  12. public:
  13.   virtual void DoDataExchange(CDataExchange *pDX);
  14. };
  15. // Fichier source (.cpp)
  16. CMyDialog::CMyDialog(CWnd* pParent /* == NULL */)
  17. :CDialog(IDD_MYDIALOG)
  18. {
  19.   m_strSomeText = _T("This text is too long" );
  20.   m_nInteger = 0; // entre 0 et 9 compris
  21. }
  22. void CMyDialog::DoDataExchange(CDataExchange *pDX)
  23. {
  24.   DDX_Text(pDX, IDC_SOMETEXT, m_strSomeText);
  25.   DDV_MaxChars(pDX, m_strSomeText, 7); //7 car. max
  26.   // IDC_INTEGER n'est plus traité ici.
  27. }
  28. BOOL CMyDialog::OnInitDialog()
  29. {
  30.   if(!m_myEdit.SubclassDlgItem(IDC_INTEGER, this))
  31.   {
  32.     TRACE0("Impossible de surclasser IDC_INTEGER!\n" );
  33.   }
  34.   CDialog::OnInitDialog();
  35.   // Q: comment j'initialise le contrôle IDC_INTEGER
  36.   // avec la valeur de m_nInteger?
  37.   return TRUE;
  38. }


La classe CMyEdit

Code :
  1. class CMyEdit : public CEdit
  2. {
  3. public:
  4.   CMyEdit();
  5. protected:
  6.   afx_msg void OnKillFocus(/* parametres */);
  7.   DECLARE_MESSAGE_MAP()
  8. };
  9. CMyEdit::CMyEdit(){}
  10. BEGIN_MESSAGE_MAP(CMyEdit, CEdit)
  11.   ON_EN_KILLFOCUS()
  12. END_MESSAGE_MAP()
  13. void CMyEdit::OnKillFocus(/* ... */)
  14. {
  15.   CMyDialog* pDialog = (CMyDialog*)GetParent();
  16.   //Q: comment mettre à jour uniquement
  17.   //pDialog->m_nInteger tout en réalisant les mêmes
  18.   //opérations/vérifications que
  19.   //DDX_Text(pDX, IDC_INTEGER, m_nInteger) et
  20.   //DDV_MinMaxChars(m_nInteger, 0, 9) ?
  21. }


Message édité par slash33 le 29-06-2005 à 10:13:56
Reply

Marsh Posté le 30-06-2005 à 15:04:20    

mouais... effectivement, le subclassing n'est pas adapté ici :/
je ne vois guère que la soluce de XTremDev, GetDlgItemText(). tes validations DDX/DDV n'ont pas l'air trop chiantes à faire apparemment, ça devrait pas être bien méchant


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 30-06-2005 à 16:07:20    

L'exemple est resté volontairement simple mais il m'arrive de coder des méthodes DDX (un peu) ou DDV (plus souvent) perso.
 
Un exemple classique de DDX perso c'est les DDX_Text mais pour des champs optionnels (le champ peut n'avoir aucune valeur). Ca paraît simple comme ça mais dans les faîts ça pose pas mal de problèmes: représentation de indéfini pour tous les types de bases, gestion des -INF et +INF, etc.  :pt1cable:  
 
Merci quand même pour tes réponses. Je trouve effectivement que la solution de XTremDev (celle avec l'indicateur du champ à traiter) est de loin la plus efficace pour régler ma problématique.
 
Cela soulève les problèmes que l'on rencontre quand on s'écarte de l'architecture MFC. C'est un peu comme avec le mécanisme document/vue: c'est très bien quand tu respectes à la lettre l'architecture, par contre quand tu fais quelque chose hors norme ça devient un peu la galère...  :(  


Message édité par slash33 le 30-06-2005 à 16:09:41
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed