MODIFIEZ DES COMPOSANTS EXISTANTS SANS MODIFIER VOS FICHES EXISTANTES (+ DIDACTI

JulioDelphi Messages postés 2226 Date d'inscription dimanche 5 octobre 2003 Statut Membre Dernière intervention 18 novembre 2010 - 15 janv. 2005 à 18:28
cs_systmd Messages postés 41 Date d'inscription mercredi 25 février 2004 Statut Membre Dernière intervention 29 août 2012 - 12 sept. 2009 à 19:16
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/28866-modifiez-des-composants-existants-sans-modifier-vos-fiches-existantes-didacticiel

cs_systmd Messages postés 41 Date d'inscription mercredi 25 février 2004 Statut Membre Dernière intervention 29 août 2012
12 sept. 2009 à 19:16
Bonjour,
C'est remarquable, simple et trés clair.
Merci de nous faire partager cette méthode.
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
18 avril 2008 à 18:30
Je tiens à remercier publiquement Cirec pour son aide dans l' élaboration des nouvelles fonctionnalités de mon compo tcyDBGrid.
Celui-ci étant bientôt finaliser, il est normal de le publier sur CS pour en faire profiter tout le monde.

Merci à Cirec, à DelphiProg, aux Admins (qui ont pas mal de boulot) et longue vie à CS!
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
17 avril 2008 à 15:24
Mais j' appelle la procédure originale dans celle dont j' ai fait dévier le flux des messages!?

if Assigned(FSauvDBGridWndProc)
then FSauvDBGridWndProc(Msg);

Je comprends pas ...
Utilisateur anonyme
17 avril 2008 à 15:21
En fait c'est simple
le windowproc est surchargé depuis la classe TComponent par toutes les classes intermédiaires jusqu'au composant final.
Et dans leurs surcharge respective si un message n'est pas à traiter car non utile, il est renvoyé au DefWindowProc :
"DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);"

Donc si toi en cours de route tu le modifie et tu ne le restitue pas les classes parentes ne pourront plus travailler correctement.

Qu'on me corrige si je dis une connerie ^^
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
17 avril 2008 à 12:50
Salut à tous,

j' ai compris que l' erreur venait de :
procedure TcyDBGrid.cyDBGridWndProc(var Msg: TMessage);
begin
// We have to call this all the time (not just when scrollBar properties handlers changes) :
ShowScrollBar(Handle, SB_HORZ, FHorizontalScrollBar);
ShowScrollBar(Handle, SB_VERT, FVerticalScrollBar);

if Assigned(FSauvDBGridWndProc)
then FSauvDBGridWndProc(Msg);
end;

Alors j' ai corrigé en ajoutant ceci:
procedure TcyDBGrid.BeforeDestruction;
begin
WindowProc := FSauvDBGridWndProc; // Avoid overload component error ...
Inherited;
end;

Voilà, ça marche très bien! Par contre, je comprends pas pourquoi il me donnait l' erreur citée plus haut, si quelqu' un a une idée ...

A+ et merci encore pour ton excelent tuto!
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
16 avril 2008 à 17:30
Salut DelphiProg,
après avoir fait quelques compos c' est beaucoup plus simple de comprendre ton tuto :)

Comme j' en ai eu besoin et que je fais légèrement differemment (en plus simple), voilà comment j' ai procédé :

J' ai un compo appelé TcyDBGrid dans mon unité cyDBGrid qui permet entre autre la gestion du MouseWheel et permet de cacher les ScrollBars gràce aux sources déposées sur CS:

type
TcyDBGrid = class(dbgrids.TDBGrid)
private
etc ...

à la fin de la déclaration du compo, je déclare ceci:
TDBGrid = class(tcyDBGrid);
// Déclarer une nouvelle class TDBGrid pour que les compos dbgrid.TDBGrid déjà utilisés dans vos appli aient les mêmes options!

Comme tu peux le voir, mon compo n' a pas besoin de s' appeler TDBGrid.

Ensuite, dans mes applis cette fois, il ne me reste plus qu' à mettre dans les Uses le nom de l' unité de mon compo sans rien faire d' autre:
Uses classes, windows, dbgrids, etc ..., cyDBGrid;
Le compilateur semble aller chercher la Class TDBGrid dans mon unité cyDBGrid car c' est la dernière à être déclarée dans les Uses.

Pas besoin donc de déclarer une class TDBGrid héritant de cyDBGRid.TcyDBGrid! dans toutes les forms de mes applis.

Le mouseWheel est très bien géré mais par contre j' ai une erreur lorsque j' utilise l' option pour cacher les ScrollBars: l' erreur survient en fermant mon appli seulement: "control DBGrid2 has no parent window"

Voici mon unité :
unit cyDBGrid;

interface

uses Classes, Windows, Grids, DBGrids, Messages;

type
TMouseWheelMode = (mwDoNothing, mwNavigate, mwSelect);

TcyDBGrid = class(dbgrids.TDBGrid)
private
FOnSelectCell: TSelectCellEvent;
FSauvDBGridWndProc: TWndMethod;
FHorizontalScrollBar: Boolean;
FVerticalScrollBar: Boolean;
FMouseWheelMode: TMouseWheelMode;
procedure SetHorizontalScrollBar(const Value: Boolean);
procedure SetVerticalScrollBar(const Value: Boolean); // Save original WindowProc ...
protected
procedure Loaded; override;
function SelectCell(ACol, ARow: Longint): Boolean; override;
procedure cyDBGridWndProc(var Msg: TMessage);
procedure MouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
procedure MouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
public
constructor Create(AOwner: TComponent); override;
property FixedCols;
function CellRectX(ACol, ARow: Longint): TRect;
published
property Col; // Current column
property LeftCol; // First displayed column
property Row; // Current row
property VisibleColCount; // Visible normal columns (Not Fixed Columns)
property VisibleRowCount; // Visible normal rows (Not Fixed Rows)
// property TopRow; Not working, return always 1 ...
// property Selection; Not working, return always Col e Row ...

property MouseWheelMode: TMouseWheelMode read FMouseWheelMode write FMouseWheelMode default mwDoNothing;
property HorizontalScrollBar: Boolean read FHorizontalScrollBar write SetHorizontalScrollBar default true;
property VerticalScrollBar: Boolean read FVerticalScrollBar write SetVerticalScrollBar default true;
property OnSelectCell: TSelectCellEvent read FOnSelectCell write FOnSelectCell;
end;

TDBGrid = class(tcyDBGrid); // Déclarer une nouvelle class TDBGrid pour que les compos
// dbgrid.TDBGrid déjà utilisés dans vos appli aient les mêmes options!

procedure Register;

implementation

{ TcyDBGrid}
constructor TcyDBGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSauvDBGridWndProc := WindowProc;
FMouseWheelMode := mwDoNothing;
FHorizontalScrollBar := true;
FVerticalScrollBar := true;

// Mouse wheel :
With TDrawGrid(self) do
begin
OnMouseWheelDown := MouseWheelDown;
OnMouseWheelUp := MouseWheelUp;
end;
end;

procedure TcyDBGrid.Loaded;
begin
Inherited;
end;

function TcyDBGrid.SelectCell(ACol, ARow: Longint): Boolean;
begin
Result := True;
if Assigned(FOnSelectCell) then FOnSelectCell(Self, ACol, ARow, Result);
end;

function TcyDBGrid.CellRectX(ACol, ARow: Longint): TRect;
begin
RESULT := CellRect(ACol, ARow);
end;

procedure TcyDBGrid.cyDBGridWndProc(var Msg: TMessage);
begin
// We have to call this all the time (not just when scrollBar properties handlers changes) :
ShowScrollBar(Handle, SB_HORZ, FHorizontalScrollBar);
ShowScrollBar(Handle, SB_VERT, FVerticalScrollBar);

if Assigned(FSauvDBGridWndProc)
then FSauvDBGridWndProc(Msg);
end;

procedure TcyDBGrid.SetHorizontalScrollBar(const Value: Boolean);
begin
FHorizontalScrollBar := Value;

if not (FHorizontalScrollBar and FVerticalScrollBar)
then WindowProc := cyDBGridWndProc
else WindowProc := FSauvDBGridWndProc;

Invalidate;
end;

procedure TcyDBGrid.SetVerticalScrollBar(const Value: Boolean);
begin
FVerticalScrollBar := Value;

if not (FHorizontalScrollBar and FVerticalScrollBar)
then WindowProc := cyDBGridWndProc
else WindowProc := FSauvDBGridWndProc;

Invalidate;
end;

procedure TcyDBGrid.MouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
Handled := true;

case FMouseWheelMode of
mwNavigate:
begin
if DataSource.DataSet.Active
then
if not DataSource.DataSet.Eof
then DataSource.DataSet.Next;
end;

mwSelect:
begin

end;
end;
end;

procedure TcyDBGrid.MouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
Handled := true;

case FMouseWheelMode of
mwNavigate:
begin
if DataSource.DataSet.Active
then
if not DataSource.DataSet.Bof
then DataSource.DataSet.Prior;
end;

mwSelect:
begin

end;
end;
end;

procedure Register;
begin
RegisterComponents('Cindy DB', [TcyDBGrid]);
end;

end.

A+
jihelb Messages postés 49 Date d'inscription lundi 27 janvier 2003 Statut Membre Dernière intervention 24 mars 2017
20 janv. 2005 à 09:51
BRAVO Delphiprog pour ton didacticiel ! (10/10)
J'ai appris une chose qui m'aurait rendu de grands services par le passé, et qui m'ouvre des horizons.
Je ne suis par contre pas certain d'avoir bien compris ta conclusion (contrairement à tous j'ai bien peur)
"... quand ils doivent installer de nouveaux composants juste pour tester une application écrite par d'autre."
Celà signifie-t-il qu'au lieu d'installer l'unité TrucUnit contenant le composant TrucCompo, il suffit de rajouter dans l'unité où TrucCompo est utilisé la ligne alias (et de rajouter dans le FormCreate les lignes pour les éventuels Onxxxxxx) ?
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
19 janv. 2005 à 10:10
oui exact!
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
18 janv. 2005 à 20:56
Euh...non, on a pas besoin du source non plus. Il suffit juste d'avoir l'unité. Comment fait Delphi pour retrouver les propriétés/événements/méthodes dans l'édition personnelle puisque cette mouture n'est pas livrée avec les codes sources mais des .DCU à la place.
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
18 janv. 2005 à 18:53
Ok, cette fois g compris et c' est gràce à JulioDelphi qui a bien voulu perdre son temps à m' expliquer :
on a besoin du source du compo mais on a pas besoin de l' installer. Merci Merci ...
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
18 janv. 2005 à 11:59
Je l' ai bien compris DelphiProg.
C' est d' ailleurs ce que je dis dans mon commentaire!
Mais ça répond pas à mes 2 questions, désolé ou alors g pas tout pigé.
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
17 janv. 2005 à 20:05
Mauricio : les propriétés/évènements n'apparaissent pas et ne peuvent pas apparaître dans l'inspecteur d'objet pour une simple raison : le composant n'est pas enregistré dans Delphi.
Le fait de déclarer en section publiée est sans influence au niveau de l'inspecteur d'objets dans ce cas.

En revanche, on peut y trouver un avantage dans le cas où l'on veut que ces propriétés soient enregistrées automatiquement quand on utilise des fonctions comme WriteComponentRes, WriteComponentResFile qui sauvegardent dans le stream toutes les propriétés publiées. C'est ce que fait Delphi avec les fichiers DFM des fiches.

En résumé, ajouter des propriétés publiées à un composant existant doit avoir un objectif bien précis et justifié.
Cette conclusion rejoint donc tout à fait les propos de Delphimaniac.

Je suis heureux que même les experts sur ce forum aient trouvé un intérêt à ce tuto.
delphimaniac Messages postés 5 Date d'inscription dimanche 25 mai 2003 Statut Membre Dernière intervention 13 juin 2005
17 janv. 2005 à 18:12
Hello DelphiProg,

très bonne source, très interessante. Mais comme le dis jmp77, il faut quand même réfléchir à son utilisation. Dans le cas effectivement de tests d'une application qui utilise un composant qu'on a pas, ou dans le cas d'une utilisation bien particulière d'un composant à un endroit donné, sachant qu'on en aura pas l'utilité ailleurs, je suis d'accord. Par contre dès qu'il peut y avoir ré-utilisation potentielle, je penses qu'il faut impérativement revenir à la méthode classique du composant. En fait c'est une question de volume, modifier deux ou trois unités comme cela n'est pas trop génant, mais penser à le faire à chaque fois qu'on utilise un composant de cette façon, et surtout si on l'utilise souvent, cela devient contraignant.

En attendant, merci pour ce tuto qui est vraiment très clair.

Delphimaniac
jmp77 Messages postés 1119 Date d'inscription lundi 4 février 2002 Statut Membre Dernière intervention 4 octobre 2006 7
17 janv. 2005 à 17:39
Hello DelphiProg,

Super Sourçe mais vu l'auteur je m'en serais douter.

Deja avec ta réponse sur le forum c'était clair mais alors maintenant c'est super fluide.
Honnetement plutot que de creer ces compos puis obligé tout le monde a les installé pour tester ta source au moins avec cette méthode c'est bien plus pratique. Biensure il ne faut pas en abuser et être prudent.

Enfin Merci à toi DelphiProg qui arrive toujours a nous apprendre des choses super interessantes.

Note finale sans appel 10/10.

Bonne prog,
JMP77.
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
17 janv. 2005 à 14:55
Quand je dis:
"Ça a l' avantage de pouvoir utiliser les 2 class et le systeme ajoutera les propriétés/events automatiquement (enfin je crois) manquantes à TStaticTextEx à l' ouverture de la fiche. "
Je voulais dire:
"Ça a l' avantage de pouvoir utiliser les 2 class et le systeme ajoutera les propriétés/events automatiquement (enfin je crois) manquantes aux compos de type TStaticTextEx à l' ouverture de la fiche. "
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
17 janv. 2005 à 14:52
En tout cas, c très bien expliqué!
Même moi, qui ne fais pas de compos, g presque tout pigé. J' ai qques trucs que je ne comprends pas:
- Ne serait-il pas plus simple de chercher les dfm qui utilisent le compo pour remplacer la déclaration de la class par une autre? TStaticText vers TStaticTextEx?
Ça a l' avantage de pouvoir utiliser les 2 class et le systeme ajoutera les propriétés/events automatiquement (enfin je crois) manquantes à TStaticTextEx à l' ouverture de la fiche.
- Dans ta conclusion, tu dis ... non, pas la ligne "Vive la POO" l' autre: "... quand ils doivent installer de nouveaux composants juste pour tester une application écrite par d' autres". Si je me rapelle, il y avait une source qui utilisait un compo genre TSpinEdit. J' avais laissé un message à celui qui a posté la source pour qu' il remplace le compo utilisé par un TSpinEdit pour eviter d' aller chercher le source du compo sur le net et de devoir l' installer. Maintenant que je viens donc de lire le fichier pdf, il me semble que ta méthode ne nous permettra pas de se passer de l' unit du compo original étant donné que, dans les Uses, nous devons déclarer l' unité du compo original qui est, dans ton exemple StdCtrls. Je dis ça parce que seulement apres tu fais:
type
TStaticText = class(StaticTextEx.TStaticText);
Donc je pense, vu aussi qu' à l' ouverture de la form, les nouvelles propriétes n' apparaissent pas (se sont celles de StaticText de l' unité StdCtrls), que l' on a besoin de l' unité du compo original. Faudrait essayer avec un autre compo qui ne serait pas dans StdCtrls pour verifier ça ...
cs_Delphiprog Messages postés 4297 Date d'inscription samedi 19 janvier 2002 Statut Membre Dernière intervention 9 janvier 2013 32
16 janv. 2005 à 23:52
Merci InekMan.

Manchester : venant de toi, je suis sur que tu as une arrière pensée et que ta question ne peut pas être stupide. Je la prend donc très au sérieux.

Le code généré par le compilateur dénote une différence d'1 Ko environ par rapport à l'utilisation d'un TStaticText standard. Mais cette différence est faussée dans la mesure où, pour la réaliser, on doit désactiver une partie du code de l'unité de la fiche.
Il serait mieux de comparer avec un nouveau composant qui soit réellement un descendant de TStaticText.
Il faudrait aussi mesurer le temps passé à construire un paquet, à l'installer puis, et c'est surement là que le bât blesse, à remplacer tous les TStatictext par le nouveau composant créé. Les utilisateurs des outils GExperts apprécieront l'efficacité de la commande "Replace components". Mais il faudra, malgré tout, inspecter fiche après fiche et procéder aux remplacements nécessaires.
C'est donc un travail fastidieux à réaliser pour des applications comportant des dizaines de fiches.

Bien entendu, cette technique n'entend pas se substituer à celle de la "vraie" création de composants réutilisables pour de futures applications. Elle vise simplement à remplacer très rapidement un type de composant sans devoir remanier les boites de dialogue d'une application.
Et là, je dirai qu'il n'y a pas photo en temps de mise en oeuvre.

Les performances du code ?
Je serais tenté de rapprocher ma démarche à celle d'un transtypage de classe. Et là, on ne se pose jamais la question. Et pourtant...
On ne se la pose pas beaucoup plus quand il faut utiliser des directives comme Dynamic ou Virtual...et là dessus, les créateurs de composants feraient bien de se demander quelle option est la meilleure pour la suite. Encore faudrait-il qu'ils connaissent la nuance...

Quant à utiliser des intruments de mesure, il faudrait le faire sur une application plus importante que celle ci pour obtenir des mesures significatives.

Je reste ouvert à toutes propositions.

Encore merci Manchester d'avoir ouvert un débat qui promet d'être passionnant.
Inekman Messages postés 291 Date d'inscription dimanche 2 février 2003 Statut Membre Dernière intervention 30 juin 2006
16 janv. 2005 à 13:51
excellente cette astuce. Chapeau bas Delphiprog.
cs_ManChesTer Messages postés 374 Date d'inscription vendredi 20 octobre 2000 Statut Modérateur Dernière intervention 15 janvier 2021
15 janv. 2005 à 20:58
Petite question stupide...

As-tu étudié le code généré par le compilateur et ses performances ?

Amicalement

ManChesTer.
JulioDelphi Messages postés 2226 Date d'inscription dimanche 5 octobre 2003 Statut Membre Dernière intervention 18 novembre 2010 14
15 janv. 2005 à 18:28
:)
Magnifique ! les expliquations sont trèès détaillées et très bonnes ! J'ai appris des choses "de base" grâce a cette lecture (10 minutes suffisent).
Ca me donne envie de faire de meme avec la création complete de composants :)
Bravo encore DelphiProg !
10/10

ps : ça reponds encore mieux a la question du forum