4

我经常需要在 Delphi/C++Builder 中设计一个对话框,允许修改对象的各种属性,使用它的代码通常如下所示。

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;

我也经常需要类似的代码来将对象编组到/从 xml/ini-files/whatever。

是否有任何常见的习惯用法或技术来避免这种简单但重复的代码?

4

6 回答 6

3

这是我对此的变化。我已经厌倦了相同的重复代码,我所做的是根据我想要的 XML 节点名称命名所有编辑框,然后遍历组件并输出它们的值。XML 代码应该很明显,我只有一个编辑和复选框,但您应该能够看到这个想法。

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;
于 2008-10-10T15:16:08.133 回答
3

好吧,我觉得完全无价的东西是GExperts插件向导“反向语句”,它在安装 GExperts 后按 Shift + ALT + R 调用

它所做的是自动为突出显示的块切换分配。例如:

edit1.text := dbfield.asString;

变成

dbField.asString := edit1.text;

不完全是您想要的,但是当您有大量任务时可以节省大量时间。

于 2008-10-10T15:54:58.180 回答
1

访问表单上可视组件的属性并不是一种好的做法。具有单独的属性被认为更好。在上面的示例中,您将拥有带有 get 和 set 方法的用户名和密码属性。

例如:

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.

这意味着您可以在不影响调用代码的情况下更改表单上的可视组件。

另一种选择是将对象作为属性传递给对话框;

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.

希望有帮助。

于 2008-10-13T10:12:45.717 回答
0

Delphi 至少有'With',虽然它并没有完全解决问题。

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;

和建设者 AFAIK 没有任何相似之处。

于 2008-10-10T14:33:33.773 回答
0

将控件绑定到数据在 Delphi 中运行良好,但不幸的是,只有当数据驻留在 TDataSet 后代中时。您可以编写一个使用对象进行数据存储的 TDataSet 后代,事实证明这样的东西已经存在。请参阅下面的链接...此实现似乎仅适用于对象集合(TCollection 或 TObjectList),而不适用于单个对象。

http://www.torry.net/pages.php?id=563 - 在页面中搜索“Snap Object DataSet”

我对此没有个人经验,但如果它有效,特别是如果它也适用于单个对象实例,例如数据模块上的属性,它将非常有用......

于 2008-10-10T17:03:32.707 回答
0

查“中介模式”。这是一种 GoF 设计模式,在他们的书中,GoF 实际上激发了这种设计模式,情况与您在此处描述的情况有些相似。它旨在解决一个不同的问题——耦合——但我认为无论如何你也有这个问题。

简而言之,这个想法是创建一个对话中介,一个位于所有对话小部件之间的额外对象。没有小部件知道任何其他小部件,但每个小部件都知道中介。中介知道所有小部件。当一个小部件发生变化时,它会通知中介;调解器然后通知相关的小部件。例如,当您单击确定时,调解器可能会通知其他小部件有关此事件。

这样,每个小部件只处理与自身相关的事件和操作。中介负责所有小部件之间的交互,因此所有这些“样板”代码都拆分到所有小部件上,对所有小部件全局的“剩余”是交互,它是中介的责任。

于 2008-10-11T15:47:24.373 回答