© Harry Broeders.
Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft.
Als je nog niet weet hoe je een eenvoudige GUI Windows applicatie met C++ Builder kunt maken lees dan eerst deze pagina.
Je kunt een voorwerp op het scherm op allerlei manieren laten bewegen. Een van de mogelijkheden is gebruik te maken van drag en drop. Het is ook mogelijk om de objecten zelf te verplaatsen door mouse events af te vangen.
Een van de mogelijkheden om objecten te verplaatsen is het gebruik van drag
en drop. Alle van TControl afgeleide classes in VCL ondersteunen
drag en drop. In het onderstaande voorbeeld zijn TPanel's gebruikt
maar dat is maar een voorbeeld. De code voor het onderstaande voorbeeld kun
je vinden in dragdrop.zip
.
De vier gekleurde vlakken zijn allemaal van het type TPanel. De layout van de verschillende objecten op de TForm is hieronder weergegeven.
Het lichtgroene vlak is Panel1 en rode vlak is
Panel2. Beide kunnen versleept worden als de applicatie runt.
Om dit mogelijk te maken moet de property DragMode op
dmAutomatic worden gezet.
De reset knoppen op deze panels zetten de Top en
Left properties weer op hun oorspronkelijke waarden zodat de
panels weer teruggaan naar hun oorspronkelijke posities.
void __fastcall TForm1::Button1Click(TObject *Sender) {Panel1->Top=8;Panel1->Left=8;}void __fastcall TForm1::Button2Click(TObject *Sender) {Panel2->Top=120;Panel2->Left=8;}
Het donkergroene vlak is Panel3 en het gele vlak is
Panel4. Op deze vakken kun je Panel1 en
Panel2 laten vallen. Om dit mogelijk te maken moeten de events
OnDragDrop en OnDragOver gedefinieerd worden.
De functie Panel3DragOver wordt aangeroepen als een gesleept
object boven Panel3 "zweeft". Met de parameter
Accept moet aangegeven worden of het gesleepte object (aangewezen
door de pointer Source) wordt geaccepteerd. In dit geval accepteren
we Panel1 en Panel2.
void __fastcall TForm1::Panel3DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept) {Accept = Source==Panel1 || Source==Panel2 ;}
Als het gesleepte object op Panel3 wordt "gedropt" dan wordt
de functie Panel3DragDrop aangeroepen. In deze functie wordt
het gesleepte object (aangewezen door de pointer Source) verplaatst
naar het object (aangewezen door de pointer Sender) waar het
gedropt is.
void __fastcall TForm1::Panel3DragDrop(TObject *Sender, TObject *Source, int X, int Y) {TControl* bestemming(static_cast<TControl*>(Sender));TControl* bron(static_cast<TControl*>(Source));// Bepaal nieuwe lokatie binnen TFormbron->Left=bestemming->Left;bron->Top=bestemming->Top;// Zet op voorgrondbron->BringToFront();}
Panel4 is groter dan Panel1 en Panel2.
Hierdoor wordt de implementatie van de functie Panel4DragDrop
iets ingewikkelder. Een object kan ook binnen Panel4 verplaatst
worden. Omdat er op Panel4 meer ruimte is kunnen ook
Panel1 en Panel2 beide op Panel4 geplaatst
worden. Panel4 is voorzien van een TCheckBox. De
toestand van deze CheckBox1 wordt bijgehouden in de private
variabele lock. Panel4 accepteerd alleen objecten
als de variabel lock false is.
void __fastcall TForm1::CheckBox1Click(TObject *Sender) {TCheckBox* box(static_cast<TCheckBox*>(Sender));lock=box->Checked;}void __fastcall TForm1::Panel4DragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept) {// kijk of je het object wil accepteren// accepteert Panel1 en Panel2 als lock niet aanstaatAccept = (Source==Panel1 || Source==Panel2) && !lock;}void __fastcall TForm1::Panel4DragDrop(TObject *Sender, TObject *Source, int X, int Y) {TControl* bestemming(static_cast<TControl*>(Sender));TControl* bron(static_cast<TControl*>(Source));int Left, Top;// Bepaal nieuwe lokatie binnen bestemmingLeft=X-bron->Width/2;Top=Y-bron->Height/2;// Zorg dat bron binnen bestemming blijft.if (Left<0)Left=0;if (Left+bron->Width>bestemming->Width)Left=bestemming->Width-bron->Width;if (Top<0)Top=0;if (Top+bron->Height>bestemming->Height)Top=bestemming->Height-bron->Height;// Bepaal nieuwe lokatie binnen TFormbron->Left=bestemming->Left+Left;bron->Top=bestemming->Top+Top;bron->BringToFront();}
Objecten kunnen ook verplaatst worden als reactie op mouse events. Op deze manier kun je objecten "verslepen".
Eind 2005 stelde ik aan student Chris Werkmeester de vraag:
Ik heb geprobeerd om het hele panel te verslepen door bij een MouseMove de Top en Left attributen aan te passen maar dat werkt niet goed. Je ziet het panel dan 2x getekend (op een behoorlijke afstand van elkaar) en het panel volgt de muis ook niet goed. Heb jij al een manier gevonden om het panel in zijn geheel met de muis te verslepen? Dus zonder drag en drop maar gewoon door zelf op de muisbewegingen te reageren.
Chris schreef:
Ja dat lukte me makkelijk.Idd met behulp van Top & Left. En de MouseMove & MouseDown functie.
Maar daarbinnen gebruik je dan nl twee globale/static variabelen. en je moet niet vergeten dat de X & Y argumenten afhankelijk zijn van Top & Left, je moet daarvoor compenseren anders krijg je idd een schokkend effect.
Een simpel voorbeeld is MoveMe.zip. Niet helemaal 'mooi' maar dat hoeft ook niet als voorbeeld ;)
Ps: als de automatische refresh je hindert kan je eventueel een timer plaatsen mbv een constructie als if( currentx != 0xFFFFFFFF && (timed) ) om de verplaating te forceren naar bv. elke 100ms max ipv. direct bij een event. Maar dan moet je bij mouseup ook nog de verplaats controle uitvoeren, anders kan hij natuurlijk blijven haken.
Met dit programma kun je twee "blokjes" binnen een window verslepen.