[VC++] Implémenter du Drag And Drop

Implémenter du Drag And Drop [VC++] - C++ - Programmation

Marsh Posté le 31-10-2003 à 21:52:43    

Voilà, mes besoins sont tous simples, mais ils me permettront peut etre d'y voir plus clair.
 
Je dispose pour le moment d'une appli en Visual C++ avec un monodocument et une monoView (Single Document Application)
 
Pour l'instant, l'appli se contente de calculer des coordonnées au hasard dans la fenetre, et des couleurs au hasard, de créer des objects Cercle (classe contenant comme donnée membre une couleur et un Point).
 
Ce que je voudrais, ce serait etre capable de sélectionner sur ma fenetre des Cercles et de les déplacer à autre endroit sur la fenetre... Faire du Drag And Drop quoi!
 
Est ce que quelqu'un pourrait m'expliquer comment ca pt marcher (enfin, en supposant que ca ne soit pas trop compliqué)
 
Est ce que c'est basé sur des évenements de souris? Sur la View?Bref, je ne sais pas par ou commencer! Comment par exemple faire "savoir" à ma View qu'il y a un cercle sous ma souris quand je clique?
 
Merci :hello:

Reply

Marsh Posté le 31-10-2003 à 21:52:43   

Reply

Marsh Posté le 01-11-2003 à 01:32:28    

Deux types de drag & drop :
1/ Tu l'implémentes à la pogne (que tu décris) : tu le manages de A à Z.
2/ Tu utilises des fonctions COM, eg http://msdn.microsoft.com/library/ [...] o_706o.asp - ça te permet de faire du drag & drop entre les fenêtres de ton applic, entre des applics différentes - bref, partout.
 
Le plus simple est évidemment le 1/ - un drag & drop limité à une fenêtre. Comme je suis une bille en MFC, je ne peux pas détailler. En win32, tu utilises trois messages : WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP.
 
Tu appuies sur le bouton de la souris, WM_LBUTTONDOWN :  
tu testes si tu as cliqué sur un objet, si oui tu peux commencer un drag : tu sauves la position de ton objet (int origX, int origY), un pointeur vers celui-ci (object* draggedObject), tu mets un booléen à true. (dragging = true)
 
WM_MOUSEMOVE :  
si dragging == true, tu assignes les coordonnées de la souris à l'objet draggedObject.
 
WM_LBUTTONUP :
tu mets dragging à false.
 
Certaines applics te permettent de canceller un drag avec le bouton droit. Tu peux tester WM_RBUTTONDOWN : si dragging == true, tu le mets à false, tu restores les coordonnées de l'objet (origX, origY).
 
//
 
Tu n'as plus qu'à trouver l'équivalent de WM_xxx pour ta view. Je crois me souvenir que ça se fait par le classWizard.
 
//
 
>> Comment par exemple faire "savoir" à ma View qu'il y a un cercle sous ma souris quand je clique?  
 
Il te faut un tableau d'objets affichés par ta view (Ici, une liste de cercles). Pour savoir si tu as cliqué sur un cercle : tu calcules la distance centre du cercle - point clické. Si la distance est égale au radius du cercle (+ ou - un delta), tu le considère comme sélectionné.

Reply

Marsh Posté le 01-11-2003 à 01:38:21    

D'accord!
 
En gros, je dois raiment tout tout faire à la main, lorsque que ma View recoit un évenement du type "Bouton Cliqué", je dois parcourir tous les objets un par un et me débrouiller pour faire mon calcul?
 
Ca me parait pas hyper efficace, mais au moins, cette réponse me rassure quelque peu, car je préfère souvent bien controler ce que je fais en me "tapant" tous les mécanismes que d'utiliser un truc tout intégré!
 
Merci :hello:

Reply

Marsh Posté le 01-11-2003 à 01:41:21    

Ah oui, par contre, y a un truc qui me fait flipper avec le "tout à la main", c'st qu'au fur et à mesure que je déplace mon cercle, je suppose que je dois consamment rafraichir ma fenetre, et donc, tout redessiner à chaque instant? Ca va donner une impression de clignotement à l'écran, non?

Reply

Marsh Posté le 01-11-2003 à 01:48:36    

>> En gros, je dois raiment tout tout faire à la main, lorsque que ma View recoit un évenement du type "Bouton Cliqué", je dois parcourir tous les objets un par un et me débrouiller pour faire mon calcul?  
 
Oui, pas de miracle. Comment pourrais-tu faire autrement ?  
 
>> Ah oui, par contre, y a un truc qui me fait flipper avec le "tout à la main", c'st qu'au fur et à mesure que je déplace mon cercle, je suppose que je dois consamment rafraichir ma fenetre, et donc, tout redessiner à chaque instant? Ca va donner une impression de clignotement à l'écran, non?  
 
Oui, il faut rafraichir constamment. Tu peux te contenter de rafraichir la zone dans laquelle tu déplaces ton objet.
 
Pour éviter le clignotement, tu dessines dans un buffer offscreen que tu affiches à l'écran. Fais une recherche sur le forum avec CreateCompatibleDC, CreateCompatibleBitmap, BitBlt. Tu peux aussi regarder par là http://www.codeproject.com/bitmap/ .

Reply

Marsh Posté le 01-11-2003 à 01:51:09    

youdontcare a écrit :

>> En gros, je dois raiment tout tout faire à la main, lorsque que ma View recoit un évenement du type "Bouton Cliqué", je dois parcourir tous les objets un par un et me débrouiller pour faire mon calcul?  
 
Oui, pas de miracle. Comment pourrais-tu faire autrement ?

Enfin, c'est le genre de chose qui s'optimise avec un quadtree.

Reply

Marsh Posté le 01-11-2003 à 09:19:36    

D'accord, merci beaucoup l'ami :)
 
Je vais d'abord implémenter une première version à la main!
 
Puis je ferai ensuite des recherches pour utiliser par exemple un buffer offscreen.
 
En fait, quand j'ai posé ma question, sur ce topic, je pensais que VC++ allait me fournier un moyen d'avoir des objects "niveaux haut", tels que la MFC/windows sache à tout moments où ils sont représentés sur ma View.

Reply

Marsh Posté le 02-11-2003 à 19:28:36    

Voilà,
 
J'ai fait ma première version de Drag and Drop à la main, et tout semble bien marcher.= (à part quelques détails qu'il fo que je rège, par exemple, lorsque je fais sortir ma souris de l'écran).
 
Par contre, j'ai toujours un probleme de scintillement, d'où l'idée de dessiner dans un buffer hors écran, et ensuite, de switcher les 2 Device Context.  
 
Voici mon code original, qui scintille :
 

Code :
  1. void CFromToCallsView::OnDraw(CDC* pDC)
  2. {
  3. CFromToCallsDoc* pDoc = GetDocument();
  4. ASSERT_VALID(pDoc);
  5. // TODO: add draw code for native data here
  6. pDoc->LaunchDraw(&pDC);
  7. }


 
Voici alors ma nouvelle version :
 

Code :
  1. void CFromToCallsView::OnDraw(CDC* pDC)
  2. {
  3. CFromToCallsDoc* pDoc = GetDocument();
  4. ASSERT_VALID(pDoc);
  5. // TODO: add draw code for native data here
  6. CDC dcMem;
  7. dcMem.CreateCompatibleDC(pDC);
  8. pDoc->LaunchDraw(&dcMem);
  9. pDC->BitBlt(0,0,200,200,&dcMem,0,0,SRCCOPY);
  10. }


 
Mais là, le probleme, c'est que ca ne marche plus du tout, je me retrouver avec une fenêtre blanche...
 
Une idée?


Message édité par Yoyo@ le 02-11-2003 à 19:29:11
Reply

Marsh Posté le 02-11-2003 à 20:01:59    

>> (à part quelques détails qu'il fo que je rège, par exemple, lorsque je fais sortir ma souris de l'écran).
 
Utilise SetCapture() au _LBUTTONDOWN, puis ReleaseCapture() au _LBUTTONUP.
 
http://msdn.microsoft.com/library/ [...] apture.asp
 
>> Mais là, le probleme, c'est que ca ne marche plus du tout, je me retrouver avec une fenêtre blanche...
 
http://msdn.microsoft.com/library/ [...] licker.asp

Reply

Marsh Posté le 03-11-2003 à 17:12:51    

Merci beaucoup!
 
Tout marche à la perfection, surtout pour le scintillement! Le tout était de comprendre comment tout celà marchait.
 
Sinon, pour le Drag and Drop, j'ai du peaufiner un peu tout ça, de sorte que lorsque que je chope un cercle avec ma souris, et que je la sorte de mon écran, le Drag s'arrete et mon objet reste dans l'écran! N'ayant pas trouvé d'évènement m'indquant que la souris était sortie de l'écran, je me suis contenté de faire un test à la main!
 
Ma&intenant, j'ai quelques autres questions, plus concernat le C++ en général!
 
Voilà, quand j'ai implémenté mon "BitBlt", j'ai du créé des objects tels qu'un CBitmap et un CBrush! Est il nécessaire, une fois qu'on s'en est servis, de les Deleter avec un DeleteObject. Si je ne le fais pas, qu'est ce que ça fait?
 
D'autre part, à quoi ca sert de faire ça :
 

Code :
  1. hbmOld = SelectObject(hdcMem, hbmMem);
  2. pour à la fin faire ça :
  3. SelectObject(hdcMem, hbmOld);
  4. DeleteDC(hdcMem);


 
Enfin, concernant les classes, je me pose une question...  
Ma classe cercle dispose d'un membre "Position" du type CPoint. Je m'en sert pour cacluler à la distance de mon pointeur de souris au centre de mon cercle.
 
J'accède à ce membre par une fonction d'accès "Cpoint GetPosition()". Est ce qu'il ne serait pas plus efficace d'accéder directement à la valeur de ce point en faisant directement appel à lui au lieu de l'encapsuler dans une fonction (surtout que je dois l'élever au carré, donc, je dois faire myCircle->GetPosition().x * myCircle->GetPosition().x etc etc...
 
Merci bien :)

Reply

Marsh Posté le 03-11-2003 à 17:12:51   

Reply

Marsh Posté le 03-11-2003 à 18:35:46    

>> et que je la sorte de mon écran, le Drag s'arrete et mon objet reste dans l'écran!  
 
C'est pour ça que je mentionnais SetCapture(). Par défaut, windows envoie les messages souris à la fenêtre qui est directement sous le pointeur. En faisant utiliser SetCapture() à ta fenêtre, elle recevra tous les messages souris, même si le pointeur sort de la fenêtre, et tu pourras continuer à manipuler ton objet.
 
>> Est il nécessaire, une fois qu'on s'en est servis, de les Deleter avec un DeleteObject. Si je ne le fais pas, qu'est ce que ça fait?  
 
Delete toujours ce que tu alloues. À chaque fois que tu alloues un objet, tu réserves une resource windows. Sous win95, win98 les ressources diminuent très rapidement, de telle sorte que ton application ne pourra plus rien afficher. Sous win2000+, les ressources ne sont plus limitées, mais tu bouffes de la mémoire inutilement. Si tu utilises ton applic 30 secondes, tu ne t'en apercevras pas, si tu l'utilises pendant qq heures, tu peux bouffer des centaines de mégas.
 
Donc, delete toujours.
 
(Windows efface tout lorsque tu quittes ton applic, mais ce n'est pas une raison pour coder n'importe comment).
 
>> D'autre part, à quoi ca sert de faire ça :  
 
Lis la doc, c'est le mécanisme standard de fonctionnement de la gdi : un objet est toujours sélectionné par défaut. Si tu fais un SelectObject sans sauver cet objet, tu n'as plus aucun moyen de savoir où il est. C'est le même cas que plus haut, tu boufferas de la mémoire inutilement.
 
>> Est ce qu'il ne serait pas plus efficace d'accéder directement à la valeur de ce point  
 
NON !
 
Dans ton programme, 99.99% du temps machine est bouffé par l'affichage : effacement du fond, dessin du cercle, BitBlt(). Optimiser un accès à une valeur n'a aucun sens ! En faisant ça, tu rends ton code plus crade et plus chiant à maintenir.

Reply

Marsh Posté le 04-11-2003 à 22:47:54    

D'accord,
 
Merci pour mes réponses.
 
Pour le Drag&Drp, mon but était de limiter le déplacement des cercles à la surface visible à l'écran! C'est donc pourquoi j'ai du faire des tests supplémentaires à la main, genre pour savoir si l'object étaiet en train de sortir de l'écran.
 
Sinon, pour ce qui est de la libération des resources, je sais que globalement, il faut détruire tout ce qu'on n'utilise plus, lbérer la mémoire... Mais ma question est de savoir ce qu'il est bon de deleter et ce qu'il n'est pas nécessaire de déleter... Par eemple, pourquoi effacer un CBrush dès lors qu'on ne s'en sert plus, alors qu'il n'est pas nécessaire de faire de meme pour un CPoint? Comment le savoir?

Reply

Marsh Posté le 04-11-2003 à 23:15:51    

Yoyo@ a écrit :

Mais ma question est de savoir ce qu'il est bon de deleter et ce qu'il n'est pas nécessaire de déleter... Par eemple, pourquoi effacer un CBrush dès lors qu'on ne s'en sert plus, alors qu'il n'est pas nécessaire de faire de meme pour un CPoint? Comment le savoir?

Un CBrush est une classe qui englobe un HBRUSH de windows, bref une resource à libérer. Un CPoint contient juste 2 entiers, donc rien de particulier à faire. (À part un delete après un new).
 
Comment savoir quoi effacer ? Si tu peux faire un DeleteObject() dessus, tu dois le faire :D

Reply

Marsh Posté le 05-11-2003 à 00:06:12    

D'accord!
 
Ca me parait plutot clair!
 
Merci pour toutes les infos que tu as pu me donner depuis le début, ca m'a beaucoup aidé à y voir plus clair !

Reply

Sujets relatifs:

Leave a Replay

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