Éviter les boîtes de dialogue de dialogue dans Delphi et / ou C ++
-
08-07-2019 - |
Question
J'ai souvent besoin de concevoir une boîte de dialogue dans Delphi / C ++ Builder qui permette de modifier diverses propriétés d'un objet, et le code à utiliser ressemble généralement à ceci.
Dialog.Edit1.Text := MyObject.Username;
Dialog.Edit2.Text := MyObject.Password;
// ... many more of the same
if (Dialog.ShowModal = mrOk)
begin
MyObject.Username := Dialog.Edit1.Text;
MyObject.Password := Dialog.Edit2.Text;
// ... again, many more of the same
end;
J'ai également souvent besoin d'un code similaire pour le classement des objets vers / depuis xml / ini-files / que ce soit.
Existe-t-il des expressions ou techniques communes permettant d'éviter ce type de code simple mais répétitif?
La solution
eh bien, quelque chose qui me semble tout à fait précieux est l’assistant GExperts du plugin " Reverse Statement " qui est appelé après avoir installé GExperts en appuyant sur Maj + ALT + R
Ce qu'il fait est de changer automatiquement les assignations pour le bloc en surbrillance. Par exemple:
edit1.text := dbfield.asString;
devient
dbField.asString := edit1.text;
Ce n'est pas exactement ce que vous recherchez, mais un gain de temps considérable lorsque vous avez un grand nombre de missions.
Autres conseils
Voici ma variation à ce sujet. Ce que j'ai fait, après en avoir marre du même code répétitif, a été de nommer toutes les zones d'édition en fonction des noms de nœud XML que je voulais, puis de parcourir les composants et de générer leurs valeurs. Le code XML doit être évident, et je ne dispose que d’une boîte de dialogue et d’une case à cocher, mais vous devriez pouvoir voir l’idée.
procedure TfrmFTPSetup.LoadFromXML(szFileName : string);
var
xComponent : TComponent;
nLoop : Integer;
xMainNode : TXmlNode;
xDocument : TNativeXml;
begin
inherited;
xDocument := TNativeXml.Create;
try
xDocument.LoadFromFile(szFileName);
xMainNode := xml_ChildNodeByName(xDocument.Root, 'options');
for nLoop := 0 to ComponentCount - 1 do
begin
xComponent := Components[nLoop];
if xComponent is TRzCustomEdit then
begin
(xComponent as TRzCustomEdit).Text := xMainNode.AttributeByName[xComponent.Name];
end;
if xComponent is TRzCheckBox then
begin
(xComponent as TRzCheckBox).Checked := xml_X2Boolean(xMainNode.AttributeByName[xComponent.Name], false);
end;
end;
finally
FreeAndNil(xDocument);
end;
end;
procedure TfrmFTPSetup.SaveToXML(szFileName : string);
var
xComponent : TComponent;
nLoop : Integer;
xMainNode : TXmlNode;
xDocument : TNativeXml;
begin
inherited;
xDocument := TNativeXml.CreateName('ftpcontrol');
try
xMainNode := xml_ChildNodeByNameCreate(xDocument.Root, 'options');
for nLoop := 0 to ComponentCount - 1 do
begin
xComponent := Components[nLoop];
if xComponent is TRzCustomEdit then
begin
xMainNode.AttributeByName[xComponent.Name] := (xComponent as TRzCustomEdit).Text;
end;
if xComponent is TRzCheckBox then
begin
xMainNode.AttributeByName[xComponent.Name] := xml_Boolean2X((xComponent as TRzCheckBox).Checked);
end;
end;
xDocument.XmlFormat := xfReadable;
xDocument.SaveToFile(szFileName);
finally
FreeAndNil(xDocument);
end;
end;
L’accès aux propriétés des composants visuels d’une fiche n’est pas une bonne pratique. Il est considéré préférable d'avoir des propriétés séparées. Dans l'exemple ci-dessus, vous avez les propriétés nom d'utilisateur et mot de passe avec les méthodes get et set.
Par exemple:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
private
function GetPassword: string;
function GetUsername: string;
procedure SetPassword(const Value: string);
procedure SetUsername(const Value: string);
public
property Password: string read GetPassword write SetPassword;
property Username: string read GetUsername write SetUsername;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.GetPassword: string;
begin
Result := Edit2.Text;
end;
function TForm1.GetUsername: string;
begin
Result := Edit1.Text;
end;
procedure TForm1.SetPassword(const Value: string);
begin
Edit2.Text := Value;
end;
procedure TForm1.SetUsername(const Value: string);
begin
Edit1.Text := Value;
end;
end.
Cela signifie que vous pouvez modifier les composants visuels de la fiche sans que cela affecte le code d'appel.
Une autre option serait de transmettre l'objet en tant que propriété à la boîte de dialogue.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TUserObject = class(TObject)
private
FPassword: string;
FUsername: string;
public
property Password: string read FPassword write FPassword;
property Username: string read FUsername write FUsername;
end;
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
btnOK: TButton;
procedure btnOKClick(Sender: TObject);
private
FUserObject: TUserObject;
procedure SetUserObject(const Value: Integer);
public
property UserObject: Integer read FUserObject write SetUserObject;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnOKClick(Sender: TObject);
begin
FUserObject.Username := Edit1.Text;
FUserObject.Password := Edit2.Text;
ModalResult := mrOK;
end;
procedure TForm1.SetUserObject(const Value: Integer);
begin
FUserObject := Value;
Edit1.Text := FUserObject.Username;
Edit2.Text := FUserObject.Password;
end;
end.
L’espoir que cela aide.
Delphi a au moins un 'Avec', bien que cela ne résolve pas complètement le problème.
if (Dialog.ShowModal = mrOk)
begin
with MyObject do
begin
Username := Dialog.Edit1.Text;
Password := Dialog.Edit2.Text;
// ... again, many more of the same
end;
end;
Et AFAIK, le constructeur, n’a rien à voir.
La liaison des contrôles aux données fonctionne bien dans Delphi, mais malheureusement uniquement lorsque ces données résident dans un descendant de TDataSet. Vous pourriez écrire un descendant de TDataSet qui utilise un objet pour le stockage de données, et il s'avère qu'une telle chose existe déjà. Voir le lien ci-dessous ... Cette implémentation semble fonctionner uniquement avec des collections d'objets (TCollection ou TObjectList), pas avec des objets isolés.
http://www.torry.net/pages.php?id=563 : recherchez dans la page "Snap Object DataSet"
.Je n'ai pas d'expérience personnelle avec cela, mais ce serait très utile si cela fonctionnait et surtout s'il fonctionnerait également avec des instances d'objet unique, telles qu'une propriété sur un module de données ...
Recherchez le modèle de médiateur " ;. C'est un modèle de conception GoF, et dans leur livre, le GoF motive en fait ce modèle de conception dans une situation quelque peu similaire à celle que vous décrivez ici. Il vise à résoudre un problème différent - le couplage - mais je pense que vous avez également ce problème de toute façon.
En bref, l’idée est de créer un médiateur de dialogue, un objet supplémentaire placé entre tous les widgets de dialogue. Aucun widget ne connaît aucun autre widget, mais chaque widget connaît le médiateur. Le médiateur connaît tous les widgets. Lorsqu'un widget change, il en informe le médiateur. le médiateur en informe ensuite les widgets pertinents. Par exemple, lorsque vous cliquez sur OK, le médiateur peut informer d’autres widgets de cet événement.
Ainsi, chaque widget gère uniquement les événements et les actions qui le concernent. Le médiateur prend en charge l’interaction entre tous les widgets, c’est-à-dire que tout cela "passe-partout". le code est divisé sur tous les widgets, et le "résidu" Ce qui est global pour tous les widgets, c’est l’interaction, et c’est la responsabilité du médiateur.