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?

È stato utile?

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

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top