Search code examples
user-interfacedelphidelphi-xe7paradigmsvisual-paradigm

How to automatically save the GUI to file? (Paradigm)


The good (and bad) old Delphi taught us the "classic" way of building application because of the way we write code "behind" the IDE.

Based on this paradigm, I built some time ago a library that allows me to save/load the GUI to INI file with a single line of code.

LoadForm(FormName)

BAM! That's it! No more INI files!

The library only saves "relevant" properties. For example, for a TCheckBox it saves only its Checked property not also its Color or Top/Left. But it is more than enough.

Saving the form has no issues. However, I have "problems" during app initialization. They are not quite problems, but the initialization code is not that nice/elegant.

For example when I assign Checkbox1.Checked := True it will trigger the OnClick event (supposing that the checkbox was unchecked at design time). But assigning a False value (naturally) will not trigger the OnClick event.

Therefore, I have to manually call CheckBox1Click(Sender) after SaveForm(FormName) to make sure that whatever code is in CheckBox1Click, it gets initialized. But this raises another problem. The CheckBox1Click(Sender) might be called twice during application start up (once by SaveForm and once my me, manually).

Of course, in theory the logic of the program should be put in individual objects and separated from the GUI. But even if we do this, the initialization problem remains. You load the properties of the object from disk and you assign them to the GUI. When you set whatever value you have in your object to Checkbox1, it will (or not) call CheckBox1Click(Sender) which will set the value back into the object.

On app startup:

procedure TForm.FormCreate (Sender: TObject);
begin
  LogicObject.Load(File); // load app logic
  Checkbox1.Checked := LogicObject.Checked; // assign object to GUI
end;

procedure TForm.CheckBox1Click(Sender: TObject);
begin
  LogicObject.Checked := Checkbox1.Checked; 
end;

Probably the solution involves writing stuff like this for EVERY control on the form:

OldEvent := CheckBox1.OnClick;
CheckBox1.OnClick := Nil;
CheckBox1.Checked := something;
CheckBox1.OnClick := OldEvent;

Not elegant.

Question:
How do you solve this specific problem OR what is your approach when saving/restoring your GUI to/from disk?


Solution

  • This is one of the things which botthered me in some components from the beginning. What I know the are 3 options, except separating GUI and the business logic as @David said, which is not always an option.

    1. As you wrote above, always unassign the events so they don't get triggered
    2. Use non-triggered events such as OnMouseDown or OnMouseUp
    3. Or a similar solution that I use and I think is the most elegant

    Create a global variable FormPreparing which is set during initialization and check its value at the beginning of the events like below.

    procedure TForm.FormCreate (Sender: TObject);
    begin
      FormPreparing := True;
      try    
        LogicObject.Load(File); // load app logic
        Checkbox1.Checked := LogicObject.Checked; // assign object to GUI
      finally
        FormPreparing := False;
      end;
    end;
    
    procedure TForm.CheckBox1Click(Sender: TObject);
    begin
      if FormPreparing then
        Exit;
      LogicObject.Checked := Checkbox1.Checked;
    end;