© Harry Broeders.
Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft.
Met behulp van C++ Builder kun je twee soorten windows applicaties ontwikkelen:
Tot nu toe hebben we bij dit practicum console applicaties gemaakt. Ik zal jullie op deze pagina laten zien hoe eenvoudig het is om een Windows applicatie (inclusief menu, knoppen enz) in elkaar te zetten met behulp van Borland C++ Builder 6.
Voordat we kunnen beginnen moet ik jullie eerst enkele dingen vertellen over het Windows OS (operating systeem). Zoals je wel weet is het operating systeem verantwoordelijk voor alle beheerszaken in de PC. Het operating systeem is onder andere verantwoordelijk voor:
Een applicatie kan (moet) dus voor al deze zaken gebruik maken van het operating systeem. De vraag is nu hoe communiceert jouw Windows-applicatie met het Windows OS. Dit gaat door middel van functie-aanroepen. Alle functies die aan te roepen zijn in het Windows operating systeem vormen een zogenaamde API (Application Programmers Interface). Als we gebruik maken van de zogenaamde win32 API dan draaien onze applicaties op Windows95, Windows98, Windows2000 en WindowsXP. Het aantal functies in deze API is heel groot en het gebruik van deze API is zeker niet eenvoudig. Behalve dat onze applicatie met het Windows OS moet communiceren moet het Windows OS ook events (gebeurtenissen) aan onze applicatie kunnen doorgeven. Denk daarbij aan dingen zoals:
Als z'n event optreedt dan stuurt het Windows OS een message naar het Window waarop dit event betrekking heeft. Een applicatie kan bestaan uit meerdere windows. Het OS ziet een knop of een scrollbar overigens ook als een apart "window". Elk window moet daarom bij het opstarten het adres van een zogenaamde messagehandler opgeven aan het OS. Als er dan een event optreedt dan zal het Windows OS de messagehandler van het betreffende window aanroepen. Als argument geeft Windows dan een message datastructuur mee. Deze message bevat het message-id en enkele parameters. De messagehandler van een window bestaat dus uit een groot switch statement waarin bepaald wordt welke message het OS naar dit window stuurt en welke actie het window daarop moet ondernemen.
Samengevat kunnen we dus zeggen:
De meeste windows applicaties zullen nadat ze opgestart zijn niets anders doen dan wachten op messages van het OS en afhankelijk van deze message's bepaalde acties uitvoeren. Omdat de events die optreden het gedrag van z'n programma bepalen noemt men zulke programma's event-driven.
Het Windows operating systeem heeft dus geen object georiënteerde interface maar een procedurele interface. Het ligt natuurlijk voor de hand om een groep met classes te ontwikkelen die de complexiteit van de win32 API voor ons verbergen. Een verzameling bij elkaar behorende classes die gebruikt kunnen worden om bepaalde applicaties te ontwikkelen (in dit geval GUI = Graphical User Interface applicaties) wordt een framework genoemd. Er zijn meerdere bekende frameworks voor het ontwikkelen van Windows applicaties:
Behalve de frameworks bieden zowel Microsoft als Borland ontwikkeltools die het gebruik van deze frameworks aanzienlijk vereenvoudigen. Microsoft noemt deze hulptools Wizards. Zo heeft de Microsoft C++ ontwikkelomgeving een ClassWizard en een ApplicationWizard. De C++ Builder ontwikkelomgeving heeft vergelijkbare tools.
Als je een applicatie ontwikkelt met behulp van het VCL framework maak je (bijna zonder dat je er erg in hebt) gebruik van overerving. Om een GUI applicatie te kunnen maken moet je het volgende doen:
TForm1
in de editor.__published
en __fastcall
zijn specifieke
Borland extensies en kun je negeren. Je ziet dat deze class is afgeleid van
de class TForm
. De class TForm
heeft heel veel
properties en memberfuncties die TForm1
dus allemaal overerft.
Properties zijn een specifieke Borland extensie van C++. Op dit moment kun
je net doen of properties membervariabelen zijn. Deze properties kun je eenvoudig
aanpassen in de Object Inspector.Caption
op Een window met een kruis
. Deze tekst
komt in de titelbalk van het Window.
ClientHeight
op 250
. Dit is de hoogte van de
binnenkant van het window (zonder de titelbalk en randen).
ClientWidth
op 250
. Dit is de hoogte van de
binnenkant van het window (zonder de titelbalk en randen).
Color
op clWindow
.
WM_PAINT
) naar het window. We kunnen reageren op dit event
door met behulp van de Object Inspector een eventhandler te definiëren.
Klik op de tab Events en selecteer de regel OnPaint
en dubbelklik
op het invoerveld. De memberfunctie FormPaint
wordt nu automatisch
aan de class Form1
toegevoegd en de code voor deze memberfunctie
wordt in de editor geopend.![]() |
![]() |
TForm1::FormPaint
de volgende code
in:
Canvas->Pen->Color=clGray;
Canvas->Pen->Width=2;
Canvas->MoveTo(0, 0);
Canvas->LineTo(ClientWidth, ClientHeight);
Canvas->MoveTo(0, ClientHeight);
Canvas->LineTo(ClientWidth, 0);
Canvas->Font->Name="Arial";
Canvas->Font->Size=20;
Canvas->Font->Color=clBlue;
Canvas->TextOut(10, 10, "Hallo!");
TForm1
is afgeleid van TForm
en deze class
bevat een groot aantal properties en memberfuncties (methods). Zet in de
editor de cursor op het woord TForm1
en druk op F1 om uit te
vinden welke proporties en memberfuncties beschikbaar zijn. Let op je kunt
kiezen uit twee help pagina's voor TForm
.
Als je op Hierarchy klikt zie je dat een TForm
is afgeleid van
TCustomForm
die is afgeleid van
TScrollingWinControl
die is afgeleid van
TWinControl
die is afgeleid van TControl
die is
afgeleid van TComponent
die is afgeleid van
TPersistent
die (tot slot) is afgeleid van TObject
.
Ben je er nog?
Als je op Properties klikt zie je de beschikbare properties. Als je een stukje
naar beneden scrolt vindt je de properties Caption
en
Color
(overgeërfd van TControl
) die we al
eerder met behulp van de Object Inspector hadden aangepast.
Als je op methods klikt zie je de memberfuncties die je kunt aanroepen.
Als je op events klikt zie je welke events (windows messages) deze class
kan afvangen. Uiteraard staat het OnPaint
event dat we hebben
afgevangen ook in deze lijst.
Canvas
die we in de memberfunctie TForm1::FormPaint
hebben gebruikt een pointer naar een object van het type TCanvas
is. De eigenschappen van deze class kun je in de helpfile vinden:TCanvas
een ding
waarop je kunt schrijven of tekenen. De class TCanvas
bevat
veel properties en memberfuncties die je kunt gebruiken om te tekenen/schrijven.
Laten we de eerste 4 regels van de memberfunctie
TForm1::FormPaint
nog eens wat beter bekijken.
Canvas->Pen->Color=clGray;
Canvas->Pen->Width=2;
Canvas->MoveTo(0,
0);
Canvas->LineTo(ClientWidth, ClientHeight);
In de eerste regel heb ik de property Pen
(een pointer naar
een TPen
) gebruikt. Dit is een class uit VCL die een pen voorstelt
waarmee we op een TCanvas
kunnen tekenen. Zoals je in
de helpfile van TPen
kunt vinden heeft een
TPen
een property Color
die de kleur van de pen
bepaald (in dit geval dus clGray
). clGray
is een
constante van het type TColor
.
In de tweede regel heb ik de property Width
van de
Pen
(de dikte van de pen) op 2 pixels gezet.
In de derde regel stuur ik de pen met behulp van de memberfunctie (method)
MoveTo
van Canvas
naar de positie 0,0
in het window (dat is de linker bovenhoek).
In de vierde regel gebruik ik de memberfunctie LineTo
van
Canvas
om met de pen een lijn te tekenen naar de positie
ClientWidth,ClientHeight
in het window. ClientWidth
en ClientHeight
zijn properties (een soort member variabelen)
van TForm1
(overgeërfd van TForm
) die
respectievelijk de breedte en de hoogte van de binnenkant van het window
aangeven. De positie ClientWidth,ClientHeight
komt dus overeen
met de rechter benedenhoek van het window.
Deze 4 regels tekenen dus een grijze lijn van 2 pixels breed van linksboven
naar rechtsonder in het window.
Opdracht A.
Probeer nu zelf met behulp van de helpfiles van C++ Builder de rest van de
|
Opdracht B.Pas het programma nu zodanig aan dat het kruis met een groene stippellijn die slechts 1 pixel dik is wordt getekend. Zorg er tevens voor dat de tekst veel groter en in een rode kleur wordt weergegeven. |
Laten we het programma nu eens zodanig aanpassen dat dit programma reageert op bepaalde events die veroorzaakt worden door de gebruiker. Bijvoorbeeld op een muisklik. We willen het volgende:
Om dit mogelijk te maken moeten we een eventhandler schrijven de juiste acties uitvoert als op een muisknop wordt gedrukt:
TForm1
moet een private variabele van het type
TColor
worden opgenomen genaamd penKleur
. Deze
variabele wordt in de constructor op clGray
(grijs) gezet en
in de FormPaint
memberfunctie gebruikt om de keur van de pen
te kiezen.
TForm1
ziet er nu als volgt uit:
class TForm1 : public TForm
{
__published:
void __fastcall FormPaint(TObject *Sender);
private:
TColor penKleur;
public:
__fastcall TForm1(TComponent* Owner);
};
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner), penKleur(clGray)
{
}
FormPaint
wordt nu:
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Pen->Color=penKleur;
OnMouseDown
afvangen. De benodigde code ziet er als volgt uit:
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
switch (Button) {
case mbRight: penKleur=clRed; break;
case mbLeft: penKleur=clGreen; break;
}
Invalidate();
}
De memberfunctie die het OnMouseDown
event
afhandeld TForm1::FormMouseDown
heeft diverse parameters.
De parameter Button
geeft aan welke muisknop (links, midden
of rechts) is ingedrukt. De parameter Shift
geeft de toestand
van de shift, ctrl en alt toetsen op het moment dat op de muis werd gedrukt.
De parameters X
en Y
bevatten de positie in het
window waar de muiscursor zich bevond toen op de muisknop werd gedrukt. In
dit programma maken we alleen gebruik van de parameters Button
.
In de FormMouseDown
memberfunctie kennen we de gewenste kleur
toe aan de variabele penKleur
en roepen we de memberfunctie
Invalidate
van de baseclass TWinControl
aan. Deze
aanroep zal een win32 API functie aanroepen waarmee de applicatie aan het
Windows OS vraagt of die het window opnieuw wil tekenen. Als het Windows
OS hier tijd voor heeft dan stuurt hij dit window de message
WM_PAINT
. Deze message zal het aanroepen van de memberfunctie
FormPaint
tot gevolg hebben waarin het window opnieuw wordt
getekend. We kunnen ook zelf meteen de memberfunctie FormPaint
aanroepen vanuit de FormMouseDown
functie. Als we
Invalidate
aanroepen wordt het window echter eerst gewist en
dat gebeurt niet als we rechtstreeks FormPaint
aanroepen.
Opdracht C.
Als je de grootte van het window aanpast door aan de randen van het window
te trekken dan wordt het kruis niet opnieuw getekend. Dit geeft
een slordig beeld. Als een window van grootte veranderd stuurt het windows
OS de message |
Opdracht D.
Pas het programma aan zodat de kleur van het kruis ook met het toetsenbord
aangepast kan worden. Als je op een G drukt moet de kleur groen worden, als
je op een R drukt rood en als je op een B drukt blauw. Om dit voor elkaar
te krijgen moet je de windows message |
VCL bevat (zoals de naam al aangeeft) een groot aantal kant en klare componenten. Laten we eens kijken hoe je deze componenten kunt gebruiken om je programma van een menubalk, knoppenbalk en een statusbalk te voorzien. De menubalk en de knoppenbalk kunnen we gebruiken om commando's naar het programma te sturen. De statusbalk gebruiken we om informatie (hints) over deze commando's te laten zien.
Het toevoegen van de statusbar gaat als volgt:
StatusBar
component op de Win32 tab van het
component window.AutoHint
en ParentShowHint
op true
. Dit zorgt ervoor dat
de statusbar de hints van de menubalk en knoppenbalk die we zo meteen gaan
toevoegen zal weergeven.
Bepaalde commando's zullen zowel in de knoppenbalk als in de menubalk voorkomen.
Om deze reden bied C++ Builder de mogelijkheid om commando's te definiëren
in een ActionList
. Later kun je deze commando's dan bij het
definiëren van menu-items en knoppen gebruiken.
Hieronder volgt een overzicht van de commando's die we willen toevoegen:
Commando | Menu | In knoppenbalk? | Beschrijving |
---|---|---|---|
Afsluiten |
- |
Nee |
Sluit de applicatie. |
Blauw |
Kleur |
Ja |
Kleur het kruis blauw. |
Groen |
Kleur |
Ja |
Kleur het kruis groen. |
Rood |
Kleur |
Ja |
Kleur het kruis rood. |
Voordat we deze commando's gaan toevoegen gaan we eerst de plaatjes voor de knoppen tekenen:
Deze plaatjes moet je opslaan in een zogenaamde ImageList
component.
Dit gaat als volgt:
ImageList
component op de Win32 tab van het
component window.ImageList
te benaderen.ImageList
worden toegevoegd:
ImageList
. Er wordt nu een
nieuwe tool de zogenaamde ImageList Editor geopend.
clWhite
.
clWhite
te zetten).
De commando's kun je nu als volgt invoegen:
ActionList
component op de Standard tab van
het component window.ActionList
staat half over het icoontje
van de ImageList
maar je kunt het met je muis eenvoudig een
stukje verplaatsen.) Dit icoontje is bij het uitvoeren van het programma
niet zichtbaar maar kan bij het ontwikkelen van het programma gebruikt worden
om de ActionList
te benaderen.ActionList
worden
toegevoegd:
ActionList
. Er wordt nu een
nieuwe tool de zogenaamde ActionList Editor geopend.
Caption
op Af&sluiten
. Een
&
voor een letter zorgt ervoor dat deze letter in het menu
onderstreept wordt als de ALT toets wordt ingedrukt. Je kunt dan die letter
samen met de ALT toets indrukken om de betreffende menuoptie te selecteren.
Hint
op Sluit de applicatie.
Name
op Afsluiten
.
OnExecute
event. De memberfunctie
TForm1::AfsluitenExecute
wordt nu aangemaakt en in de editor
geopend. Vul hier de volgende code in:
Close();
Als het commando Afsluiten
wordt gekozen wordt nu dus de
memberfunctie Close
van Form1
(overgeërfd
van TCustomForm
) aangeroepen. Deze memberfunctie zorgt ervoor
dat het window wordt gesloten.
Caption
op &Blauw
.
Category
op Kleur
.
Hint
op Kleur het kruis blauw.
ImageIndex
op 0
. Dit koppelt plaatje nummer
0
uit de ImageList
aan dit commando.
Name
op Blauw
.
OnExecute
event. De memberfunctie
TForm1::BlauwExecute
wordt nu aangemaakt en in de editor geopend.
Vul hier de volgende code in:
penKleur=clBlue;
Invalidate();
Als het commando Blauw
wordt gekozen wordt nu dus de
penKleur
op clBlue
gezet en wordt het window opnieuw
getekend.
Groen
en
Rood
toe.
We kunnen nu de menubalk als volgt toevoegen:
MainMenu
component op de Standard tab van het
component window.MainMenu
staat half over het icoontje
van de ImageList
maar je kunt het met je muis eenvoudig een
stukje verplaatsen.) Dit icoontje is bij het uitvoeren van het programma
niet zichtbaar maar kan bij het ontwikkelen van het programma gebruikt worden
om het MainMenu
te benaderen.Mainmenu
worden
toegevoegd:
MainMenu
. Er wordt nu een
nieuwe tool de zogenaamde Menu Designer geopend.
Action
op
Afsluiten
.
Afsluiten
.
Caption
op
&Kleur
.
hint
op Kies de kleur van het kruis.
Kleur
.
Action
op Blauw
.
Blauw
.
Action
op Groen
.
Groen
.
Action
op Rood
.We kunnen nu de knoppenbalk als volgt toevoegen:
ToolBar
component op de Win32 tab van het component
window.Color
op clBtnFace
.
Flat
op true
. Dit zorgt ervoor dat de knop
pas verschijnt als je er met de muis overheen gaat (ziet er moderner uit).
Images
op ImageList1
.
ToolBar
worden toegevoegd:
ToolBar
op de rechter muisknop en kies New
Button. De knop voor het commando Blauw
verschijnt.
Action
op
Blauw
.
Groen
en Rood
toe.TForm1
de property ShowHint
op true
.
Helaas staan de knoppenbalk en de statusbalk over het kruis heen getekend.
Dit kunnen we oplossen door het kruis niet rechtstreeks op de
TForm
te tekenen maar gebruik te maken van de
PaintBox
component:
PaintBox
component op de System tab van het
component window.Align
op alClient
. Dit zorgt ervoor dat de
PaintBox
alle (nog vrije) ruimte in de TForm1
opvult.
Form1::TForm1
en zet ClientHeight
op 298
.
PaintBox
en gebruik de Object Inspector om de event
OnPaint
af te vangen.
TForm1::PaintBox1Paint
de volgende code
in:
PaintBox1->Canvas->Pen->Color=penKleur;
PaintBox1->Canvas->Pen->Width=2;
PaintBox1->Canvas->MoveTo(0, 0);
PaintBox1->Canvas->LineTo(PaintBox1->ClientWidth, PaintBox1->ClientHeight);
PaintBox1->Canvas->MoveTo(0, PaintBox1->ClientHeight);
PaintBox1->Canvas->LineTo(PaintBox1->ClientWidth, 0);
PaintBox1->Canvas->Font->Name="Arial";
PaintBox1->Canvas->Font->Size=20;
PaintBox1->Canvas->Font->Color=clBlue;
PaintBox1->Canvas->TextOut(10, 10, "Hallo!");
TForm1::FormPaint
(ook uit de class
declaratie in Unit1.h).
PaintBox
en gebruik de Object Inspector om de event
OnMouseDown
af te vangen.
TForm1::PaintBox1MouseDown
de volgende
code in:
switch (Button) {
case mbRight: RoodExecute(this); break;
case mbLeft: GroenExecute(this); break;
}
TForm1::FormMouseDown
(ook uit de
class declaratie in Unit1.h).
TForm1::FormKeyPress
gewijzigd worden in:
switch (Key) {
case 'R':
case 'r': RoodExecute(this); break;
case 'G':
case 'g': GroenExecute(this); break;
case 'B':
case 'b': BlauwExecute(this); break;
}
Het aanpassen van de kleur door op de muisknoppen te klikken is nu een beetje overbodig.
TForm1::PaintBox1MouseDown
.
Opdracht E.
Breidt het programma uit met een zogenaamd "context menu" (een popup menu
dat verschijnt als je op de rechtermuisknop drukt) waarmee je de kleur van
het kruis kunt kiezen. Dit kun je doen door een |
Windows kent een aantal zogenaamde "common dialogs". Dit zijn standaard in Windows beschikbare dialogs. Laten we ons programma eens uitbreiden met de standaard dialog om een kleur te kiezen.
ColorDialog
component op de Dialogs tab van
het component window.ColorDialog
te
benaderen.Kiezen
toe. (Denk aan het invullen van alle relevante properties.)
OnExecute
event van dit commando af en voer de volgende
code in voor de memberfunctie TFom1::KiezenExecute
:
if (ColorDialog1->Execute())
penKleur=ColorDialog1->Color;
Invalidate();
TForm1::FormKeyPress
als volgt uit:
switch (Key) {
case 'R':
case 'r': RoodExecute(this); break;
case 'G':
case 'g': GroenExecute(this); break;
case 'B':
case 'b': BlauwExecute(this); break;
case 'K':
case 'k': KiezenExecute(this); break;
}
K
toets te drukken.Opdracht F.Zorg ervoor dat het commando kiezen ook in de menubalk, in de knoppenbalk en in het popupmenu wordt opgenomen. |
Het is ook mogelijk om zelf dialog schermen te ontwerpen.
Opdracht G.
Zorg ervoor dat Form2 er als volgt uitziet: |
RadioButtonBlauw
wordt gekozen door op deze radiobutton te
dubbelklikken:
void __fastcall TForm2::RadioButtonBlauwClick(TObject *Sender)
{
Form1->BlauwExecute(this);
}
TForm1
in TForm2
aan te
kunnen roepen moet boven in de file Unit2.cpp de volgende regel worden
opgenomen:
#include "Unit1.h"
Opdracht H.
Definieer zelf de code voor |
Opdracht I.Form2 moet met een commando vanuit Form1 worden geopend:
|
Form2
bij het openen meteen de juiste
radiobutton zou selecteren als het kruis al blauw, groen of rood is. Dit
kun je voor elkaar krijgen door het OnShow
event van
TForm2
af te vangen:
void __fastcall TForm2::FormShow(TObject *Sender)
{
switch (Form1->getPenKleur()) {
case clBlue: RadioButtonBlauw->Checked=true; break;
case clGreen: RadioButtonGroen->Checked=true; break;
case clRed: RadioButtonRood->Checked=true; break;
}
}
getPenKleur
van TForm1
moet je
nog wel definiëren. In de headerfile Form1.h moet de volgende declaratie
worden toegevoegd:
TColor __fastcall TForm1::getPenKleur() const;
De definitie moet in de file Form1.cpp worden opgenomen en spreekt voor zich:
TColor __fastcall TForm1::getPenKleur() const
{
return penKleur;
}
Opdracht J.
Ontwerp en implementeer zelf een dialog waarmee de tekst (inhoud en
lettertype) die in het |
Bij het gebruik van Builder zijn er een aantal dingen die heel handig zijn om te weten:
Op de volgende plaatsen vind je meer informatie over het gebruik van C++ Builder.