Pregunta

A menudo necesito diseñar un cuadro de diálogo en Delphi / C ++ Builder que permita que se modifiquen varias propiedades de un objeto, y el código para usarlo generalmente se ve así.

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;

También a menudo necesito un código similar para ordenar objetos a / desde xml / ini-files / lo que sea.

¿Hay modismos o técnicas comunes para evitar este tipo de código simple pero repetitivo?

¿Fue útil?

Solución

bueno, algo que considero completamente invaluable es el GExperts asistente de complemento " Declaración inversa " que se invoca después de instalar GExperts presionando Shift + ALT + R

Lo que hace es cambiar automáticamente las asignaciones para el bloque resaltado. Por ejemplo:

edit1.text := dbfield.asString;

se convierte

dbField.asString := edit1.text;

No es exactamente lo que está buscando, pero ahorra mucho tiempo cuando tiene una gran cantidad de tareas.

Otros consejos

Aquí está mi variación sobre esto. Lo que hice, harto del mismo código repetitivo, fue nombrar todos los cuadros de edición de acuerdo con los nombres de nodo XML que quería, luego iterar alrededor de los componentes y generar sus valores. El código XML debería ser obvio, y solo tengo una edición y una casilla de verificación, pero debería poder ver la 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;

No se considera una buena práctica acceder a las propiedades de los componentes visuales en un formulario. Se considera mejor tener propiedades separadas. En el ejemplo anterior, tendría propiedades de nombre de usuario y contraseña con los métodos get y set.

Por ejemplo:

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.

Esto significa que puede cambiar los componentes visuales en el formulario sin que afecte al código de llamada.

Otra opción sería pasar el objeto como una propiedad al diálogo;

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.

Espero que eso ayude.

Delphi al menos tiene 'With', aunque no resuelve el problema por completo.

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;

Y el constructor AFAIK no tiene nada parecido.

Los controles de enlace a datos funcionan bien en Delphi, pero desafortunadamente solo cuando esos datos residen en un descendiente TDataSet. Podría escribir un descendiente TDataSet que use un objeto para el almacenamiento de datos, y resulta que tal cosa ya existe. Vea el enlace a continuación ... Parece que esta implementación solo funciona con colecciones de objetos (TCollection o TObjectList), no con objetos individuales.

http://www.torry.net/pages.php?id=563 : busca en la página "Snap Object DataSet"

No tengo experiencia personal con esto, pero sería muy útil si funciona y especialmente si también funcionaría con instancias de un solo objeto, como una propiedad en un módulo de datos ...

Busque " patrón de mediador " ;. Es un patrón de diseño de GoF, y en su libro el GoF de hecho motiva este patrón de diseño con una situación algo similar a lo que estás describiendo aquí. Su objetivo es resolver un problema diferente, el acoplamiento, pero creo que usted también tiene este problema de todos modos.

En resumen, la idea es crear un mediador de diálogo, un objeto adicional que se encuentra entre todos los widgets de diálogo. Ningún widget conoce ningún otro widget, pero cada widget conoce al mediador. El mediador conoce todos los widgets. Cuando un widget cambia, informa al mediador; el mediador luego informa a los widgets relevantes sobre esto. Por ejemplo, cuando hace clic en Aceptar, el mediador puede informar a otros widgets sobre este evento.

De esta manera, cada widget se encarga de los eventos y acciones relacionados solo con sí mismo. El mediador se encarga de la interacción entre todos los widgets, por lo que todo esto '' repetitivo '' el código se divide en todos los widgets, y el "residuo" Lo que es global para todos los widgets es la interacción, y es responsabilidad del mediador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top