Developpez.com - C
X

Choisissez d'abord la catégorieensuite la rubrique :



Création d'un composant C++ Builder

Par CGi

Le 8 septembre 2003


6 - Contrôle fenêtré :

Un contrôle fenêtré est un contrôle qui peut recevoir le focus clavier, qui possède un Handle de fenêtre et qui peut à l'occasion contenir d'autres contrôles. Avec C++ Builder pour construire un contrôle fenêtré, il faut que le composant soit un descendant de TWinControl.

Pour cet exemple nous allons construire un contrôle case à cocher dont la partie cochée sera représentée soit par un carré, une croix ou une coche dont on pourra choisir la couleur et dont on pourra changer l'état par l'appui sur la touche espace s'il détient le focus.



Nous ferons donc descendre notre composant de TCustomControl (descendant direct de TWinControl) car il possède en plus de TWinControl un Canvas permettant de dessiner.

Fichier "CaseACocher.h" :

#ifndef CaseACocherH
#define CaseACocherH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
//---------------------------------------------------------------------------
enum TTypeCoche {tcCarre, tcCroix, tcCoche};

class PACKAGE TCaseACocher : public TCustomControl
{
private:
        bool FChecked;
        TColor FCouleur;
        TTypeCoche FCoche;
        bool ALeFocus;
protected:
        void __fastcall Paint();
        DYNAMIC void __fastcall KeyPress(char &Key);
        DYNAMIC void __fastcall DoEnter();
        DYNAMIC void __fastcall DoExit();
        DYNAMIC void __fastcall Click();
        DYNAMIC void __fastcall MouseDown(TMouseButton Button,
                                   Classes::TShiftState Shift, int X, int Y);
        virtual void __fastcall SetChecked(bool Value);
        virtual void __fastcall SetCouleur(TColor Value);
        virtual void __fastcall SetCoche(TTypeCoche Value);
        virtual void __fastcall TextChanged(TMessage &Msg);
BEGIN_MESSAGE_MAP
  MESSAGE_HANDLER(CM_TEXTCHANGED, TMessage, TextChanged)
END_MESSAGE_MAP(TCustomControl)
public:
        __fastcall TCaseACocher(TComponent* Owner);
__published:
        __property Caption;
        __property OnClick;
        __property OnEnter;
        __property OnExit;
        __property OnKeyUp;
        __property OnKeyPress;
        __property OnKeyDown;
        __property TabOrder;
        __property TabStop;
        // nouvelles propriétés :
        __property bool Checked =  {read=FChecked, write=SetChecked};
        __property TColor Couleur =  {read=FCouleur, write=SetCouleur};
        __property TTypeCoche Coche = {read=FCoche, write=SetCoche};
};
//---------------------------------------------------------------------------
#endif

Dans le fichier CaseACocher.h comme pour les autres chapitres : déclarations des propriétés avec leurs données membres et méthodes de mise à jour respectives pour les nouvelles propriétés, déclaration des méthodes à redéfinir, déclaration de la méthode TextChanged. Elle nous servira à rafraîchir l'affichage si le texte du contrôle est modifié. Cette méthode existe dans la librairie CLX mais malheureusement pas dans la VCL. Elle est appelée si le texte d'un contrôle a changé. Donc nous allons intercepter le message Windows CM_TEXTCHANGED qui est envoyé au contrôle quand son texte a été modifié, pour le lier à la méthode TextChanged dans la carte des messages afin qu'elle soit appelée à l'interception de ce message. Nous y déclarons aussi une donnée membre qui nous indiquera si notre contrôle détient le focus. Nous aurons aussi comme au chapitre précédent un type enum pour le choix du dessin de la coche.


Fichier "CaseACocher.cpp"

#include <vcl.h>

#pragma hdrstop

#include "CaseACocher.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
//  ValidCtrCheck est utilisé pour vérifier que les composants créés n'ont
// aucune fonction virtuelle pure.
static inline void ValidCtrCheck(TCaseACocher *)
{
        new TCaseACocher(NULL);
}
//---------------------------------------------------------------------------
__fastcall TCaseACocher::TCaseACocher(TComponent* Owner)
        : TCustomControl(Owner)
{
     Width = 100;
     Height = 20;
     TabStop = true;
     FChecked = false;
     FCouleur = clBlack;
     FCoche = tcCarre;
     ALeFocus = false;
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::Paint()
{
   RECT CoRect;
   CoRect.left = 2;
   CoRect.top = 2;
   CoRect.right = 18;
   CoRect.bottom = 18;
   Canvas->Brush->Color = clWindow;
   Canvas->FillRect(CoRect);
   DrawEdge(Canvas->Handle, &CoRect, EDGE_SUNKEN, BF_RECT );
   Canvas->Brush->Color = Color;
   Canvas->TextOut(25,2,Caption);
   if(Checked)
     {
      switch (Coche)
        {
         case tcCarre :
           Canvas->Brush->Color = Couleur;
           RECT InRect;
           InRect.left = 6;
           InRect.top = 6;
           InRect.right = 14;
           InRect.bottom = 14;
           Canvas->FillRect(InRect);
           break;
         case tcCroix :
           Canvas->Pen->Color = Couleur;
           Canvas->Pen->Width = 2;
           Canvas->MoveTo(6,6);
           Canvas->LineTo(14,14);
           Canvas->MoveTo(6,14);
           Canvas->LineTo(14,6);
           break;
         case tcCoche :
           Canvas->Pen->Color = Couleur;
           Canvas->Pen->Width = 2;
           Canvas->MoveTo(5,9);
           Canvas->LineTo(8,12);
           Canvas->LineTo(14,6);
           break;
        }
     }
   if (ALeFocus)
   {
     Canvas->Brush->Color = Color;
     TRect FocRect;
     FocRect.Top = 1;
     FocRect.Left = 22;
     FocRect.Right = Width - 2;
     FocRect.Bottom = Height - 2;
     Canvas->DrawFocusRect(FocRect);
   }
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::KeyPress(char &Key)
{
    if (Key == 32) Checked = !Checked;
    TCustomControl::KeyPress(Key);
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::Click()
{
    TCustomControl::Click();
    Checked = !Checked;
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::MouseDown(TMouseButton Button,
                                  Classes::TShiftState Shift, int X, int Y)
{
     SetFocus();
     ALeFocus = true;
     Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::DoEnter()
{
     TCustomControl::DoEnter();
     ALeFocus = true;
     Invalidate(); // Pour afficher le rectangle de focus.
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::DoExit()
{
     TCustomControl::DoExit();
     ALeFocus = false;
     Invalidate(); // Pour effacer le rectangle de focus.
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::TextChanged(TMessage &Msg)
{  
     Invalidate(); // Pour afficher la modification du texte.
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::SetChecked(bool Value)
{
     FChecked = Value;
     Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::SetCouleur(TColor Value)
{
     FCouleur = Value;
     Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TCaseACocher::SetCoche(TTypeCoche Value)
{
     FCoche = Value;
     Invalidate();
}
//---------------------------------------------------------------------------
namespace Caseacocher
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(TCaseACocher)};
                 RegisterComponents("MesCompo", classes, 0);
        }
}

Dans le constructeur initialisation des données membres, propriétés héritées, TabStop mise à true pour permettre au contrôle de recevoir le focus.
Dans la méthode Paint : dessin de la case à l'aide de la fonction de l'API Windows DrawEdge, dessin du texte, dessin de la coche si la case est cochée en fonction du choix fait dans la propriété Coche, puis dessin du rectangle de focus à l'aide de la méthode du Canvas DrawFocusRect si le contrôle détient le focus.
La méthode KeyPress change l'état de la case à cocher si la touche "Espace" est enfoncée. La méthode Click change aussi l'état de la case à cocher et la méthode MouseDown donne le focus au contrôle. On aurait pu lui donner le focus dans sa méthode Click, mais je l'ai fait dans le MouseDown pour que le comportement de notre case à cocher soit identique à la CkeckBox de Windows.
Les méthodes DoEnter et DoExit rafraîchissent l'affichage du contrôle s'il prend ou perd le focus et mettent à jour la donnée membre ALeFocus. Pareil pour TextChanged si le texte du contrôle est modifié. Dans les méthodes redéfinies on appelle la méthode de la classe ancêtre correspondante, par exemple TCustomControl::Click() dans la méthode Click(), ceci afin de pouvoir traiter les événements correspondants à ces méthodes. Les méthodes SetChecked et SetCouleur changent l'état des nouvelles propriétés et mettent à jour l'affichage du contrôle après leurs changements.

Avec cet exemple nous pouvons remarquer que la construction d'un contrôle fenêtré n'est pas beaucoup plus compliqué qu'un contrôle ordinaire.



Télécharger les sources : Caseacocher.zip
(Est inclus le fichier "Caseacocher.dcr" contenant l'icône qui le représente sur la palette de composants)
Je vous mets en prime un deuxième contôle fenêtré à télécharger : Colorbtn.zip
Il s'agit d'un contrôle semblable à un TButton auquel on peux changer sa couleur. Je ne commente pas son code source qui est fort semblable au contôle précédant.

A bientôt pour la suite,
CGi

Avec la contribution d'Alacazam pour la relecture.





Sommaire

1 - Création d'un composant pas à pas.
2 - Création d'un composant graphique.
3 - Propriété de type TStrings.
4 - Propriété de type TBitmap et contrôle du Owner.
5 - Propriété de type enum.
6 - Contrôle fenêtré.
7 - Evénement personnalisé et sous-propriétés.
8 - Composant pouvant interagir avec ses semblables.
9 - Ressources dans un composant.
10 - Boîte de dialogue dans un composant.
11 - Composant conteneur.
12 - Les éditeurs de composants. Nouveau



C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?

  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.

Assembleur
  Assembleur sous Visual C++.

C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.

  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.

DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.

Java
  Applet java.





Copyright 2002-2016 CGi - Tous droits réservés CGi. Toutes reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation écrite de la part de l'auteur, propriétaire des droits intellectuels.
Les codes sources de ce document sont fournis en l'état. L'utilisateur les utilise à ses risques et périls, sans garantie d'aucune sorte de la part de l'auteur. L'auteur n'est responsable d'aucun dommage subi par l'utilisateur pouvant résulter de l'utilisation ou de la distribution des codes sources de ce document.
De la même façon, l'auteur n'est en aucun cas responsable d'une quelconque perte de revenus ou de profits, ou de données, ou de tous dommages directs ou indirects, susceptibles de survenir du fait de l'utilisation des codes sources de ce document, quand bien même l'auteur aurait été averti de la possibilité de tels dommages. L'utilisation des codes sources de ce document vaut acceptation par l'utilisateur des termes de la licence ci-dessus.

Contacter le responsable de la rubrique C