Avoiding a diagonal boiler room in Delphi and / or C ++

Often I need to create a dialog box in Delphi / C ++ Builder that allows you to change various properties of an object, and the code used in it usually looks like this.

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 to sort objects to / from xml / ini-files / whatever.

Are there common idioms or methods to avoid such simple but repetitive code?

+4
source share
6 answers

Well, I find GExperts ' Reverse Operator Plugin Wizard, which is called after installing GExperts by pressing Shift + ALT + R, to be completely invaluable.

What he does is automatically switch assignments for the selected block. For instance:

 edit1.text := dbfield.asString; 

becomes

 dbField.asString := edit1.text; 

Not exactly what you are looking for, but a huge time saver when you have a large number of appointments.

+3
source

Here is my option. What I did, getting the same duplicate code, was to name all the edit fields according to the XML node names I wanted, and then iterate over the components and display their values. The XML code should be obvious, and I only have the editing and checkbox, but you should be able to see this 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; 
+3
source

Access to the properties of visual components in a form is not considered good practice. It is considered better to have separate properties. In the above example, you have username and password properties with get and set methods.

For instance:

 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 that you can change the visual components of the form without affecting the calling code.

Another option is to pass the object as a property in the dialog box;

 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 this helps.

+1
source

Delphi at least has a C, although it does not completely solve the problem.

 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 the AFAIK builder has nothing in common.

0
source

Data binding controls work well in Delphi, but unfortunately only when that data is in a TDataSet descendant. You can write a TDataSet descendant that uses an object to store data, and it turns out that one such thing already exists. See the Link below ... This implementation only works with collections of objects (TCollection or TObjectList), and not with individual objects.

http://www.torry.net/pages.php?id=563 - find the page for the "Snap Object DataSet"

I have no personal experience with this, but it would be very helpful if it worked, and especially if it would also work with instances of individual objects, such as a property in the data module ...

0
source

Look up the “ plectrum pattern ”. This is a GoF design template, and in their book, GoF actually motivates this design template with some similar situations to what you describe here. It aims to solve another problem - communication - but I think that you also have this problem.

In short, the idea is to create a dialog intermediary, an additional object that is located between all the widgets of the dialog. No widget knows about any other widgets, but each widget knows an intermediary. The mediator knows all the widgets. When one widget changes, it informs the intermediary; the pick then informs the relevant widgets about it. For example, when you click OK, the pick can inform other widgets about this event.

Thus, each widget only cares about events and actions related only to itself. The intermediary takes care of the interaction between all widgets, so all this "template" code is divided into all widgets, and the "remainder", which is global for all widgets, is an interaction, and it is the responsibility of the intermediary.

0
source

Source: https://habr.com/ru/post/1277649/


All Articles