© 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 TForm
bron->Left=bestemming->Left;
bron->Top=bestemming->Top;
// Zet op voorgrond
bron->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 aanstaat
Accept = (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 bestemming
Left=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 TForm
bron->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.