Inserting set of controls at runtime is extremely slow - delphi

We have a point-of-sale application and in this application we have a scrollbox container. If the seller selects a product, then a new product row is created and inserted into the scrollbox. The product row component is a frame - textboxes, buttons and labels in it.
But here's a little problem by inserting this product row control into the scrollbox at runtime. It's slow. I can see how selecting product draws edittext components slowly into the scrollbox.
I tried to set the components' visibility to false before ScrollBox.InsertControl and enabling it after, but it doesn't help speed up things very much. Also I read about DisableAlign/EnableAlign thing, but I don't know exactly where I have to put this line of code.
How can I speed up inserting this custom component into the form's scrollbox container?

TScrollBox doesn't have BeginUpdate/EndUpdate, but you can get the same effect using WM_SETREDRAW messages. I would probably avoid more heavy handed methods like LockWindowUpdate.
SendMessage(ScrollBox1.Handle, WM_SETREDRAW, 0, 0);
try
// add controls to scrollbox
// set scrollbox height
finally
SendMessage(ScrollBox1.Handle, WM_SETREDRAW, 1, 0);
RedrawWindow(ScrollBox1.Handle, nil, 0, RDW_ERASE or RDW_INVALIDATE or RDW_FRAME or RDW_ALLCHILDREN);
end;

Normally, a control adding to a container takes very little time. There's a high probability this has something to do with the creation of the control rather the insertion.

Try disabling screen updates when adding controls and enable screen update when done in a try-finally routine. Then the screen doesn't have to update for each separate control but just once, when all controls are placed.

Related

When to use TPanel FullRepaint?

I noticed that the resize flicker gets much better when I set TPanel.FullRepaint to False. Since the property exists and is True by default, there must be some reason for that.
How to decide whether it should be set or not?
The help just states:
FullRepaint controls how the panel responds when it is resized. When FullRepaint is true, the entire panel, including the beveled border repaints when the size changes. When FullRepaint is false, only the area inside the beveled border repaints.
http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.ExtCtrls.TPanel.FullRepaint
That text says what it does, but not why ...
The effect of a missing Fullrepaint can be shown and you will have to decide if you need it or not.
Place a panel on a form, set anchors to all directions
Set PaintCaption to false or use a empty caption
Place another panel on the form, so that if you are resizing the
form, parts of the first panel will be covered by the second panel.
Run the program and size the form, somtimes the borders of the first panel will not be refreshed.
This happens because in WMWindowPosChanged in case of (FullRepaint or (ShowCaption and (Caption <> ''))) a invalidate will be called, otherwise only InvalidateRect(Handle, Rect, True) of a rects only containing the right and/or bottom border are invalidated. (thanks to Sertac Akyuz for correction)
As you mentioned avoiding invalidate reduces flicker and in many cases the need for a full invalidate is not given, so the user can decide on his own how to proceed.
Panels as the rarely will be used, upper without Fullrepaint
In previous versions of Windows (not sure up to which version, exactly) FullRepaint was required to prevent graphical artefacting on panel borders when a form was resized.
To the best of my knowledge, this hasn't been an issue since at least Windows XP.

TTabSet tab Order....Different At Run Time Than Design Time

I am running Lazarus 0.9.30.2.
I have a TForm on which there is a TPageControl. Within the TPageControl there is a series of TTabSheets. At runtime the order of the TTabSheets differs from design time (see picture).
The order in design time is what I want to see at runtime, at least for the very first time the form is displayed. Why does the order change at run time and is there a way to control this?
#TLama is correct that this is related to way the Windows tab control behaves when in multi-line view. The behaviour you are observing is related to the way selection is handled for multi-line tabs. When you select a tab it is always shown in the bottom row because the visual cue to indicate which tab is selected can only really work for tabs in the bottom row.
Given that constraint the control simply has to rearrange rows of tabs as you modify the selected tab. It's astoundingly confusing for the user. Good UI design never has UI elements changing position like this.
Clearly what is happening here is that the rearrangement is happening at runtime when the form is first shown and for whatever reason this is resulting in a different arrangement from the design time arrangement. Given that the user can arrange the rows in any order just by selecting them I'm not sure you should worry about what order the rows appear in.
If you are dead set on forcing a particular arrangement when the form first shows you can add code like this to a OnCreate handler for the form:
PageControl1.ActivePage := TabSheet9;
PageControl1.ActivePage := TabSheet5;
PageControl1.ActivePage := TabSheet1;
Best practise for UI design is to avoid multi-line tab controls and I urge you to attempt to re-design your UI that way.

Delphi: 3 Tool Buttons - 3 Frames = switching

I have 3 grouped Tool Buttons (a Tool Bar). One of them is always down. And I have 3 frames. What is the easiest and right way to change the frames switching among the buttons?
Thanks!
The right way is moot at best. One of the easiest ways of many can be to set unique Tags for the grouped buttons, f.i. 0, 1, 2, then set all of the three button's 'OnClick' to the same handler and show one of your frames according to the tag of the clicked button and hide the others:
procedure TForm1.ToolButton1Click(Sender: TObject);
begin
Frame1.Hide; // will return immediately if already hidden
Frame2.Hide;
Frame3.Hide;
case TToolButton(Sender).Tag of
0: Frame1.Show;
1: Frame2.Show;
2: Frame3.Show;
end;
end;
This is assuming you've already put the frames on your form at design time. Don't forget setting the Grouped property of the buttons and their Styles to 'tbsCheck'.
If the frames are all in the same position on the screen, then doing it the way the Sertac suggests will make it really cumbersome to see in the IDE what they look like on their owner form
I suggest you put the frames in a page control or tab control.

Frame behavior in Delphi application issue

I have an application that uses a frame extensively and needs to hide/show certain buttons depending on which form is active at the time. In order to keep the buttons neat and organized appropriately, I have put them on panels and show or hide the panels as needed for each form. My problem is when each form is initially created, the panels on the frame are out of order even though I am explicitly telling them which order to put themselves into. After I hide and re-show the form, the panels are in the correct order. Any suggestions on how to keep them in the proper order from the very beginning?
Instead of giving the panels explicit positions, try giving them alignments. They tend to stick better than way, and they do a better job of resizing if you resize the form.
You can also try using a stackpanel (or was it flowpanel?) as parent for the panels. Then you will have a order instead of a position to manipulate.
Maybe you can have a look at the DevExpress LAyoutControl? It helps us creating interfaces that always look good, no matter if we show or hide certain groups / panels. It even allows for run-time customization of the interface, if you want that!
You may try to organize them by coordinates i.e.: setting Top and Left. Unless your panels are aligned, this will always work (but it takes bit lot of work).
I had this problem and I found that the solution was to do this in FormCreate (or in a CMShowingChanged method of your frame):
MyPanel1.Align := alNone;
MyPanel2.Align := alNone;
MyPanel1.Align := alBottom;
MyPanel2.Align := alBottom;
Restore in the order that you need - this seemed to sort out the order visually.

GridPanel does not adjust at first resize

I have a problem I am unable to solve even though I spend long time trying to do it.
I usually use GridPanels to align controls on forms. It has, however, an annoying bug. When the GridPanel align mode is alClient and I maximize its parent window, the GridPanel adjusts to the new size of that window, however, the controls laying on the grid do not. They stay in the same position they were before window resize.
It happens only at the first window's maximization. If the window is first resized manually, everyting is OK. I think the grid adjusts its child controls after the second resize event (??).
What to to do make GridPanel work properly if it comes to this bug? It might be enough to send a message to it (but what message?), I do not know. I tried to use Realign, Refresh etc., but they did not help.
Thanks for your help in advance,
Mariusz.
Ah, I've had similar issues as well. It might be related to a resizing problem in the VCL. You might want to try the fix by Andreas Hausladen. It seems to work for me in most of the cases.
Changing the width / invalidating the control doesn't work for me (something changed with recent versions of RAD Studio?).
Anyway a similar, simple workaround along that line is:
procedure TForm1.FormResize(Sender: TObject);
begin
GridPanel1.ControlCollection.BeginUpdate;
GridPanel1.ControlCollection.EndUpdate;
end;
I found one trick.
in OnResize event of parent of gridpanel, change gridpanel's width by 1 pixel.
then gridPanel will notice size changed, then rearrange sub-controls in gridpanel..
sample is below..
procedure TForm1.FormResize(Sender: TObject);
begin
GridPanel1.Width := GridPanel1.Width - 1; // subtract 1
GridPanel1.Width := GridPanel1.Width + 1; // recover width by adding 1
end;
I have had this error too, on several projects. I'm not sure how I solved this (it was on projects for my previous employer, I don't have access to this source code anymore). I think I had te redraw / refresh that parent frame or form on which the GridPanel was placed.
on the resize of the owner call GridPanel.Invalidate.
I didn't test it. I hope it's work.

Resources