I often need to design a dialog in Delphi/C++Builder that allows various properties of an object to be modified, and the code to use it typically looks like this.
我经常需要在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;
I also often need similar code for marshalling objects to/from xml/ini-files/whatever.
我也经常需要类似的代码来将对象编组到xml / ini-files /中。
Are there any common idioms or techniques for avoiding this kind of simple but repetitive code?
是否有任何常用的习惯用法或技巧来避免这种简单但重复的代码?
6 个解决方案
#1
3
well, something that I feel completely invaluable is the GExperts plugin wizard "Reverse Statement" which is invoked after installing GExperts by pressing Shift + ALT + R
好吧,我认为非常宝贵的东西是GExperts插件向导“反向声明”,它是在按Shift + ALT + R安装GExperts后调用的
What it does is automatically switch the assignments around for the highlighted block. For example:
它的作用是自动切换突出显示的块的分配。例如:
edit1.text := dbfield.asString;
becomes
dbField.asString := edit1.text;
Not exactly what your looking for, but a huge time saver when you have a large number of assignments.
不完全是你想要的,但是当你有大量的任务时,节省了大量的时间。
#2
3
Here's my variation on this. What I did, having got fed up with the same repetitive code, was to name all the edit boxes according to the XML node names I wanted, then iterate around the components and output their values. The XML code should be obvious, and I only have an edit and checkbox, but you should be able to see the idea.
这是我的变化。我做了什么,厌倦了相同的重复代码,是根据我想要的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;
#3
1
It's not considered good practice to access properties of visual components on a form. It is considered better to have seperate properties. In the example above you would have username and password properties with get and set methods.
在表单*问可视组件的属性并不是一种好习惯。具有单独的属性被认为是更好的。在上面的示例中,您将拥有带get和set方法的用户名和密码属性。
For example:
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.
This means you can change the visual components on the form without having it affecting the calling code.
这意味着您可以更改表单上的可视组件,而不会影响调用代码。
Another option would be to pass the object as a property to the dialog;
另一种选择是将对象作为属性传递给对话框;
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.
Hope that helps.
希望有所帮助。
#4
0
Delphi at least have 'With', though it doesn't solve the problem completely.
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;
And builder AFAIK has nothing alike.
和建筑师AFAIK没有任何相似之处。
#5
0
Binding controls to data works well in Delphi, but unfortunately only when that data resides in a TDataSet descendant. You could write a TDataSet descendant that uses an object for data storage, and it turns out that one such thing already exists. See link below... This implementation appears to only work with collections of objects (TCollection or TObjectList), not single objects.
将控件绑定到数据在Delphi中运行良好,但不幸的是,只有当数据驻留在TDataSet后代时。您可以编写一个使用对象进行数据存储的TDataSet后代,事实证明已经存在这样的事情。请参阅下面的链接...此实现似乎仅适用于对象集合(TCollection或TObjectList),而不是单个对象。
http://www.torry.net/pages.php?id=563 - search the page for for "Snap Object DataSet"
http://www.torry.net/pages.php?id=563 - 在页面中搜索“Snap Object DataSet”
I have no personal experience with this, but it would be very useful if it works and especially if it would also work with single object instances, such as a property on a data module...
我对此没有任何个人经验,但如果它有效,它将非常有用,特别是如果它也适用于单个对象实例,例如数据模块上的属性......
#6
0
Look up "mediator pattern". It's a GoF design pattern, and in their book the GoF in fact motivate this design pattern with a somewhat similar situation to what you're describing here. It aims at solving a different problem -- coupling -- but I think you have this problem too anyhow.
查找“中介模式”。这是一个GoF设计模式,在他们的书中,GoF实际上激发了这种设计模式,其情况与您在此描述的情况有些相似。它旨在解决一个不同的问题 - 耦合 - 但我认为你无论如何都有这个问题。
In short, the idea is to create a dialog mediator, an extra object that sits in between all the dialog widgets. No widget knows about any other widget, but each widget does know the mediator. The mediator knows all widgets. When one widget changes it informs the mediator; the mediator then informs the relevant widgets about this. For example, when you click OK the mediator may inform other widgets about this event.
简而言之,我们的想法是创建一个对话框中介,一个位于所有对话框小部件之间的额外对象。没有小部件知道任何其他小部件,但每个小部件都知道中介。调解员知道所有小部件。当一个小部件发生变化时,它会通知中介者;然后,调解员会通知相关小部件。例如,单击“确定”时,介体可能会通知其他窗口小部件有关此事件的信息。
This way each widgets takes care of events and actions related to itself only. The mediator takes care of the interaction between all widgets, so all this "boilerplate" code is split over all widgets, and the "residue" which is global to all widgets is the interaction, and it is the responsibility of the mediator.
这样,每个小部件都只处理与自身相关的事件和动作。调解器负责所有小部件之间的交互,因此所有这些“样板”代码都分散在所有小部件上,并且所有小部件全局的“残留”是交互,并且它是调解器的责任。
#1
3
well, something that I feel completely invaluable is the GExperts plugin wizard "Reverse Statement" which is invoked after installing GExperts by pressing Shift + ALT + R
好吧,我认为非常宝贵的东西是GExperts插件向导“反向声明”,它是在按Shift + ALT + R安装GExperts后调用的
What it does is automatically switch the assignments around for the highlighted block. For example:
它的作用是自动切换突出显示的块的分配。例如:
edit1.text := dbfield.asString;
becomes
dbField.asString := edit1.text;
Not exactly what your looking for, but a huge time saver when you have a large number of assignments.
不完全是你想要的,但是当你有大量的任务时,节省了大量的时间。
#2
3
Here's my variation on this. What I did, having got fed up with the same repetitive code, was to name all the edit boxes according to the XML node names I wanted, then iterate around the components and output their values. The XML code should be obvious, and I only have an edit and checkbox, but you should be able to see the idea.
这是我的变化。我做了什么,厌倦了相同的重复代码,是根据我想要的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;
#3
1
It's not considered good practice to access properties of visual components on a form. It is considered better to have seperate properties. In the example above you would have username and password properties with get and set methods.
在表单*问可视组件的属性并不是一种好习惯。具有单独的属性被认为是更好的。在上面的示例中,您将拥有带get和set方法的用户名和密码属性。
For example:
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.
This means you can change the visual components on the form without having it affecting the calling code.
这意味着您可以更改表单上的可视组件,而不会影响调用代码。
Another option would be to pass the object as a property to the dialog;
另一种选择是将对象作为属性传递给对话框;
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.
Hope that helps.
希望有所帮助。
#4
0
Delphi at least have 'With', though it doesn't solve the problem completely.
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;
And builder AFAIK has nothing alike.
和建筑师AFAIK没有任何相似之处。
#5
0
Binding controls to data works well in Delphi, but unfortunately only when that data resides in a TDataSet descendant. You could write a TDataSet descendant that uses an object for data storage, and it turns out that one such thing already exists. See link below... This implementation appears to only work with collections of objects (TCollection or TObjectList), not single objects.
将控件绑定到数据在Delphi中运行良好,但不幸的是,只有当数据驻留在TDataSet后代时。您可以编写一个使用对象进行数据存储的TDataSet后代,事实证明已经存在这样的事情。请参阅下面的链接...此实现似乎仅适用于对象集合(TCollection或TObjectList),而不是单个对象。
http://www.torry.net/pages.php?id=563 - search the page for for "Snap Object DataSet"
http://www.torry.net/pages.php?id=563 - 在页面中搜索“Snap Object DataSet”
I have no personal experience with this, but it would be very useful if it works and especially if it would also work with single object instances, such as a property on a data module...
我对此没有任何个人经验,但如果它有效,它将非常有用,特别是如果它也适用于单个对象实例,例如数据模块上的属性......
#6
0
Look up "mediator pattern". It's a GoF design pattern, and in their book the GoF in fact motivate this design pattern with a somewhat similar situation to what you're describing here. It aims at solving a different problem -- coupling -- but I think you have this problem too anyhow.
查找“中介模式”。这是一个GoF设计模式,在他们的书中,GoF实际上激发了这种设计模式,其情况与您在此描述的情况有些相似。它旨在解决一个不同的问题 - 耦合 - 但我认为你无论如何都有这个问题。
In short, the idea is to create a dialog mediator, an extra object that sits in between all the dialog widgets. No widget knows about any other widget, but each widget does know the mediator. The mediator knows all widgets. When one widget changes it informs the mediator; the mediator then informs the relevant widgets about this. For example, when you click OK the mediator may inform other widgets about this event.
简而言之,我们的想法是创建一个对话框中介,一个位于所有对话框小部件之间的额外对象。没有小部件知道任何其他小部件,但每个小部件都知道中介。调解员知道所有小部件。当一个小部件发生变化时,它会通知中介者;然后,调解员会通知相关小部件。例如,单击“确定”时,介体可能会通知其他窗口小部件有关此事件的信息。
This way each widgets takes care of events and actions related to itself only. The mediator takes care of the interaction between all widgets, so all this "boilerplate" code is split over all widgets, and the "residue" which is global to all widgets is the interaction, and it is the responsibility of the mediator.
这样,每个小部件都只处理与自身相关的事件和动作。调解器负责所有小部件之间的交互,因此所有这些“样板”代码都分散在所有小部件上,并且所有小部件全局的“残留”是交互,并且它是调解器的责任。