Evitare la finestra di dialogo della finestra di dialogo in Delphi e / o C ++
-
08-07-2019 - |
Domanda
Spesso ho bisogno di progettare una finestra di dialogo in Delphi / C ++ Builder che consenta di modificare varie proprietà di un oggetto e il codice per usarlo in genere è simile a questo.
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;
Spesso ho anche bisogno di un codice simile per eseguire il marshalling degli oggetti su / da xml / ini-files / qualunque cosa.
Esistono idiomi o tecniche comuni per evitare questo tipo di codice semplice ma ripetitivo?
Soluzione
bene, qualcosa che mi sembra del tutto inestimabile è la GExperts procedura guidata plugin " Dichiarazione inversa " che viene richiamato dopo l'installazione di GExperts premendo Maiusc + ALT + R
Quello che fa è cambiare automaticamente le assegnazioni per il blocco evidenziato. Ad esempio:
edit1.text := dbfield.asString;
diventa ??p>
dbField.asString := edit1.text;
Non esattamente quello che stai cercando, ma un enorme risparmio di tempo quando hai un gran numero di incarichi.
Altri suggerimenti
Ecco la mia variazione su questo. Quello che ho fatto, dopo essermi stufato dello stesso codice ripetitivo, è stato nominare tutte le caselle di modifica in base ai nomi dei nodi XML desiderati, quindi scorrere i componenti e generare i loro valori. Il codice XML dovrebbe essere ovvio e ho solo una modifica e una casella di controllo, ma dovresti essere in grado di vedere l'idea.
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;
Non è considerata buona pratica accedere alle proprietà dei componenti visivi in ??un modulo. È considerato migliore avere proprietà separate. Nell'esempio sopra avresti le proprietà username e password con i metodi get e set.
Ad esempio:
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.
Ciò significa che è possibile modificare i componenti visivi nel modulo senza che ciò influisca sul codice chiamante.
Un'altra opzione sarebbe quella di passare l'oggetto come proprietà alla finestra di dialogo;
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.
Spero che sia d'aiuto.
Delphi ha almeno 'With', sebbene non risolva completamente il problema.
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;
E il costruttore AFAIK non ha nulla di simile.
I controlli di associazione ai dati funzionano bene in Delphi, ma sfortunatamente solo quando quei dati risiedono in un discendente TDataSet. È possibile scrivere un discendente TDataSet che utilizza un oggetto per l'archiviazione dei dati e si scopre che una cosa del genere esiste già. Vedi link sotto ... Questa implementazione sembra funzionare solo con raccolte di oggetti (TCollection o TObjectList), non singoli oggetti.
http://www.torry.net/pages.php?id=563 - cerca nella pagina per " Snap Object DataSet "
Non ho esperienza personale con questo, ma sarebbe molto utile se funzionasse e specialmente se funzionasse anche con istanze a singolo oggetto, come una proprietà su un modulo dati ...
Cerca " modello di mediatore " ;. È un modello di progettazione GoF e nel loro libro il GoF in realtà motiva questo modello di progettazione con una situazione in qualche modo simile a quella che stai descrivendo qui. Mira a risolvere un problema diverso - l'accoppiamento - ma penso che tu abbia questo problema in ogni caso.
In breve, l'idea è quella di creare un mediatore di dialogo, un oggetto in più che si trova tra tutti i widget di dialogo. Nessun widget conosce altri widget, ma ogni widget conosce il mediatore. Il mediatore conosce tutti i widget. Quando un widget cambia, informa il mediatore; il mediatore informa quindi i relativi widget al riguardo. Ad esempio, quando si fa clic su OK, il mediatore può informare altri widget su questo evento.
In questo modo ogni widget si occupa di eventi e azioni relativi solo a se stesso. Il mediatore si occupa dell'interazione tra tutti i widget, quindi tutto questo "boilerplate" il codice è suddiviso su tutti i widget e il "residuo" che è globale per tutti i widget è l'interazione ed è la responsabilità del mediatore.