I have only one form in my application, where I insert a frame accordingly the user action.
This form is inserted in a TScrollBox, since it sometimes has a width bigger than the screen/window.
I have one or more TListViews on the frame, and many TEdits.
When running the application and opening any frame, one or more TListView are not filled in. By debug I see the data is pulled from the SQL. TEdits are filled in. Moving the SQL cursors gets all TEdits updated accordingly.
The closing this frame and opening again (or any other) then it start showing the data on the TListView.
I have not found any correlation that make sense. Apparently there is some kind of initialization missing. Not all TListView are empty, in some frames I have 4 or 5 of them and it show 2 with data, the others are empty.
EDIT: I have changed the title of this question, since I have noted that the problem seems not be related to the LiveBindings primarly, but seems to be related to some kind of initialization of the TListView on other lists.
I made a new test and see that TTreeView is also having the same problem, showing totally out of order the data, only at first time. If I previouslly opens something else it works fine, if I close and open the frame with the TTreeview the second time is ok.
In this image I show the TreeView messed:
It should not have all that spaces and some of the nodes are overlapped.
My application has only one form, and everything are frames that are created as parent of a TScrollBox.
With Delphi XE5 the issues of TScrollBox were many, I had to upgrade to XE6.
Many things start working with XE6, however there is still a little problem with TScrollBox:
There is a need to call ApplyStyleLookup, after the frame is inserted and parented to the scroll, to make the initialization. That makes the TListView and TTreeView works fine since the very first time.
constructor TFrameBase.Create(AOwner: TComponent);
begin
inherited;
Align := TAlignLayout.Left;
Parent := AOwner as TFmxObject;
if (AOwner is TScrollBox) then
(AOwner as TScrollBox).ApplyStyleLookup;
end;
This is my frame constructor. This fixed all the problems.
Related
I have a TStackPanel which I want to add a number of frames into at runtime. The number may vary each time the form is opened.
There seems to be limited information about TStackPanel around, and the examples I can find are in languages other than Delphi.
I have a loop that creates the frame, gives it a unique name and then adds it to the TStackPanel:
for i := 0 to 10 do
begin
mfSubFrame[i] := TMyFrame.Create(Application);
mfSubFrame.Name := name_array[i];
StackPanel1.InsertComponent(mfSubFrame[i]);
end;
This does not put anything in the stack panel. If I change the SP line to:
StackPanel1.InsertControl(mfSubFrame[i]);
then I do get a frame in the SP. It is the last one of the loop as I can tell by the name, the others may be hidden behind it but I can't tell. They are certainly not stacked horizontally like they should.
I have tried various other things like setting the parent of the frames to be the SP, and had a look at things like:
StackPanel1.Components.InsertComponent(mfSubFrame[i]);
and other sub-methods, but had no luck so far.
I suspect it may require a combination of statements, like add a control item as well as the actual component, but as I am working on the basis of trial and error it could be a long time before I stumble on the right combination.
I have never used the TStackPanel before, but it seems like you can add controls to it exactly the same way you add controls to any other windowed control: just create the control and assign its Parent.
For example,
for var i := 1 to 10 do
begin
var Memo := TMemo.Create(Self);
Memo.Parent := StackPanel1;
end;
will add ten memo controls (all owned by Self) to StackPanel1. There is no need to name the controls; referring to components by string name at runtime is an antipattern. (So is using FindComponent.)
InsertComponent() changes a component's Owner, which has no effect on visual display. You are creating each frame with Application as its Owner, and then changing its Owner to StackPanel1. You should assign the desired Owner when calling the component's constructor.
InsertControl() changes a control's Parent, which does affect visual display. You are creating each frame without a Parent, and then changing its Parent to be StackPanel1. You should be using the actual Parent property, not calling InsertControl() directly.
That being said, TStackPanel has a ControlCollection property that you are not doing anything with. That collection manages the actual stacking.
If needed 1, for each frame, try calling StackPanel1.ControlCollection.Add(), and then assigning the frame to the TStackPanelCollectionItem.Control property.
1: I don't have the source code for TStackPanel to look at, but I suspect TStackPanel probably handles this automatically for UI controls dropped onto it at design-time, but you might need to perform it manually for controls that you create dynamically at runtime. I'm not sure.
I have noticed something very strange. I am persisting the top, left, width, and height properties of a form when it is closing, and using this information to restore the form's last position when it is once again opened by calling SetBounds using the previously stored information. This works well, but only if the form's Position property is set to poDefault at design time. If set to something else, such as poDesigned, poScreenCenter, or poMainFormCenter, SetBounds does not restore the form's previous position and size.
Here's the strange part. What appears to matter is what the Position property is set to at design time. I can change the value of this property at runtime to poDefault and the call to SetBounds still does not work correctly. I have tried something like the following
if Self.Position <> poDefault then
Self.Position := poDefault;
in both the form's OnCreate event handler, as well as from an overridden constructor (and have set Position to poDefault in the constructor, and called SetBounds in the OnCreate event handler). In all cases, changing the form's Position property to poDefault at runtime does not fix the problem that I've observed with SetBounds. The only consistent pattern that I have found is that SetBounds works as it should only if the form's Position property was poDefault at design time.
There are other things that I've noticed with respect to how SetBounds works when a form's Position property is not set to poDefault at design time. For example, a form whose Position property is set to poScreenCenter at design time will not necessarily appear centered on the screen if you call SetBounds. However, it does not appear in the top-left location defined by SetBounds, nor does it respect the width and height specified in the call to SetBounds. Let me repeat, however, that I am setting the Position property of the form to poDefault before calling SetBounds. I've even stuck a call to Application.ProcessMessages between the two operations, but that doesn't fix the problem.
I have tested this extensively with Delphi 10.1 Berlin running on Windows 10. I have also tested it using Delphi XE6 on Windows 7. Same results.
If you have doubts, create a VCL application with four forms. On the first form place three buttons, and add something like the following OnClick to each button:
with TForm2.Create(nil) do
try
ShowModal;
finally
Release;
end;
where the constructor creates TForm2, then TForm3 and TForm4.
On the OnCreate of forms 2 through 4, add the following code:
if Self.Position <> poDefault then
Self.Position := poDefault;
Self.SetBounds(500,500,500,500);
On form2, set Position to poDefault, on form3 set Position to poScreenCenter, and on form4 leave Position set to the default, poDefaultPosOnly. Only form2 will appear at 500, 500, with a width of 500 and a height of 500.
Does anyone have a logical explanation for this result?
poDefault and friends mean "let Microsoft Windows position this form's window when the form would create and show it".
You just created Delphi object - but I wonder if it also has created/shown Windows object (HWND handle and all corresponding Windows internal structures). Especially with themed applications, not ones using standard pre-XP look and feel - they tend to ReCreateHWND when showing, because pre-loading those fancy Windows Themes is relatively expensive operation and only should be done when needed.
I think your default bounds (every property value set in the constructor might be considered a default non-tuned value, to be tuned later after object being constructed) are correctly ignored when you (or TApplication - that makes little difference for the topic) finally do FormXXX.Show.
It is during "make me a window and display it" sequence when your form looks at its properties and tells to MS Windows something like "now I want to create your internal HWND-object and position it at default coordinates/size at your discretion".
And that is absolutely correct behaviour - otherwise WHEN and HOW could TForm apply the Position property??? It just makes no sense to ask Windows for coordinates of a window that does not exists on the screen yet and maybe never would. Windows offers default coords/sizes for the this very second it being asked, looking how many other windows are there and where they are positioned ( and AMD/NVidia video drivers might also apply their correction to it).
It would make little sense, to acquire defaults now, and apply them two hours later when everything would probably be different - different amount of other windows and different positions of those, different set of monitors attached and with different resolutions, etc.
Just consider a "desktop replacement" type of notebook. It was set upon the table connected to large stationary external monitor. Then - let's imagine it - I run your application and it created the tform Delphi object and in the constructor it asked MS Windows for position - and Windows rightfully offered the position at that very secondary large monitor. But then an hour later I unplugged the notebook and walked away with it. Now an hour later I tell your application to show the form - and it will do what? display it with coordinates belonging to that now-detached external display? Outside of the viewport of the notebook's internal display that I only have at the moment? Should this form be displayed in the now "invisible" position just because when I started the application back then that spot was still visible there yet??? Way to confuse users for no gain, I think.
So the only correct behaviour would be to ask Windows for default coords this very second WHEN the form is going from hidden to visible and not a second earlier.
And that means that if you want to move your form - you should do it after it was being show. Place your Self.SetBounds(500,500,500,500); into OnShow event handler. So let the MS Windows materialize your form into default position like required by poDefault in Position property - and move your Window after that. Attempts to move the window that does not exist yet look correctly futile to me.
Either PRESET your form ( in constructing sequence) to explicitly ignore MS Windows defaults and use pre-set cords (via poDesigned value), or let the form ask Windows coordinates, but MOVE it with SetBounds after it got visible via OnShow handler.
I have three buttons placed at the right on my form. The buttons' anchor property parameters akTop, akRight are set to true, the other ones left to false so that the buttons always remain at the right side near the boarder when the form is resized. Then I set the form's WindowState property to wsMaximized so that it covers the whole screen when run at start-up. But when I start the application the buttons are closer to the middle rather than on the right. But when I resize the form at design-time everything seems to work just fine.
Here are some snapshots to show you exactly what I mean:
At design-time:
At run-time:
Please, explain what I'm doing wrong and how to fix this so that it works as intended.
This looks like the buttons are being created with their designed positions, the form is then set to Maximized, then the anchor properties are set or put in place.
In design time the anchors are already set and so that’s why you see them move as you want. To prove my theory on this make the form much smaller, run the application and notice that the items are in their smaller design time locations.
An easy fix to get what you want. Keep the Window state at wsNormal and on FormShow (which occurs after Create) do this:
procedure TForm1.FormShow(Sender: TObject);
begin
self.WindowState := wsMaximized;
end;
You will see the results you want.
I have seen your answer in one of those tutorials. But realy dont remember was which one. You can watch all videos, even you will learn more things. it will not time waste all the way.
Link: Learn Delphi TV
Also you can try something like below if you are lazy enough to watch videos. Put this code in form resize:
buttoncreate.left := panel.width - (buttoncreate.width + buttonedit.width + buttondelete.width);
buttonedit.left := panel.width - (buttonedit.width + buttondelete.width);
buttondelete.left := panel.width - buttondelete.width;
I was wondering why Delphi (2007) provides three widgets that seem to do the same thing, and what the advantages/disadvantages are for each.
On the same topic, if I want to display different sets of controls, why should I favor eg. PageControl + TabSheets + Frames, instead of just displaying different frames directly on the parent form?
Thank you.
From the helpfile on TTabSet:
Tab set controls are commonly used to
display tabbed pages within a dialog
box. TTabSet is provided for backward
compatibility. Use TTabControl
component in 32-bit Windows
applications.
So the real question is, what's the difference between TTabControl and TPageControl? The difference is that TTabControl only has one "page", whereas TPageControl has one page for each tab. This makes them useful in different situations.
TPageControl is useful for dialogs where you want to fit more UI on the screen than you have screen space to fit it in. Organize your UI into categories and put each category on one page. You see this pattern a lot in Options dialogs, for example.
TTabControl, on the other hand, works well for working on an array/list of objects. Create a UI to display and edit the properties of a single object, and place it on a TTabControl, then create one tab for each object and set up the event handlers so it'll load a new object from the array into the controls whenever you change tabs.
As for the frames question, the main reason to use a TPageControl in conjunction with frames is because it provides a prebuilt way to decide which frame to display. That way you don't have to reinvent a mechanism for it.
One method that I have used with great success is to use frames with a TPageControl and late bind my frames to the tPageControl the first time the page is selected. This keeps the form load time down, by not creating frames which are never viewed, but yet allows the flexibility of being created, the state is maintained when changing between tabs. Recently I switched to using forms and embedding them instead of frames...but the concept is the same.
The same can be done using a single "mount point" on a TTabControl and switching it out as the tab is changed, but then the issue of how to deal with the tab state as tabs are switched back too comes up.
[EDIT] The question comes up how do I handle communication between the frame and the parent form. This actually is very easy to do using interfaces. Just create a new unit that will be shared by the form AND the frame and add two interfaces:
type
IFormInterface = interface
[guid]
procedure FormProc;
end;
IFrameInterface = interface
[guid]
procedure SetFormController(Intf:IFormInterface);
end;
Have the form implement the IFormInterface, and the frame implement the IFrameInterface. When you click on a tab and show a frame, then run code like the following:
var
FrameIntf : IFrameInterface;
begin
if Supports(FrameObj,IFrameINterface,FrameIntf) then
FrameIntf.SetFormController(Self);
end;
your frame implementation of the SetFormController method would then hold onto the reference passed, which would allow it to call upwards into the form.
procedure TFrame1.SetFormController(Intf:IFormInterface);
begin
fFormController := Intf;
end;
Procedure TFrame1.Destroy; override;
begin
fFormController := nil; // release the reference
inherited;
end;
Procedure TFrame1.Button1Click(Sender:tObject);
begin
if fFormController <> nil then
fFormController.FormProc
else
Raise Exception.Create('Form controller not set');
end;
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.