Reputation: 73
I create over 100 rectangles at runtine in code below;
var
RectT: TRectangle;
MyThread: TThread;
Layout1: TLayout;
begin
MyThread := TThread.CreateAnonymousThread(procedure()
begin
TThread.Synchronize(nil, procedure()
var
z, i: integer;
begin
z := 0;
for i := 0 to 99 do
begin
RectT := TRectangle.Create(Self);
RectT.Name := 'Rectangle' + IntToStr(i);
RectT.Align := TAlignLayout.Top;
RectT.Margins.Top := 6;
RectT.Position.Y := z;
RectT.Height := 20;
RectT.Parent := Layout1;
if (i mod 10) = 0 then Layout1.UpdateEffects;
inc(z, 20);
end;
end);
end);
MyThread.FreeOnTerminate := True;
MyThread.Start;
end;
Why didn't display the rectangle when is created and only are displayed when finish the iteration of all rectangles?.
Upvotes: 0
Views: 1165
Reputation: 23036
If this code is running on the main thread (which appears to be the case since you don't mention any threading) then the first opportunity that the FMX runtime has of visually updating the UI is when your code has itself finished running.
If you want the UI to update to display the rectangles as they are added, then you will need to re-write this to use an approach that allows the UI an opportunity to repaint periodically.
Your updated code in the question now involves a thread. However, in your posted code you Synchronize() all of the work in that thread. Synchronized code runs in the main thread so the consequence of synchronizing all of the work is to remove any benefit of the thread at all.
You are nearly there however.
A small change to your posted code so that the layout child objects are added in the thread, synchronizing only the repainting of the layout object itself periodically, then you get the result you are seeking:
var
MyThread: TThread;
begin
MyThread := TThread.CreateAnonymousThread
(
procedure()
var
z, i: integer;
RectT: TRectangle;
begin
z := 0;
for i := 0 to 999 do
begin
RectT := TRectangle.Create(Self);
RectT.Name := 'Rectangle' + IntToStr(i);
RectT.Align := TAlignLayout.Top;
RectT.Margins.Top := 6;
RectT.Position.Y := z;
RectT.Height := 20;
RectT.Parent := Layout1;
TThread.Synchronize(nil, procedure()
begin
Layout1.Repaint;
end);
inc(z, 20);
end;
end
);
MyThread.FreeOnTerminate := True;
MyThread.Start;
end;
I have increased the number of child objects in this demonstration of the approach to 999 since 99 was not enough to see any appreciable change in performance.
As written, the above code also repaints after every rectangle has been added, but this could be easily modified in a way similar to your posted code so that the layout is repainted only after "batches" of rectangles have been added:
if (i mod 10) = 0 then
TThread.Synchronize(nil, procedure()
begin
Layout1.Repaint;
end);
This is a simplistic approach, addressing the immediate problem of updating the UI to show the progress of some background changes made to that UI using this very simple test case. Whether this is actually the most appropriate approach in your specific case only you can really say.
Upvotes: 1
Reputation: 686
First, you need to move the for loop in one thread and the creation of the rectangles in a Synchronize call, as Deltics has done. The difference is that you do not need the call to Repaint and you need to use the currentthread to pass the call for synchronization.
Try this (in OnClick event of a Button):
procedure TForm4.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure
var
I,z: Integer;
Total: Integer;
begin
Total := 0;
for I := 1 to 99 do
begin
TThread.Synchronize (TThread.CurrentThread,
procedure
var
RectT: TRectangle;
begin
RectT := TRectangle.Create(Self);
RectT.Name := 'Rectangle' + IntToStr(i);
RectT.Align := TAlignLayout.Top;
RectT.Margins.Top := 6;
RectT.Position.Y := z;
RectT.Height := 20;
RectT.Parent := Layout1;
Inc(z, 20);
end);
end;
end).Start;
end;
Upvotes: 2