Search code examples
delphifiremonkey

Using threads in firemonkey frames


There is a main form, when you click on the button, a frame is loaded into the form.

Button code:

procedure TMain.ButtonObjectsClick(Sender: TObject);
begin
  TFrameSystems.Execute(LayoutMain,
  procedure(Frame: TFrameSystems)
  begin
    // DoSomething
  end,
  procedure(Frame: TFrameSystems; Success: Boolean)
  begin
    // DoSomething
  end);
end;

Frame:

  ...

  TKey = class
    public
      S: string;
      constructor Create(const AStr: string);
  end;

  TFrameSystems = class(TFrameOverlay)
    ...
  private
    ...
    FProcCallback: TProc<TFrameSystems, Boolean>;
    FActiveUser: TUser;
    procedure UpdateBuildingList;
    ...
  public
    constructor Create(AOwner: TComponent); override;
    class procedure Execute(AParent: TControl; ProcSet: TProc<TFrameSystems>; ProcExecuted: TProc<TFrameSystems, Boolean>);
    property ActiveUser: TUser read FActiveUser write FActiveUser;
    ...
    { Public declarations }
  end;
implementation


uses 
 ...
{$R *.fmx}

class procedure TFrameSystems.Execute(AParent: TControl; ProcSet: TProc<TFrameSystems>;
  ProcExecuted: TProc<TFrameSystems, Boolean>);
begin
  var Frame := TFrameSystems.Create(AParent);
  Frame.Parent := AParent;
  Frame.FProcCallback := ProcExecuted;
  Frame.Align := TAlignLayout.Contents;
  Frame.BringToFront;
  if Assigned(ProcSet) then
    ProcSet(Frame);
end;

constructor TFrameSystems.Create(AOwner: TComponent);
begin
  inherited;
  Name := '';
  FActiveUser := ClientModule.User;
  UpdateBuildingList;
end;

procedure TFrameSystems.UpdateBuildingList;
begin
  TThread.CreateAnonymousThread(procedure
  var
    s1: TStrings;
    JSONObject: TJSONObject;
    i: integer;
  begin
    JSONObject := ClientModule.ServerMethodsClient.RefreshUserBuildingList(ActiveUser.id);
    ActiveUser.RefreshBuildingList(JSONObject);
    s1 := TStringList.Create;
    for I := 0 to ActiveUser.BuldingList.Count - 1 do
    begin
    with cb_object_list.Items do
      AddObject(ActiveUser.BuldingList[I].Name, TKey.Create(IntToStr(ActiveUser.BuldingList[I].id)));
      s1.AddObject(ActiveUser.BuldingList[I].Kks, TIntObj.Create(ActiveUser.BuldingList[I].id));
    end;
    Edit1.AssignItems(s1);
  end).Start;

end;


When the frame opens, I need to upload some data to the combobox. If you call the UpdateBuildingList function and do it without a thread, then the program freezes for a while.

I tried to solve the problem by adding an anonymous thread to this function and it seems like the problem is solved. However, why does it work without TThread.Synchronize and in general, did I do the right thing by creating an anonymous thread?


Solution

  • The problem here is that you are populating your ComboBox from secondary thread item by item which is not allowed and could lead to various problems.

    I also see you are using AddObject in order to add object to each ComboBox item.

    You should do this in a different way that is tread safe. How?

    First create yourself a new string list and populate it using AddObject like you populate your combo box now. You can freely do this in secondary thread.

    Once you have done that simply assign your StringList to your ComboBox.Items property. You do this within Synchronize so that it is done within main thread.

    Here is a quick code example:

    //Populate StringList with strings and connected objects
    SL := TStringList.Create;
    SL.AddObject('Panel1',Panel1);
    SL.AddObject('Panel2',Panel2);
    //Assign StringList to ComboBox.Items property
    //This can be done since TStringList is actually descendant class from 
    //TStrings which is the property type of ComboBox.Items property
    ComboBox1.Items := SL;