Remove TChart from form programmatically - delphi

I'm looking for the correct way in order to remove a TChart and deallocate all the memory.
I am using Delphi2007 with the standard TeeChart 7
I create the TChart programmatically:
var parentform: TForm;
begin
newchart:= TChart.Create(parentform);
newchart.Parent:= parentform;
...
Then, I want to remove only the chart from the form (not closing the form itself), but I get only that the chart becomes blank and stays on the form:
newChart.FreeAllSeries;
FreeAndNil(newChart);
if I use
NewChart.Parent := nil,
I don't see the chart anymore, but I think the TChart object still exists (until the parentform is destroyed). Is there a specific method for doing this?
Thank you

The most straightforward way to get rid of a TChart control, or just about any control, for that matter, is to call Free on it:
newChart.Free;
You can call FreeAndNil instead if you wish. That has the same effect of calling Free, but also sets the variable's value to nil. That's useful if you later test the variable's value to detect whether you still have access to the control. If you never reference the variable again, then FreeAndNil doesn't get you much.
The control will automatically free the other things it owns, such as the series you manually freed with FreeAllSeries. You don't need to free them yourself.
Merely clearing the control's Parent property does not free the control. You can prove that by re-assigning the Parent property and watching as the control re-appears on the screen. That wouldn't happen if the control had ceased to exist.
If the control remains visible on the screen after you free it, then you have other problems. Maybe the parent control hasn't repainted itself properly. You might try calling Refresh on the parent control. You might also have multiple controls visible. After all, the question's code creates two chart controls, so maybe one of them is still visible, and you've mistaken it for the control you destroyed.

Related

How do you add an item to a TStackPanel at runtime

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.

Strange wm_keydown behaviour with THtmlEdit

This is a strange one: This is the situation
The application builds an edit form at run-time from an XML document. The UI/UX design is such that there are three levels of nested TTabControls and for the final (lowest) level the TTabItem is created at run-time. This has a TVertScrollBox which itself contains a column-grid control of mine and that (at last!) contains the THtmlEditor.
The problem is that when the controls are built and the content loaded, the THtmlEdit initially ignores keydown. It will respond to mouse events, the caret can be positioned. Switching to a different top-level tab, or away from the whole application and back, then cures the issue and the editor responds to keydown messages.
I have tried putting a breakpoint in procedure THtmlEditor.KeyDown() ... and then tracing back up the call-chain. There doesn't seem to be anything behaving differently. I've paid particular attention to input focus, explicitly calling Editor.SetFocus even though that is apparently already called ...
I have tried putting a conditional breakpoint in function TPlatformWin.HandleMessage: Boolean; ... and my only observation is that when in non-working mode, the call to DispatchMessage(Msg) doesn't arrive at the editor, or its parent form.
I have tried to build a MDC for this, replicating the structure outlined above but ... that always works!
What can I try next?
Has anyone seen this behaviour (and fixed it)?
EDIT:
The detail I didn't mention -- didn't think about it -- is that for the error condition to show, the control focused before the THmtlEditor is a TWebBrowser. If I set focus on a TEdit after TWebBrowser and then to THtmlEditor it seems to work.
Grrr!
This is only half an answer, but for future reference, this is what I've done:
It's a bodge :-(
I've put a TEdit on the main form and set it invisible. I've then added a handler to the MainForm OnFocusChanged which sets a boolean trap-flag to track if TWebBrowser has previously been focused.
For the instances of THtmlEditor I've added an OnClick handler which checks the trap-flag and makes the TEdit visible, calls SetFocus on it, re-hide the TEdit and return True so that the THtmlEditor can re-set focus to itself. The trap-flag is to avoid unnecessarily losing focus if the user is just clicking in the THtmlEditor.
The next step -- which I may never get to -- would be to trace through the focus code for TEdit. My suspicion is that it all relates ITextInput which TEdit supports and THtmlEditor doesn't.

Why does TForm.SetBounds only work correctly when TForm.Position is set to poDefault at design time

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.

Delphi - How do I break when a form's ComponentCount decrements

The code below is reproduced from Toolbar2000. It is part of routine that reads toolbar positions and dock states from an INI file. I call this routine during initialisation. This code below is iterating through all the components on the main form (OwnerComponent) and loading the settings of any toolbars it finds.
for I := 0 to OwnerComponent.ComponentCount-1 do begin
ToolWindow := OwnerComponent.Components[I]; // <------------------------
....
This iterating takes some time (seconds - there are 1500-odd components on the form) and I'm getting a range error at the point shown. I have ascertained that one or more items is being shed from the main form's components while this loop is executing, so eventually the loop tries to access one past the end of the array once this has happened (presumably it would be better to code this as a "downto" for-loop to prevent this).
Anyway, I need to find out where the main form is losing a component. Can anybody give me any Delphi 2006 debugging tips on how to do this? I wouldn't expect any main form components to be freed at this point in my program.
UPDATE
I found that when I had repositioned a toolbar's default dock position at design-time I had inadvertently docked it onto another toolbar, rather than the dock site that the other toolbar was in. I fixed the problem by removing the toolbar from the toolbar it was docked in and adding it to the dock instead. So the arrangement that caused the problem was:
Dock
Toolbar 1
Control 1
Control 2
Toolbar 2
Control 3
Control 4
and the fix was to arrange them thus:
Dock
Toolbar 1
Control 1
Control 2
Toolbar 2
Control 3
Control 4
It still points to a bug in the TB2k code though - one would assume it should be able to handle nested toolbars.
In addition to Lieven's answer, you could also use debug dcu's and set a breakpoint in TComponent.Destroy just before you enter the loop.
In both cases you will need to examine the call stack to see where the call/change to count is coming from.
A very interesting article on breakpoints was written by Cary Jensen: http://caryjensen.blogspot.com/2010/08/breakpoints-with-side-effects.html
You'll have to add a data breakpoint at #Self.FComponents.FCount to break whenever the count changes.
ComponentCount is a property that returns the value from GetComponentCount
GetComponentCount returns FComponents.Count.
FComponents is a TList instance that has a private FCount variable.

Design-time drag and drop in Delphi?

Before Delphi 2006 (I think) introduced the TFlowPanel and TGridPanel, I did a control that was similar in concept. It still does a couple of things those controls do not do, and when upgrading my code to Delphi 2009, I decided to add a couple of enhancements to that as well.
Right now, the order of the child controls is determined by their creation order. The FlowPanel and GridPanel show a better way with ControlIndex and other filtered properties, but I was wondering if there is a way to handle drag and drop reordering in design-time? As far as I can tell, dragging an edit control and dropping it onto my panel doesn't call anything that I can access at design-time.
I was half-fantasising about a way to either detect the drop operation directly, or to perhaps detect when a control is moved so I can determine where it should go.
Any ideas?
Update:
OK, got it working. The container control was already overriding AlignControls to manage the placement of the controls. When you drag the nested control and drop it, AlignControls is again called. I then compared the new coordinates of the control with the other controls in the list and moved it to the appropriate position.
There were a couple of problems that I had to work through (mostly related to the many calls to AlignControls) but the basic concept is simple enough. Thanks to all the commenters for all the help.
You can't drag a control that's already on the form and drop it onto your panel. Dragging is only for moving a control, not for changing its parent. To change the parent, cut and paste.
If the control is already on your panel, and you want to move it to another position on your panel, then the panel can control the layout by overriding the TWinControl.AlignControls method. When a control is moved, its SetBounds method is called, and among the things tha happens is that it calls AlignControl(Self) on its parent window. That calls AlignControls. Look in Controls.pas, and you'll see that that's a complicated method, but it's what is responsible for the layout of the children on a control, and that's exactly what you're planning to change.
Perhaps some of these suggestions might help.
You can re-parent a control in the designer without having to do cut-and-paste. View the structure pane, and simply drag the visual control to the node of another parent in the structure pane. If you have things in a flowpanel, drag everything out of the flow panel and drag them back in the order that you want them to be.
(You can re-parent ANY visual control this way, without changing anything other than its parent. I highly recommend doing it this way.)
You can view the form as text, and move the declaration order around in there -- but obviously you'll need to be careful when editing the "resource" file directly.
You can set tab order in the designer, so you could make a different control based on tab order that works as you want. You can right click on the form and change the creation order of the non-visual controls, but that doesn't work with visual controls.
Have you tried to write an "OnDragDrop" event for your grid component, where you check if your component is in design mode?
I haven't written such a component yet, but I don't see why the event shouldn't trigger.

Resources